删掉租期相关错误东西
This commit is contained in:
@@ -52,16 +52,10 @@ export interface PublishCoinCorrection {
|
||||
correction: number
|
||||
}
|
||||
|
||||
export interface PublishRentRule {
|
||||
threshold_m: number
|
||||
days: number
|
||||
}
|
||||
|
||||
export interface PublishRatioConfig {
|
||||
insurance_base_ratios: PublishInsuranceBaseRatio[]
|
||||
config_items: PublishRatioConfigItem[]
|
||||
coin_corrections: PublishCoinCorrection[]
|
||||
rent_rules: PublishRentRule[]
|
||||
}
|
||||
|
||||
export interface ListingPublishOptions {
|
||||
@@ -109,7 +103,6 @@ export const emptyListingPublishOptions: ListingPublishOptions = {
|
||||
insurance_base_ratios: [],
|
||||
config_items: [],
|
||||
coin_corrections: [],
|
||||
rent_rules: [],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -208,7 +201,6 @@ function normalizeRatioConfig(value?: unknown): PublishRatioConfig {
|
||||
coin_corrections: normalizeCoinCorrections(
|
||||
Array.isArray(row.coin_corrections) ? row.coin_corrections : [],
|
||||
),
|
||||
rent_rules: normalizeRentRules(Array.isArray(row.rent_rules) ? row.rent_rules : []),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,18 +243,6 @@ function normalizeCoinCorrections(values: unknown[]): PublishCoinCorrection[] {
|
||||
.filter((item) => item.threshold_m >= 0 && item.correction > 0)
|
||||
}
|
||||
|
||||
function normalizeRentRules(values: unknown[]): PublishRentRule[] {
|
||||
return values
|
||||
.map((item) => {
|
||||
const row = isRecord(item) ? item : {}
|
||||
return {
|
||||
threshold_m: readNumber(row.threshold_m),
|
||||
days: Math.trunc(readNumber(row.days)),
|
||||
}
|
||||
})
|
||||
.filter((item) => item.threshold_m >= 0 && item.days > 0)
|
||||
}
|
||||
|
||||
function readNumber(value: unknown) {
|
||||
const parsed = Number(value)
|
||||
return Number.isFinite(parsed) ? parsed : 0
|
||||
|
||||
@@ -20,8 +20,6 @@ export interface Listing {
|
||||
price_daily: number
|
||||
price_weekly: number
|
||||
deposit_amount: number
|
||||
min_rent_hours: number
|
||||
max_rent_hours: number
|
||||
status: string
|
||||
review_status: string
|
||||
review_reason: string
|
||||
@@ -43,8 +41,6 @@ export interface ListingPayload {
|
||||
price_daily: number
|
||||
price_weekly: number
|
||||
deposit_amount: number
|
||||
min_rent_hours: number
|
||||
max_rent_hours: number
|
||||
}
|
||||
|
||||
interface ApiResponse<T> {
|
||||
|
||||
@@ -14,7 +14,6 @@ export interface Order {
|
||||
login_platform: string
|
||||
rent_start_at?: string
|
||||
rent_end_at?: string
|
||||
rent_hours: number
|
||||
rent_amount: number
|
||||
deposit_amount: number
|
||||
platform_fee: number
|
||||
@@ -44,10 +43,9 @@ interface ApiResponse<T> {
|
||||
data: T
|
||||
}
|
||||
|
||||
export async function createOrder(listingId: number, rentHours: number) {
|
||||
export async function createOrder(listingId: number) {
|
||||
const { data } = await apiClient.post<ApiResponse<Order>>('/orders', {
|
||||
listing_id: listingId,
|
||||
rent_hours: rentHours,
|
||||
})
|
||||
return data.data
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="page-header">
|
||||
<p class="eyebrow">Create Order</p>
|
||||
<h1>创建订单</h1>
|
||||
<p>确认租期、租金、押金和账号资产快照。</p>
|
||||
<p>确认价格、押金和账号资产快照。</p>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -91,7 +91,7 @@ async function handleConfirmReceive() {
|
||||
confirming.value = true
|
||||
try {
|
||||
await confirmReceive(order.value.id)
|
||||
ElMessage.success('已确认收号,订单进入租赁中')
|
||||
ElMessage.success('已确认收号,订单进入使用中')
|
||||
await loadOrder()
|
||||
} catch (error) {
|
||||
ElMessage.error(readError(error, '确认收号失败'))
|
||||
@@ -197,7 +197,7 @@ function readError(error: unknown, fallback: string) {
|
||||
<strong>{{ order.handoff_status }}</strong>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<span>租金</span>
|
||||
<span>订单金额</span>
|
||||
<strong>¥{{ order.rent_amount }}</strong>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
@@ -207,9 +207,8 @@ function readError(error: unknown, fallback: string) {
|
||||
</div>
|
||||
|
||||
<div v-if="order" class="order-panel">
|
||||
<p>租期:{{ order.rent_hours }} 小时</p>
|
||||
<p>开始:{{ formatDateTime(order.rent_start_at, '未开始') }}</p>
|
||||
<p>结束:{{ formatDateTime(order.rent_end_at, '未设置') }}</p>
|
||||
<p>预计截止:{{ formatDateTime(order.rent_end_at, '未设置') }}</p>
|
||||
<el-button v-if="order.status === 'pending_handoff'" type="danger" :loading="cancelling" @click="handleCancel">
|
||||
取消订单并释放账号
|
||||
</el-button>
|
||||
@@ -236,7 +235,7 @@ function readError(error: unknown, fallback: string) {
|
||||
class="order-panel"
|
||||
>
|
||||
<h2>确认收号</h2>
|
||||
<p>确认账号可以正常登录后,订单会进入租赁中并重新计算租期结束时间。</p>
|
||||
<p>确认账号可以正常登录后,订单会进入使用中并重新计算预计截止时间。</p>
|
||||
<el-button type="primary" :loading="confirming" @click="handleConfirmReceive">确认已收到账号</el-button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -23,14 +23,13 @@ async function loadOrders() {
|
||||
<div class="page-header">
|
||||
<p class="eyebrow">Orders</p>
|
||||
<h1>我的订单</h1>
|
||||
<p>跟踪待交接、租赁中、待归还、申诉中和已完成订单。</p>
|
||||
<p>跟踪待交接、使用中、待归还、申诉中和已完成订单。</p>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" class="table-panel" :data="orders">
|
||||
<el-table-column prop="order_no" label="订单号" min-width="210" />
|
||||
<el-table-column prop="title" label="账号" min-width="180" />
|
||||
<el-table-column prop="rent_hours" label="租期" width="90" />
|
||||
<el-table-column prop="rent_amount" label="租金" width="100" />
|
||||
<el-table-column prop="rent_amount" label="订单金额" width="100" />
|
||||
<el-table-column prop="deposit_amount" label="押金" width="100" />
|
||||
<el-table-column prop="status" label="状态" width="140" />
|
||||
<el-table-column label="操作" width="100">
|
||||
|
||||
@@ -56,7 +56,7 @@ function money(value?: number) {
|
||||
<strong>{{ dashboard.metrics.total_orders }}</strong>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<span>租赁中</span>
|
||||
<span>使用中</span>
|
||||
<strong>{{ dashboard.metrics.renting_orders }}</strong>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
@@ -119,7 +119,7 @@ function money(value?: number) {
|
||||
<el-table-column prop="order_no" label="最近订单" min-width="210" />
|
||||
<el-table-column prop="title" label="账号" min-width="170" />
|
||||
<el-table-column prop="status" label="状态" width="140" />
|
||||
<el-table-column prop="rent_amount" label="租金" width="100" />
|
||||
<el-table-column prop="rent_amount" label="订单金额" width="100" />
|
||||
<el-table-column prop="deposit_amount" label="押金" width="100" />
|
||||
<el-table-column label="创建时间" min-width="180">
|
||||
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
|
||||
|
||||
@@ -56,6 +56,10 @@ function money(value: number) {
|
||||
return `¥${Number(value || 0).toFixed(2)}`
|
||||
}
|
||||
|
||||
function listingPrice(row: Listing) {
|
||||
return money(row.price_daily || row.price_hourly)
|
||||
}
|
||||
|
||||
function extractObjectKey(url: string) {
|
||||
try {
|
||||
const parsed = new URL(url, window.location.origin)
|
||||
@@ -110,8 +114,8 @@ function readError(error: unknown, fallback: string) {
|
||||
<strong>{{ listing.review_status }}</strong>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<span>时租</span>
|
||||
<strong>{{ money(listing.price_hourly) }}</strong>
|
||||
<span>价格</span>
|
||||
<strong>{{ listingPrice(listing) }}</strong>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<span>押金</span>
|
||||
@@ -132,13 +136,11 @@ function readError(error: unknown, fallback: string) {
|
||||
</div>
|
||||
|
||||
<div class="order-panel dashboard-panel">
|
||||
<h2>号主与租期</h2>
|
||||
<h2>号主与价格</h2>
|
||||
<p>号主:{{ listing.owner_phone || listing.owner_nickname || listing.owner_id }}</p>
|
||||
<p>号主 ID:{{ listing.owner_id }}</p>
|
||||
<p>最短租期:{{ listing.min_rent_hours }} 小时</p>
|
||||
<p>最长租期:{{ listing.max_rent_hours }} 小时</p>
|
||||
<p>日租:{{ money(listing.price_daily) }}</p>
|
||||
<p>周租:{{ money(listing.price_weekly) }}</p>
|
||||
<p>价格:{{ listingPrice(listing) }}</p>
|
||||
<p>押金:{{ money(listing.deposit_amount) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -62,6 +62,10 @@ function openEvidence(row: Listing) {
|
||||
evidenceListing.value = row
|
||||
}
|
||||
|
||||
function listingPrice(row: Listing) {
|
||||
return `¥${Number(row.price_daily || row.price_hourly || 0).toFixed(2)}`
|
||||
}
|
||||
|
||||
function extractObjectKey(url: string) {
|
||||
try {
|
||||
const parsed = new URL(url, window.location.origin)
|
||||
@@ -95,7 +99,7 @@ function readError(error: unknown, fallback: string) {
|
||||
<div class="page-header">
|
||||
<p class="eyebrow">Review</p>
|
||||
<h1>商品审核</h1>
|
||||
<p>审核号主提交的账号资产、价格、押金和租期规则。</p>
|
||||
<p>审核号主提交的账号资产、价格和押金。</p>
|
||||
</div>
|
||||
<el-button @click="loadListings">刷新</el-button>
|
||||
</div>
|
||||
@@ -107,7 +111,9 @@ function readError(error: unknown, fallback: string) {
|
||||
<el-table-column prop="login_platform" label="平台" width="120" />
|
||||
<el-table-column prop="haf_coin_amount" label="哈夫币" width="110" />
|
||||
<el-table-column prop="rank_level" label="段位" width="110" />
|
||||
<el-table-column prop="price_hourly" label="时租" width="90" />
|
||||
<el-table-column label="价格" width="90">
|
||||
<template #default="{ row }">{{ listingPrice(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="deposit_amount" label="押金" width="90" />
|
||||
<el-table-column label="截图" width="90">
|
||||
<template #default="{ row }">
|
||||
|
||||
@@ -41,6 +41,10 @@ function money(value: number) {
|
||||
return `¥${Number(value || 0).toFixed(2)}`
|
||||
}
|
||||
|
||||
function listingPrice(row: Listing) {
|
||||
return money(row.price_daily || row.price_hourly)
|
||||
}
|
||||
|
||||
function ownerName(row: Listing) {
|
||||
return row.owner_phone || row.owner_nickname || `号主 ${row.owner_id}`
|
||||
}
|
||||
@@ -84,7 +88,7 @@ function reviewType(status: string) {
|
||||
<strong>{{ publishedCount }} 个</strong>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<span>租赁中</span>
|
||||
<span>已锁定</span>
|
||||
<strong>{{ rentedCount }} 个</strong>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
@@ -101,7 +105,7 @@ function reviewType(status: string) {
|
||||
<el-select v-model="filters.status" clearable placeholder="全部状态" class="full-control">
|
||||
<el-option label="草稿" value="draft" />
|
||||
<el-option label="已上架" value="published" />
|
||||
<el-option label="租赁中" value="rented" />
|
||||
<el-option label="已锁定" value="rented" />
|
||||
<el-option label="已下架" value="offline" />
|
||||
<el-option label="异常" value="abnormal" />
|
||||
</el-select>
|
||||
@@ -137,8 +141,8 @@ function reviewType(status: string) {
|
||||
<el-table-column prop="login_platform" label="平台" width="120" />
|
||||
<el-table-column prop="rank_level" label="段位" width="110" />
|
||||
<el-table-column prop="haf_coin_amount" label="哈夫币" width="110" />
|
||||
<el-table-column label="时租" width="100">
|
||||
<template #default="{ row }">{{ money(row.price_hourly) }}</template>
|
||||
<el-table-column label="价格" width="100">
|
||||
<template #default="{ row }">{{ listingPrice(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="押金" width="100">
|
||||
<template #default="{ row }">{{ money(row.deposit_amount) }}</template>
|
||||
|
||||
@@ -91,7 +91,7 @@ function readError(error: unknown, fallback: string) {
|
||||
<strong>{{ order.handoff_status }}</strong>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<span>租金</span>
|
||||
<span>订单金额</span>
|
||||
<strong>¥{{ order.rent_amount }}</strong>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
@@ -105,9 +105,8 @@ function readError(error: unknown, fallback: string) {
|
||||
<h2>用户信息</h2>
|
||||
<p>租客:{{ order.renter_phone || order.renter_id }}</p>
|
||||
<p>号主:{{ order.owner_phone || order.owner_id }}</p>
|
||||
<p>租期:{{ order.rent_hours }} 小时</p>
|
||||
<p>开始:{{ formatDateTime(order.rent_start_at, '未开始') }}</p>
|
||||
<p>结束:{{ formatDateTime(order.rent_end_at, '未设置') }}</p>
|
||||
<p>预计截止:{{ formatDateTime(order.rent_end_at, '未设置') }}</p>
|
||||
</div>
|
||||
|
||||
<div class="order-panel dashboard-panel">
|
||||
|
||||
@@ -36,7 +36,7 @@ async function loadOrders() {
|
||||
<div class="toolbar-actions">
|
||||
<el-select v-model="status" clearable placeholder="订单状态" style="width: 180px">
|
||||
<el-option label="待交接" value="pending_handoff" />
|
||||
<el-option label="租赁中" value="renting" />
|
||||
<el-option label="使用中" value="renting" />
|
||||
<el-option label="逾期中" value="overdue" />
|
||||
<el-option label="待归还确认" value="pending_return_confirm" />
|
||||
<el-option label="申诉中" value="disputing" />
|
||||
@@ -57,7 +57,7 @@ async function loadOrders() {
|
||||
<el-table-column prop="status" label="状态" width="140" />
|
||||
<el-table-column prop="handoff_status" label="交接" width="180" />
|
||||
<el-table-column prop="settlement_status" label="结算" width="120" />
|
||||
<el-table-column prop="rent_amount" label="租金" width="100" />
|
||||
<el-table-column prop="rent_amount" label="订单金额" width="100" />
|
||||
<el-table-column prop="deposit_amount" label="押金" width="100" />
|
||||
<el-table-column label="创建时间" min-width="180">
|
||||
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
|
||||
|
||||
@@ -281,17 +281,6 @@ function removeCoinCorrection(index: number) {
|
||||
publishOptionsDraft.value.ratio_config.coin_corrections.splice(index, 1)
|
||||
}
|
||||
|
||||
function addRentRule() {
|
||||
publishOptionsDraft.value.ratio_config.rent_rules.push({
|
||||
threshold_m: 0,
|
||||
days: 1,
|
||||
})
|
||||
}
|
||||
|
||||
function removeRentRule(index: number) {
|
||||
publishOptionsDraft.value.ratio_config.rent_rules.splice(index, 1)
|
||||
}
|
||||
|
||||
function addHomeBanner() {
|
||||
homeBannersDraft.value.push({
|
||||
eyebrow: '首页推荐',
|
||||
@@ -642,23 +631,6 @@ function readError(error: unknown, fallback: string) {
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="editor-block-title subtle-title">
|
||||
<span>租期规则</span>
|
||||
<el-button size="small" @click="addRentRule">添加租期</el-button>
|
||||
</div>
|
||||
<el-table :data="publishOptionsDraft.ratio_config.rent_rules" size="small" border>
|
||||
<el-table-column label="大于等于 M" min-width="130">
|
||||
<template #default="{ row }"><el-input-number v-model="row.threshold_m" :min="0" :step="10" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="租期天数" min-width="130">
|
||||
<template #default="{ row }"><el-input-number v-model="row.days" :min="1" :step="1" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="90">
|
||||
<template #default="{ $index }">
|
||||
<el-button size="small" type="danger" plain @click="removeRentRule($index)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="editor-block">
|
||||
|
||||
@@ -62,7 +62,7 @@ function directionLabel(direction: string) {
|
||||
<div class="page-header">
|
||||
<p class="eyebrow">Wallet Ledger</p>
|
||||
<h1>资金流水</h1>
|
||||
<p>查看租金、押金、冻结、解冻、退款和结算流水。当前为开发态账务记录,不代表真实支付余额。</p>
|
||||
<p>查看订单金额、押金、冻结、解冻、退款和结算流水。当前为开发态账务记录,不代表真实支付余额。</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button @click="resetFilters">重置</el-button>
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
getListingSubtitle,
|
||||
getListingTitle,
|
||||
getLoginMethod,
|
||||
getRentDays,
|
||||
getServerRegion,
|
||||
readAssetNumber,
|
||||
readAssetString,
|
||||
@@ -29,14 +28,12 @@ const router = useRouter();
|
||||
const session = useSessionStore();
|
||||
const loading = ref(false);
|
||||
const ordering = ref(false);
|
||||
const rentHours = ref(1);
|
||||
const listing = ref<Listing | null>(null);
|
||||
|
||||
onMounted(async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
listing.value = await fetchListing(String(route.params.id));
|
||||
rentHours.value = Math.max(listing.value.min_rent_hours || 1, 1);
|
||||
} catch {
|
||||
showToast({ message: "加载失败", icon: "warning-o" });
|
||||
} finally {
|
||||
@@ -44,10 +41,9 @@ onMounted(async () => {
|
||||
}
|
||||
});
|
||||
|
||||
/* 预计租金 */
|
||||
const totalRent = computed(() => {
|
||||
const orderTotal = computed(() => {
|
||||
if (!listing.value) return "0.00";
|
||||
return (listing.value.price_hourly * rentHours.value).toFixed(2);
|
||||
return getListingDisplayPrice(listing.value).toFixed(2);
|
||||
});
|
||||
|
||||
const detailMetrics = computed(() => {
|
||||
@@ -136,7 +132,7 @@ async function handleCreateOrder() {
|
||||
|
||||
ordering.value = true;
|
||||
try {
|
||||
const order = await createOrder(listing.value.id, rentHours.value);
|
||||
await createOrder(listing.value.id);
|
||||
showToast({ message: "订单已创建,账号已锁定", icon: "passed" });
|
||||
await router.push(`/m/orders`);
|
||||
} catch (error) {
|
||||
@@ -255,10 +251,6 @@ function isNavActive(path: string) {
|
||||
<span class="info-label">M单价</span>
|
||||
<span class="info-text">{{ formatRatio(listing) }}</span>
|
||||
</div>
|
||||
<div class="info-line">
|
||||
<span class="info-label">租期</span>
|
||||
<span class="info-text">{{ getRentDays(listing) }}天</span>
|
||||
</div>
|
||||
<div class="info-line">
|
||||
<span class="info-label">常用登录地</span>
|
||||
<span class="info-text">{{ assetRegions(listing).join("、") || "--" }}</span>
|
||||
@@ -316,26 +308,9 @@ function isNavActive(path: string) {
|
||||
<!-- 底部下单栏(固定) -->
|
||||
<div class="order-bar">
|
||||
<div class="order-bar-left">
|
||||
<div class="rent-control">
|
||||
<button
|
||||
class="rent-btn"
|
||||
:disabled="rentHours <= listing.min_rent_hours"
|
||||
@click="rentHours--"
|
||||
>
|
||||
−
|
||||
</button>
|
||||
<span class="rent-value">{{ rentHours }}小时</span>
|
||||
<button
|
||||
class="rent-btn"
|
||||
:disabled="rentHours >= listing.max_rent_hours"
|
||||
@click="rentHours++"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
<div class="order-price">
|
||||
<span class="price-label">租金</span>
|
||||
<span class="price-amount">¥{{ totalRent }}</span>
|
||||
<span class="price-label">价格</span>
|
||||
<span class="price-amount">¥{{ orderTotal }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<van-button
|
||||
@@ -528,7 +503,7 @@ function isNavActive(path: string) {
|
||||
color: #ff5f00;
|
||||
}
|
||||
|
||||
/* ========== 租期信息 ========== */
|
||||
/* ========== 账号资料 ========== */
|
||||
.info-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -676,38 +651,6 @@ function isNavActive(path: string) {
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.rent-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.rent-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
background: #f5f7fa;
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.rent-btn:disabled {
|
||||
opacity: 0.3;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.rent-value {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
min-width: 48px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.order-price {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
|
||||
@@ -35,7 +35,7 @@ async function loadOrders() {
|
||||
const statusTabs = [
|
||||
{ key: "all", label: "全部" },
|
||||
{ key: "pending_handoff", label: "待交接" },
|
||||
{ key: "renting", label: "租赁中" },
|
||||
{ key: "renting", label: "使用中" },
|
||||
{ key: "pending_return_confirm", label: "待归还" },
|
||||
{ key: "completed", label: "已完成" },
|
||||
];
|
||||
@@ -64,7 +64,7 @@ function statusColor(status: string) {
|
||||
function statusLabel(status: string) {
|
||||
const map: Record<string, string> = {
|
||||
pending_handoff: "待交接",
|
||||
renting: "租赁中",
|
||||
renting: "使用中",
|
||||
overdue: "已逾期",
|
||||
pending_return_confirm: "待归还",
|
||||
completed: "已完成",
|
||||
@@ -138,11 +138,7 @@ function goDetail(id: number) {
|
||||
<h3 class="order-title">{{ order.title }}</h3>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<span class="info-label">租期</span>
|
||||
<span class="info-value">{{ order.rent_hours }}小时</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">租金</span>
|
||||
<span class="info-label">订单金额</span>
|
||||
<span class="info-value price">¥{{ order.rent_amount }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
|
||||
@@ -133,9 +133,6 @@ const calculatedFinalPrice = computed(() =>
|
||||
const calculatedRatioText = computed(() =>
|
||||
calculatedRatio.value > 0 ? `1:${formatNumber(calculatedRatio.value)}` : ""
|
||||
);
|
||||
const calculatedRentDays = computed(() =>
|
||||
coinMAmount.value > 0 ? calculateRentDays(coinMAmount.value) : 0
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
restoreDraft();
|
||||
@@ -356,8 +353,7 @@ async function handleSubmit() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const title = `${form.server_region} ${form.rank_level} ${form.haf_coin_amount}M哈夫币`;
|
||||
const rentDays = Math.max(calculatedRentDays.value, 1);
|
||||
const dailyPrice = roundMoney(calculatedFinalPrice.value / rentDays);
|
||||
const dailyPrice = calculatedFinalPrice.value;
|
||||
const hourlyPrice = Math.max(roundMoney(dailyPrice / 24), 0.01);
|
||||
|
||||
await createListing({
|
||||
@@ -373,8 +369,6 @@ async function handleSubmit() {
|
||||
price_daily: dailyPrice,
|
||||
price_weekly: roundMoney(dailyPrice * 7),
|
||||
deposit_amount: Number(form.deposit_amount),
|
||||
min_rent_hours: 24,
|
||||
max_rent_hours: rentDays * 24,
|
||||
});
|
||||
localStorage.removeItem(draftKey);
|
||||
showToast({ message: "发布成功", icon: "passed" });
|
||||
@@ -402,7 +396,7 @@ function validateForm() {
|
||||
if (form.deposit_amount === "" || Number(form.deposit_amount) < 0) {
|
||||
return "请填写押金";
|
||||
}
|
||||
if (!calculatedFinalPrice.value || !calculatedRentDays.value) {
|
||||
if (!calculatedFinalPrice.value) {
|
||||
return "请完善币数、保险、体力和负重后再发布";
|
||||
}
|
||||
for (const item of screenshotSlots.value) {
|
||||
@@ -426,6 +420,7 @@ function buildAssetSummary() {
|
||||
face_owner: form.face_owner,
|
||||
secret_kd: form.secret_kd,
|
||||
fire_level: Number(form.fire_level),
|
||||
publish_ratio: calculatedRatio.value,
|
||||
season_insurance: form.season_insurance,
|
||||
stamina_level: form.stamina_level,
|
||||
load_level: form.load_level,
|
||||
@@ -535,12 +530,6 @@ function getCoinCorrection(coinM: number) {
|
||||
.find((item) => coinM > item.threshold_m)?.correction || 0;
|
||||
}
|
||||
|
||||
function calculateRentDays(coinM: number) {
|
||||
return [...ratioConfig.value.rent_rules]
|
||||
.sort((a, b) => b.threshold_m - a.threshold_m)
|
||||
.find((item) => coinM >= item.threshold_m)?.days || 1;
|
||||
}
|
||||
|
||||
function formatNumber(value: number) {
|
||||
return Number.isInteger(value) ? `${value}` : `${Math.round(value * 10) / 10}`;
|
||||
}
|
||||
@@ -795,7 +784,7 @@ function readError(error: unknown, fallback: string) {
|
||||
/>
|
||||
</div>
|
||||
<p class="field-hint">
|
||||
此在线时间指的是百分百能够联系上您的时间,若是在此期间联系不上您导致无法上号会扣除您的部分租金或上架押金,在线时长太短可能无法上架,请预留充足时间用于扫码以及冻结人脸,请谨慎填写
|
||||
此在线时间指的是百分百能够联系上您的时间,若是在此期间联系不上您导致无法上号会扣除您的部分订单金额或上架押金,在线时长太短可能无法上架,请预留充足时间用于扫码以及冻结人脸,请谨慎填写
|
||||
</p>
|
||||
|
||||
<van-field label="封禁记录" required class="publish-field">
|
||||
|
||||
@@ -27,38 +27,24 @@ export function formatHafCoinM(amountWan: number) {
|
||||
}
|
||||
|
||||
export function getListingDisplayPrice(item: Listing) {
|
||||
const rentDays = getRentDays(item);
|
||||
if (rentDays > 0 && item.price_daily > 0) {
|
||||
return roundMoney(item.price_daily * rentDays);
|
||||
}
|
||||
return Number(item.price_daily || item.price_hourly || 0);
|
||||
}
|
||||
|
||||
export function getRentDays(item: Listing) {
|
||||
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, "publish_ratio");
|
||||
if (ratio > 0) return ratio;
|
||||
const price = getListingDisplayPrice(item);
|
||||
if (price <= 0) return 0;
|
||||
return getCoinWan(item) / price;
|
||||
}
|
||||
|
||||
export function formatRatio(item: Listing) {
|
||||
const pricePerM = getPricePerM(item);
|
||||
return pricePerM > 0 ? `1M=¥${formatMoney(pricePerM)}` : "--";
|
||||
const ratio = getRatioValue(item);
|
||||
return ratio > 0 ? `1:${formatRatioNumber(ratio)}` : "--";
|
||||
}
|
||||
|
||||
export function getValuePerYuanText(item: Listing) {
|
||||
const pricePerM = getPricePerM(item);
|
||||
return pricePerM > 0 ? `1M=¥${formatMoney(pricePerM)}` : "";
|
||||
}
|
||||
|
||||
export function getPricePerM(item: Listing) {
|
||||
const coinM = getCoinM(item);
|
||||
if (coinM <= 0) return 0;
|
||||
return roundMoney(getListingDisplayPrice(item) / coinM);
|
||||
return formatRatio(item);
|
||||
}
|
||||
|
||||
export function getLoginMethod(item: Listing) {
|
||||
@@ -83,9 +69,7 @@ export function getListingTitle(item: Listing) {
|
||||
}
|
||||
|
||||
export function getListingSubtitle(item: Listing) {
|
||||
return [getValuePerYuanText(item), `租期${getRentDays(item)}天`]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
return getValuePerYuanText(item);
|
||||
}
|
||||
|
||||
export function getListingChips(item: Listing): ListingDisplayChip[] {
|
||||
@@ -225,7 +209,7 @@ function roundMoney(value: number) {
|
||||
return Math.round(value * 100) / 100;
|
||||
}
|
||||
|
||||
function formatMoney(value: number) {
|
||||
function formatRatioNumber(value: number) {
|
||||
const rounded = roundMoney(value);
|
||||
return Number.isInteger(rounded) ? `${rounded}` : rounded.toFixed(2);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ const route = useRoute();
|
||||
const router = useRouter();
|
||||
const loading = ref(false);
|
||||
const ordering = ref(false);
|
||||
const rentHours = ref(1);
|
||||
const listing = ref<Listing | null>(null);
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -26,7 +25,7 @@ async function handleCreateOrder() {
|
||||
if (!listing.value) return;
|
||||
ordering.value = true;
|
||||
try {
|
||||
const order = await createOrder(listing.value.id, rentHours.value);
|
||||
const order = await createOrder(listing.value.id);
|
||||
ElMessage.success("订单已创建,账号已锁定");
|
||||
await router.push(`/orders/${order.id}`);
|
||||
} catch (error) {
|
||||
@@ -44,6 +43,10 @@ function readError(error: unknown, fallback: string) {
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function listingPrice(item: Listing) {
|
||||
return Number(item.price_daily || item.price_hourly || 0).toFixed(2);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -78,12 +81,8 @@ function readError(error: unknown, fallback: string) {
|
||||
<strong>{{ listing.haf_coin_amount }}</strong>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<span>时租</span>
|
||||
<strong>¥{{ listing.price_hourly }}</strong>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<span>日租</span>
|
||||
<strong>¥{{ listing.price_daily }}</strong>
|
||||
<span>价格</span>
|
||||
<strong>¥{{ listingPrice(listing) }}</strong>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<span>押金</span>
|
||||
@@ -93,27 +92,12 @@ function readError(error: unknown, fallback: string) {
|
||||
</section>
|
||||
|
||||
<aside class="order-panel pc-order-card">
|
||||
<h2>立即租用</h2>
|
||||
<p class="order-safe-text">
|
||||
平台托管订单与押金,租期 {{ listing.min_rent_hours }}-{{
|
||||
listing.max_rent_hours
|
||||
}}
|
||||
小时。
|
||||
</p>
|
||||
<h2>立即下单</h2>
|
||||
<p class="order-safe-text">平台托管订单与押金,按平台交接流程完成账号使用。</p>
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="租用时长">
|
||||
<el-input-number
|
||||
v-model="rentHours"
|
||||
:min="listing.min_rent_hours"
|
||||
:max="listing.max_rent_hours"
|
||||
class="full-control"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div class="order-total-box">
|
||||
<span>预计租金</span>
|
||||
<strong
|
||||
>¥{{ (listing.price_hourly * rentHours).toFixed(2) }}</strong
|
||||
>
|
||||
<span>价格</span>
|
||||
<strong>¥{{ listingPrice(listing) }}</strong>
|
||||
<em>押金 ¥{{ listing.deposit_amount }}</em>
|
||||
</div>
|
||||
<el-button
|
||||
|
||||
@@ -36,20 +36,20 @@ const filteredListings = computed(() => {
|
||||
const matchCoin =
|
||||
!filters.minCoin || item.haf_coin_amount >= filters.minCoin;
|
||||
const matchPrice =
|
||||
!filters.maxPrice || item.price_hourly <= filters.maxPrice;
|
||||
!filters.maxPrice || listingPrice(item) <= filters.maxPrice;
|
||||
return (
|
||||
matchKeyword && matchRegion && matchPlatform && matchCoin && matchPrice
|
||||
);
|
||||
});
|
||||
|
||||
return [...result].sort((a, b) => {
|
||||
if (sortBy.value === "price-asc") return a.price_hourly - b.price_hourly;
|
||||
if (sortBy.value === "price-asc") return listingPrice(a) - listingPrice(b);
|
||||
if (sortBy.value === "coin-desc")
|
||||
return b.haf_coin_amount - a.haf_coin_amount;
|
||||
if (sortBy.value === "deposit-asc")
|
||||
return a.deposit_amount - b.deposit_amount;
|
||||
return (
|
||||
b.haf_coin_amount - a.haf_coin_amount || a.price_hourly - b.price_hourly
|
||||
b.haf_coin_amount - a.haf_coin_amount || listingPrice(a) - listingPrice(b)
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -77,6 +77,10 @@ function resetFilters() {
|
||||
function uniqueOptions(values: string[]) {
|
||||
return [...new Set(values.filter(Boolean))];
|
||||
}
|
||||
|
||||
function listingPrice(item: Listing) {
|
||||
return Number(item.price_daily || item.price_hourly || 0);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -145,7 +149,7 @@ function uniqueOptions(values: string[]) {
|
||||
class="full-control"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="最高时租">
|
||||
<el-form-item label="最高价格">
|
||||
<el-input-number
|
||||
v-model="filters.maxPrice"
|
||||
:min="0"
|
||||
@@ -203,12 +207,9 @@ function uniqueOptions(values: string[]) {
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-price-box">
|
||||
<span>时租</span>
|
||||
<strong>¥{{ item.price_hourly }}</strong>
|
||||
<span>价格</span>
|
||||
<strong>¥{{ listingPrice(item).toFixed(2) }}</strong>
|
||||
<em>押金 ¥{{ item.deposit_amount }}</em>
|
||||
<small
|
||||
>{{ item.min_rent_hours }}-{{ item.max_rent_hours }} 小时</small
|
||||
>
|
||||
</div>
|
||||
</RouterLink>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="page-header">
|
||||
<p class="eyebrow">Earnings</p>
|
||||
<h1>收益流水</h1>
|
||||
<p>查看租金、平台抽成、结算和冻结金额。</p>
|
||||
<p>查看订单金额、平台抽成、结算和冻结金额。</p>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -21,14 +21,19 @@ const form = reactive({
|
||||
price_daily: 50,
|
||||
price_weekly: 300,
|
||||
deposit_amount: 100,
|
||||
min_rent_hours: 1,
|
||||
max_rent_hours: 24,
|
||||
})
|
||||
|
||||
async function handleSubmit() {
|
||||
loading.value = true
|
||||
try {
|
||||
await createListing({ ...form, screenshot_urls: screenshotUrls.value })
|
||||
const price = Number(form.price_daily || 0)
|
||||
await createListing({
|
||||
...form,
|
||||
price_hourly: Math.max(Math.round((price / 24) * 100) / 100, 0.01),
|
||||
price_daily: price,
|
||||
price_weekly: Math.round(price * 7 * 100) / 100,
|
||||
screenshot_urls: screenshotUrls.value,
|
||||
})
|
||||
ElMessage.success('发布已创建')
|
||||
await router.push('/seller/listings')
|
||||
} catch (error) {
|
||||
@@ -115,24 +120,12 @@ function readError(error: unknown, fallback: string) {
|
||||
<el-form-item label="哈夫币数量">
|
||||
<el-input-number v-model="form.haf_coin_amount" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="时租">
|
||||
<el-input-number v-model="form.price_hourly" :min="1" />
|
||||
</el-form-item>
|
||||
<el-form-item label="日租">
|
||||
<el-form-item label="价格">
|
||||
<el-input-number v-model="form.price_daily" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="周租">
|
||||
<el-input-number v-model="form.price_weekly" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="押金">
|
||||
<el-input-number v-model="form.deposit_amount" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="最短租期(小时)">
|
||||
<el-input-number v-model="form.min_rent_hours" :min="1" />
|
||||
</el-form-item>
|
||||
<el-form-item label="最长租期(小时)">
|
||||
<el-input-number v-model="form.max_rent_hours" :min="form.min_rent_hours" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-button type="primary" :loading="loading" @click="handleSubmit">保存发布</el-button>
|
||||
</el-form>
|
||||
|
||||
@@ -29,6 +29,10 @@ async function offline(id: number) {
|
||||
ElMessage.success('已下架')
|
||||
await loadListings()
|
||||
}
|
||||
|
||||
function listingPrice(row: Listing) {
|
||||
return `¥${Number(row.price_daily || row.price_hourly || 0).toFixed(2)}`
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -48,7 +52,9 @@ async function offline(id: number) {
|
||||
<el-table-column prop="title" label="标题" min-width="180" />
|
||||
<el-table-column prop="server_region" label="区服" width="120" />
|
||||
<el-table-column prop="haf_coin_amount" label="哈夫币" width="120" />
|
||||
<el-table-column prop="price_hourly" label="时租" width="100" />
|
||||
<el-table-column label="价格" width="100">
|
||||
<template #default="{ row }">{{ listingPrice(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="deposit_amount" label="押金" width="100" />
|
||||
<el-table-column prop="status" label="状态" width="110" />
|
||||
<el-table-column prop="review_status" label="审核" width="110" />
|
||||
|
||||
Reference in New Issue
Block a user