-
-
-
-
-
-
-
-
-
-
-
-
-
- 订单状态
- {{ orderStatusLabel(order.status) }}
-
-
- 交接状态
- {{ handoffStatusLabel(order.handoff_status) }}
-
-
- {{ orderAmountLabel }}
- ¥{{ money(orderRentDisplayAmount) }}
-
-
- {{ ownerIncomeLabel }}
- ¥{{ money(ownerIncomeDisplayAmount) }}
-
-
- 押金
- ¥{{ money(order.deposit_amount) }}
-
-
-
-
-
-
-
-
- 开始时间
- {{ formatDateTime(orderRentedAt(), '未开始') }}
-
-
- 预计截止
- {{ formatDateTime(orderEstimatedEndAt(), '未设置') }}
-
-
-
-
- 订单待支付,支付后账号进入交接流程。
-
-
- 立即支付订单
-
-
- 取消订单并释放账号
-
-
-
-
-
-
- 取消订单并释放账号
-
-
-
-
-
-
-
-
-
-
-
-
-
- 交接记录
-
-
- {{ formatHandoffRecordType(record.type) }}
- {{ record.content }}
-
-
-
- 发起结账
-
-
-
-
-
-
-
- 订单快照 {{ quantity(snapshotHafCoinM) }}M,预计剩余 {{ quantity(remainingHafCoinM) }}M
-
-
-
- 不含上方额外消耗品;仅填写封禁、违规、资产损坏等需要从押金赔付的费用。
-
-
-
- 额外消耗品
- 已用金额:¥{{ money(resourceChargeAmount) }}
-
-
-
+
+
- ¥{{ money(resourceLineAmount(item)) }}
+
+
+
+
+
+
+
+
- {{ item.label }}
- 库存 {{ item.quantity }},{{ item.mode }},{{ item.price || '未设置单价' }}
-
-
+
-
+ 订单状态
+ {{ orderStatusLabel(order.status) }}
+
+
+ 交接状态
+ {{ handoffStatusLabel(order.handoff_status) }}
+
+
+ {{ orderAmountLabel }}
+ ¥{{ money(orderRentDisplayAmount) }}
+
+
+ {{ ownerIncomeLabel }}
+ ¥{{ money(ownerIncomeDisplayAmount) }}
+
+
+ 押金
+ ¥{{ money(order.deposit_amount) }}
-
- 额外消耗品已用
- ¥{{ money(resourceChargeAmount) }}
- 按上方物资数量自动计算,计入实际结算租金
+
+
-
+
- 发起结账
-
-
+
-
+ 开始时间
+ {{ formatDateTime(orderRentedAt(), '未开始') }}
+
+
+ 预计截止
+ {{ formatDateTime(orderEstimatedEndAt(), '未设置') }}
+
- 其他押金赔付
- ¥{{ money(checkoutForm.other_amount) }}
- 作为押金赔付扣除,和额外消耗品分开展示
+
+
-
+
+
+ 订单待支付,支付后账号进入交接流程。
+
+
+ 立即支付订单
+
+
+ 取消订单并释放账号
+
+
+
+
+
+ 取消订单并释放账号
+
+
-
+
-
- 结账明细
-
-
-
- 实际结算租金
- ¥{{ money(order.checkout.display_amount) }}
+
-
+
+
+
+
+
-
+
+ 交接记录
+
+
+ {{ formatHandoffRecordType(record.type) }}
+ {{ record.content }}
+
- 预收押金
- ¥{{ money(order.checkout.deposit_amount) }}
-
-
- 额外消耗品已用
- ¥{{ money(order.checkout.consumable_amount) }}
-
-
- 押金赔付扣除
- ¥{{ money(order.checkout.deposit_deduct_amount) }}
-
-
- 退还租客(未使用租金 + 剩余押金)
- ¥{{ money(order.checkout.renter_refund_amount) }}
-
-
- 号主最终收入(租金 + 押金赔付)
- ¥{{ money(order.checkout.owner_income_amount) }}
-
-
-
-
- {{ order.checkout.content }}
-
-
-
- {{ order.checkout.owner_adjustment_reason }}
-
-
+
-
- 修改结账
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 提交修正给租客确认
-
- 修改结账金额
-
+
-
+
+ 发起结账
+
+
+
+
+
+ 订单快照 {{ quantity(snapshotHafCoinM) }}M,预计剩余
+ {{ quantity(remainingHafCoinM) }}M
+
+
+
+ 不含上方额外消耗品;仅填写封禁、违规、资产损坏等需要从押金赔付的费用。
+
+
+
+ 发起结账
+
+
+
+
+
+ 额外消耗品
+ 已用金额:¥{{ money(resourceChargeAmount) }}
+
+
+
+ ¥{{ money(resourceLineAmount(item)) }}
+
+
+ {{ item.label }}
+ 库存 {{ item.quantity }},{{ item.mode }},{{
+ item.price || '未设置单价'
+ }}
+
+
+
+
+ 额外消耗品已用
+ ¥{{ money(resourceChargeAmount) }}
+ 按上方物资数量自动计算,计入实际结算租金
+
+
+ 其他押金赔付
+ ¥{{ money(checkoutForm.other_amount) }}
+ 作为押金赔付扣除,和额外消耗品分开展示
+
+
-
-
-
- 拒绝修正
-
-
- 拒绝修正并发起争议
-
- 不同意修正时填写原因,订单会进入争议处理。
-
+
-
-
+
+ 结账明细
+
+
+
+ 实际结算租金
+ ¥{{ money(order.checkout.display_amount) }}
+
+
+ 预收押金
+ ¥{{ money(order.checkout.deposit_amount) }}
+
+
+ 额外消耗品已用
+ ¥{{ money(order.checkout.consumable_amount) }}
+
+
+ 押金赔付扣除
+ ¥{{ money(order.checkout.deposit_deduct_amount) }}
+
+
+ 退还租客(未使用租金 + 剩余押金)
+ ¥{{ money(order.checkout.renter_refund_amount) }}
+
+
+ 号主最终收入(租金 + 押金赔付)
+ ¥{{ money(order.checkout.owner_income_amount) }}
+
+
+
+
+ {{ order.checkout.content }}
+
+
+
+ {{ order.checkout.owner_adjustment_reason }}
+
-
-
-
- 提交交接
+
-
- 提交交接说明
-
+
+
+
+
+ 修改结账
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 提交修正给租客确认
+
+ 修改结账金额
+
+
+
+ 拒绝修正
+
+
+ 拒绝修正并发起争议
+
+ 不同意修正时填写原因,订单会进入争议处理。
+
- 确认已收到账号
-
-
-
- 确认收号
-确认账号可以正常登录后,订单会进入使用中并重新计算预计截止时间。
-
-
- 立即提交结账
-
-
- 查看详细结账表单
-
-
-
-
- 快捷结账
-如需修改消耗品、哈夫币等详细信息,请先向下滚动填写完整表单后再提交
-
-
- 确认结账并完成订单
-
-
确认结账
-
- 或者
+
+
+
+
- 需要修改结账金额
-
-
-
-
+
+ 提交交接
- 提交交接说明
+如认为金额有误,点击上方按钮修改
-
-
- 同意修正并完成订单
-
-
- 确认修正结账
-
- 或者
+
+
- 不同意,发起争议
-
-
-
-
+ 确认已收到账号
- 确认收号
++ 确认账号可以正常登录后,订单会进入使用中并重新计算预计截止时间。 +
+不同意修正时需填写原因,将进入争议处理
-
-
- {{ isCheckoutDisputeStage ? '提交结账争议' : '提交申诉' }}
-
-
{{ isCheckoutDisputeStage ? '发起结账争议' : '发起申诉' }}
-点击后滚动到申诉表单填写详细信息
+ +
+
+ 立即提交结账
+
+
+ 查看详细结账表单
+
+
+
+
+ 快捷结账
++ 如需修改消耗品、哈夫币等详细信息,请先向下滚动填写完整表单后再提交 +
+
+
+ 确认结账并完成订单
+
+
+ 需要修改结账金额
+
+
+
+
+ 确认结账
+
+ 或者
+
+ 如认为金额有误,点击上方按钮修改
+
+
+ 同意修正并完成订单
+
+
+ 不同意,发起争议
+
+
+
+
+ 确认修正结账
+
+ 或者
+
+ 不同意修正时需填写原因,将进入争议处理
+
+
+ {{ isCheckoutDisputeStage ? '提交结账争议' : '提交申诉' }}
+
+
{{ isCheckoutDisputeStage ? '发起结账争议' : '发起申诉' }}
+点击后滚动到申诉表单填写详细信息
+{{ isCheckoutDisputeStage ? '发起结账争议' : '发起申诉' }}
-
+
@@ -993,7 +1198,12 @@ function formatHandoffRecordType(type: string) {
placeholder="证据链接,一行一个。开发阶段可先填截图地址或备注链接"
/>
{{ isCheckoutDisputeStage ? '提交结账争议' : '提交申诉' }}
@@ -1597,7 +1807,7 @@ function formatHandoffRecordType(type: string) {
border-radius: 6px;
}
-.upload-line input[type="file"] {
+.upload-line input[type='file'] {
width: 100%;
font-size: 14px;
}
diff --git a/frontend/src/features/orders/views/OrdersView.vue b/frontend/src/features/orders/views/OrdersView.vue
index 3ebcce8..eac1e64 100644
--- a/frontend/src/features/orders/views/OrdersView.vue
+++ b/frontend/src/features/orders/views/OrdersView.vue
@@ -24,7 +24,7 @@ const statusTabs = [
{ key: 'pending_checkout_confirm', label: '待结账' },
{ key: 'completed', label: '已完成' },
]
-const tabKeys = new Set(statusTabs.map((item) => item.key))
+const tabKeys = new Set(statusTabs.map(item => item.key))
const activeTab = ref(readTab(route.query.tab))
const fallbackPendingPaymentMinutes = 15
@@ -32,14 +32,15 @@ const displayOrders = computed(() => {
let filtered = orders.value
if (activeTab.value !== 'all') {
- filtered = filtered.filter((order) => order.status === activeTab.value)
+ filtered = filtered.filter(order => order.status === activeTab.value)
}
if (searchKeyword.value.trim()) {
const keyword = searchKeyword.value.trim().toLowerCase()
- filtered = filtered.filter((order) =>
- order.order_no.toLowerCase().includes(keyword) ||
- order.title.toLowerCase().includes(keyword)
+ filtered = filtered.filter(
+ order =>
+ order.order_no.toLowerCase().includes(keyword) ||
+ order.title.toLowerCase().includes(keyword)
)
}
@@ -48,9 +49,9 @@ const displayOrders = computed(() => {
const tabCounts = computed(() => {
const counts: Record = { all: orders.value.length }
- statusTabs.forEach((tab) => {
+ statusTabs.forEach(tab => {
if (tab.key !== 'all') {
- counts[tab.key] = orders.value.filter((order) => order.status === tab.key).length
+ counts[tab.key] = orders.value.filter(order => order.status === tab.key).length
}
})
return counts
@@ -60,9 +61,9 @@ onMounted(loadOrders)
watch(
() => route.query.tab,
- (tab) => {
+ tab => {
activeTab.value = readTab(tab)
- },
+ }
)
function readTab(tab: unknown) {
@@ -193,7 +194,9 @@ function getCountdownMinutes(order: Order) {
{{ tab.label }}
- {{ tabCounts?.[tab.key] }}
+ {{
+ tabCounts?.[tab.key]
+ }}
@@ -208,7 +211,9 @@ function getCountdownMinutes(order: Order) {
-
+
- {{ shortenOrderNo(row.order_no) }}
+ {{
+ shortenOrderNo(row.order_no)
+ }}
@@ -221,7 +226,9 @@ function getCountdownMinutes(order: Order) {
¥{{ money(orderRentAmount(row)) }}
{{ amountLabel(row) }}
- 实际到手 ¥{{ money(ownerActualIncome(row)) }}
+ 实际到手 ¥{{ money(ownerActualIncome(row)) }}
@@ -239,7 +246,10 @@ function getCountdownMinutes(order: Order) {
{{ orderStatusLabel(row.status) }}
-
+
{{ getCountdownMinutes(row) }}分钟后超时
@@ -270,10 +280,17 @@ function getCountdownMinutes(order: Order) {
-
+
@@ -537,7 +545,11 @@ function selectOnlineEnd(value: string) {
class="publish-field"
>
-
+
押金{{ priceConfig.deposit_hint }}
@@ -583,7 +595,7 @@ function selectOnlineEnd(value: string) {
{{
calculatedDefaultSaleRatio > 0
? `1元=${formatNumber(calculatedDefaultSaleRatio)}万`
- : "自动计算"
+ : '自动计算'
}}
@@ -599,7 +611,7 @@ function selectOnlineEnd(value: string) {
{{
maxAcceleratedSaleRatio > 0
? `1元=${formatNumber(calculatedDefaultSaleRatio)}~${formatNumber(maxAcceleratedSaleRatio)}万`
- : "自动计算"
+ : '自动计算'
}}
@@ -626,7 +638,9 @@ function selectOnlineEnd(value: string) {
class="publish-field result-field"
>
- 卖家比例
+ 卖家比例
- 纯币基础价
+ 纯币基础价
-
- {{ shortenOrderNo(order.order_no) }}
+ {{
+ shortenOrderNo(order.order_no)
+ }}
@@ -304,7 +321,10 @@ function getCountdownMinutes(order: Order) {
>((groups, group) => {
- groups[group.key] = group.options.filter((skin) => selectedSkins.value.includes(skin))
+ groups[group.key] = group.options.filter(skin => selectedSkins.value.includes(skin))
return groups
}, {}),
screenshot_groups: buildScreenshotGroups(),
@@ -614,7 +644,7 @@ export function usePublishForm(options: UsePublishFormOptions) {
}
function getScreenshotLabel(key: ScreenshotKey) {
- return pricing.screenshotSlots.value.find((item) => item.key === key)?.label || '该截图'
+ return pricing.screenshotSlots.value.find(item => item.key === key)?.label || '该截图'
}
return {
diff --git a/frontend/src/features/seller/views/MobileSellerListingCreateView.css b/frontend/src/features/seller/views/MobileSellerListingCreateView.css
index e5b8cbd..765ca73 100644
--- a/frontend/src/features/seller/views/MobileSellerListingCreateView.css
+++ b/frontend/src/features/seller/views/MobileSellerListingCreateView.css
@@ -128,13 +128,15 @@
opacity: 0;
visibility: hidden;
pointer-events: none;
- transition: opacity 0.16s ease, visibility 0.16s ease;
+ transition:
+ opacity 0.16s ease,
+ visibility 0.16s ease;
}
.hint-label::before,
.backend-hint-target.has-hint::before,
.upload-title.has-hint::before {
- content: "";
+ content: '';
top: 50%;
left: calc(100% + 2px);
transform: translateY(-50%);
@@ -753,8 +755,6 @@
background: #ff6a00 !important;
}
-
-
@media (min-width: 430px) {
.time-row {
grid-template-columns: 1fr 1fr;
diff --git a/frontend/src/features/seller/views/MobileSellerListingCreateView.vue b/frontend/src/features/seller/views/MobileSellerListingCreateView.vue
index 70a13a8..522d0ee 100644
--- a/frontend/src/features/seller/views/MobileSellerListingCreateView.vue
+++ b/frontend/src/features/seller/views/MobileSellerListingCreateView.vue
@@ -83,9 +83,9 @@ const {
showCancelButton: true,
})
},
- notifySuccess: (message) => showToast({ message, icon: 'passed' }),
- notifyWarning: (message) => showToast({ message, icon: 'warning-o' }),
- notifyError: (message) => showToast({ message, icon: 'cross' }),
+ notifySuccess: message => showToast({ message, icon: 'passed' }),
+ notifyWarning: message => showToast({ message, icon: 'warning-o' }),
+ notifyError: message => showToast({ message, icon: 'cross' }),
})
const agreementPopupVisible = ref(false)
@@ -151,7 +151,7 @@ function selectOnlineEnd(value: string) {
type="button"
class="radio-btn"
:class="{ active: form.server_region === opt }"
- @click="selectRadio(opt, (value) => (form.server_region = value))"
+ @click="selectRadio(opt, value => (form.server_region = value))"
>
{{ opt }}
@@ -168,7 +168,7 @@ function selectOnlineEnd(value: string) {
type="button"
class="radio-btn"
:class="{ active: form.face_owner === opt }"
- @click="selectRadio(opt, (value) => (form.face_owner = value))"
+ @click="selectRadio(opt, value => (form.face_owner = value))"
>
{{ opt }}
@@ -194,7 +194,7 @@ function selectOnlineEnd(value: string) {
type="button"
class="radio-btn"
:class="{ active: form.rank_level === opt }"
- @click="selectRadio(opt, (value) => (form.rank_level = value))"
+ @click="selectRadio(opt, value => (form.rank_level = value))"
>
{{ opt }}
@@ -227,7 +227,7 @@ function selectOnlineEnd(value: string) {
type="button"
class="radio-btn"
:class="{ active: form.season_insurance === opt }"
- @click="selectRadio(opt, (value) => (form.season_insurance = value))"
+ @click="selectRadio(opt, value => (form.season_insurance = value))"
>
{{ opt }}
@@ -245,7 +245,7 @@ function selectOnlineEnd(value: string) {
type="button"
class="level-btn"
:class="{ active: form.stamina_level === opt }"
- @click="selectRadio(opt, (value) => (form.stamina_level = value))"
+ @click="selectRadio(opt, value => (form.stamina_level = value))"
>
{{ opt }}
@@ -260,7 +260,7 @@ function selectOnlineEnd(value: string) {
type="button"
class="level-btn"
:class="{ active: form.load_level === opt }"
- @click="selectRadio(opt, (value) => (form.load_level = value))"
+ @click="selectRadio(opt, value => (form.load_level = value))"
>
{{ opt }}
@@ -287,7 +287,9 @@ function selectOnlineEnd(value: string) {
>
*{{ item.label }}
- {{ isQuantityItemDisabled(item) ? "3*3 已包含,不能填写" : item.price }}
+ {{
+ isQuantityItemDisabled(item) ? '3*3 已包含,不能填写' : item.price
+ }}
{{ group.title }}
- {{
- group.options.filter((skin) => selectedSkins.includes(skin))
- .length
- }} 项
+ {{ group.options.filter(skin => selectedSkins.includes(skin)).length }} 项
{{ orderStatusLabel(order.status) }}
-
+
{{ getCountdownMinutes(order) }}分钟后超时
diff --git a/frontend/src/features/seller/composables/usePublishForm.ts b/frontend/src/features/seller/composables/usePublishForm.ts
index b94f024..2110acc 100644
--- a/frontend/src/features/seller/composables/usePublishForm.ts
+++ b/frontend/src/features/seller/composables/usePublishForm.ts
@@ -73,11 +73,11 @@ export function usePublishForm(options: UsePublishFormOptions) {
selectedSkins,
})
const uploadedScreenshotCount = computed(
- () => pricing.screenshotSlots.value.filter((item) => getScreenshotCount(item.key) > 0).length,
+ () => pricing.screenshotSlots.value.filter(item => getScreenshotCount(item.key) > 0).length
)
const requiredScreenshotCount = ref(0)
const canPublishAfterAgreements = computed(
- () => virtualAssetSaleAgreementChecked.value && sellerAgreementChecked.value,
+ () => virtualAssetSaleAgreementChecked.value && sellerAgreementChecked.value
)
onMounted(() => {
@@ -98,7 +98,7 @@ export function usePublishForm(options: UsePublishFormOptions) {
() => {
saveDraft()
},
- { deep: true },
+ { deep: true }
)
watch(
@@ -106,7 +106,7 @@ export function usePublishForm(options: UsePublishFormOptions) {
() => {
syncRecommendedDeposit()
},
- { immediate: true },
+ { immediate: true }
)
watch(
@@ -114,15 +114,17 @@ export function usePublishForm(options: UsePublishFormOptions) {
() => {
clearForbiddenQuantityItems()
},
- { deep: true },
+ { deep: true }
)
watch(
[pricing.screenshotSlots, () => form.ban_record],
() => {
- requiredScreenshotCount.value = pricing.screenshotSlots.value.filter((item) => isScreenshotRequired(item)).length
+ requiredScreenshotCount.value = pricing.screenshotSlots.value.filter(item =>
+ isScreenshotRequired(item)
+ ).length
},
- { immediate: true, deep: true },
+ { immediate: true, deep: true }
)
async function loadPublishOptions() {
@@ -186,7 +188,7 @@ export function usePublishForm(options: UsePublishFormOptions) {
quantityModes,
screenshotFiles,
selectedSkins: selectedSkins.value,
- }),
+ })
)
}
@@ -256,13 +258,13 @@ export function usePublishForm(options: UsePublishFormOptions) {
function toggleSkin(skin: string) {
selectedSkins.value = selectedSkins.value.includes(skin)
- ? selectedSkins.value.filter((item) => item !== skin)
+ ? selectedSkins.value.filter(item => item !== skin)
: [...selectedSkins.value, skin]
}
function toggleRegion(region: string) {
form.common_regions = form.common_regions.includes(region)
- ? form.common_regions.filter((item) => item !== region)
+ ? form.common_regions.filter(item => item !== region)
: [...form.common_regions, region]
}
@@ -288,7 +290,9 @@ export function usePublishForm(options: UsePublishFormOptions) {
}
const uploadFiles = files.slice(0, availableCount)
if (files.length > availableCount) {
- options.notifyWarning(`${getScreenshotLabel(key)}最多上传${getScreenshotLimit(key)}张,本次仅上传${availableCount}张`)
+ options.notifyWarning(
+ `${getScreenshotLabel(key)}最多上传${getScreenshotLimit(key)}张,本次仅上传${availableCount}张`
+ )
}
uploading.value = true
try {
@@ -302,7 +306,9 @@ export function usePublishForm(options: UsePublishFormOptions) {
throw error
}
}
- options.notifySuccess(uploadFiles.length > 1 ? `已上传${uploadFiles.length}张截图` : '截图已上传')
+ options.notifySuccess(
+ uploadFiles.length > 1 ? `已上传${uploadFiles.length}张截图` : '截图已上传'
+ )
} catch (error) {
options.notifyError(readError(error, '截图上传失败'))
} finally {
@@ -353,7 +359,12 @@ export function usePublishForm(options: UsePublishFormOptions) {
async function hydrateScreenshotPreviews() {
for (const [key, fileURLs] of Object.entries(screenshotFiles)) {
for (const [index, fileURL] of fileURLs.entries()) {
- if (!fileURL || screenshotPreviews[key]?.[index] || !fileURL.startsWith('/api/files/object')) continue
+ if (
+ !fileURL ||
+ screenshotPreviews[key]?.[index] ||
+ !fileURL.startsWith('/api/files/object')
+ )
+ continue
try {
const blob = await fetchFileBlobByURL(fileURL)
setScreenshotPreview(key, index, URL.createObjectURL(blob))
@@ -411,14 +422,18 @@ export function usePublishForm(options: UsePublishFormOptions) {
}
function clampAcceleratedSaleRatioInput() {
- if (!pricing.hasAcceleratedSaleRatioInput() || pricing.calculatedDefaultSaleRatio.value <= 0) return
+ if (!pricing.hasAcceleratedSaleRatioInput() || pricing.calculatedDefaultSaleRatio.value <= 0)
+ return
const ratio = Number(form.accelerated_sale_ratio)
if (!Number.isFinite(ratio)) {
form.accelerated_sale_ratio = ''
return
}
form.accelerated_sale_ratio = roundRatio(
- Math.min(Math.max(ratio, pricing.calculatedDefaultSaleRatio.value), pricing.maxAcceleratedSaleRatio.value),
+ Math.min(
+ Math.max(ratio, pricing.calculatedDefaultSaleRatio.value),
+ pricing.maxAcceleratedSaleRatio.value
+ )
)
}
@@ -460,7 +475,7 @@ export function usePublishForm(options: UsePublishFormOptions) {
options.notifySuccess(
listing.status === 'published' && listing.review_status === 'approved'
? '发布成功,已上架'
- : '发布成功,等待后台审核',
+ : '发布成功,等待后台审核'
)
await router.push(options.submitSuccessPath)
} catch (error) {
@@ -475,7 +490,8 @@ export function usePublishForm(options: UsePublishFormOptions) {
if (pricing.coinMAmount.value <= 0) return '请填写哈夫币/M'
if (!form.rank_level) return '请选择段位'
if (!form.fire_level) return '请填写烽火等级'
- if (Number(form.fire_level) < pricing.fireLevelMin.value) return `烽火等级低于${pricing.fireLevelMin.value}级的号无法发布`
+ if (Number(form.fire_level) < pricing.fireLevelMin.value)
+ return `烽火等级低于${pricing.fireLevelMin.value}级的号无法发布`
if (!form.season_insurance) return '请选择赛季保险'
if (!form.stamina_level) return '请选择体力等级'
if (!form.load_level) return '请选择负重等级'
@@ -483,27 +499,41 @@ export function usePublishForm(options: UsePublishFormOptions) {
if (pricing.hasAcceleratedSaleRatioInput()) {
const ratio = Number(form.accelerated_sale_ratio)
if (!Number.isFinite(ratio) || ratio <= 0) return '加速出售比例格式不正确'
- if (pricing.calculatedDefaultSaleRatio.value > 0 && ratio < pricing.calculatedDefaultSaleRatio.value) {
+ if (
+ pricing.calculatedDefaultSaleRatio.value > 0 &&
+ ratio < pricing.calculatedDefaultSaleRatio.value
+ ) {
return `加速出售比例不能低于默认比例 1:${formatNumber(pricing.calculatedDefaultSaleRatio.value)}`
}
- if (pricing.maxAcceleratedSaleRatio.value > 0 && ratio > pricing.maxAcceleratedSaleRatio.value) {
+ if (
+ pricing.maxAcceleratedSaleRatio.value > 0 &&
+ ratio > pricing.maxAcceleratedSaleRatio.value
+ ) {
return `加速出售比例不能超过 1:${formatNumber(pricing.maxAcceleratedSaleRatio.value)}`
}
}
if (pricing.banRecordOptions.value.length && !form.ban_record) return '请选择封禁记录'
if (form.deposit_amount === '' || Number(form.deposit_amount) < 0) return '请填写押金'
if (!Number.isFinite(Number(form.deposit_amount))) return '押金格式不正确'
- if (pricing.recommendedDepositAmount.value > 0 && Number(form.deposit_amount) < pricing.recommendedDepositAmount.value) {
+ if (
+ pricing.recommendedDepositAmount.value > 0 &&
+ Number(form.deposit_amount) < pricing.recommendedDepositAmount.value
+ ) {
return `押金不能低于智能推荐 ¥${pricing.recommendedDepositAmount.value}`
}
- if (pricing.calculatedConsumablePrice.value > 0 && Number(form.deposit_amount) <= pricing.calculatedConsumablePrice.value) {
+ if (
+ pricing.calculatedConsumablePrice.value > 0 &&
+ Number(form.deposit_amount) <= pricing.calculatedConsumablePrice.value
+ ) {
return `押金必须大于额外消耗品总价值 ¥${pricing.calculatedConsumablePrice.value}`
}
if (!pricing.calculatedFinalPrice.value) return '请完善币数、保险、体力和负重后再发布'
- if (!Number.isFinite(pricing.calculatedFinalPrice.value)) return '发布价格计算异常,请检查填写内容'
+ if (!Number.isFinite(pricing.calculatedFinalPrice.value))
+ return '发布价格计算异常,请检查填写内容'
if (!canPublishAfterAgreements.value) return '请先阅读并勾选两份发布协议'
for (const item of pricing.screenshotSlots.value) {
- if (isScreenshotRequired(item) && getScreenshotCount(item.key) === 0) return `请上传${item.label}`
+ if (isScreenshotRequired(item) && getScreenshotCount(item.key) === 0)
+ return `请上传${item.label}`
}
for (const item of pricing.quantityItems.value) {
if (!pricing.isQuantityItemDisabled(item)) {
@@ -558,7 +588,7 @@ export function usePublishForm(options: UsePublishFormOptions) {
season_insurance: form.season_insurance,
stamina_level: form.stamina_level,
load_level: form.load_level,
- resources: pricing.quantityItems.value.map((item) => ({
+ resources: pricing.quantityItems.value.map(item => ({
key: item.key,
label: item.label,
price: item.price,
@@ -566,7 +596,7 @@ export function usePublishForm(options: UsePublishFormOptions) {
mode: pricing.isQuantityItemDisabled(item) ? '赠送' : quantityModes[item.key] || '收费',
})),
skin_groups: pricing.skinGroups.value.reduce
@@ -357,7 +356,7 @@ function selectOnlineEnd(value: string) {
type="button"
class="radio-btn"
:class="{ active: form.login_method === opt }"
- @click="selectRadio(opt, (value) => (form.login_method = value))"
+ @click="selectRadio(opt, value => (form.login_method = value))"
>
{{ opt }}
@@ -439,7 +438,7 @@ function selectOnlineEnd(value: string) {
type="button"
class="radio-btn"
:class="{ active: form.ban_record === opt }"
- @click="selectRadio(opt, (value) => (form.ban_record = value))"
+ @click="selectRadio(opt, value => (form.ban_record = value))"
>
{{ opt }}
@@ -484,11 +483,20 @@ function selectOnlineEnd(value: string) {
{{ slot.label }}*
{{ slot.hint }}
- {{ getScreenshotLimitHint(slot.key) }}
+ {{
+ getScreenshotLimitHint(slot.key)
+ }}
-
- {{ uploading && activeUploadKey === slot.key ? "上传中" : "上传" }}
+ {{ uploading && activeUploadKey === slot.key ? '上传中' : '上传' }}
-
+
- {{ uploading && activeUploadKey === slot.key ? "上传中" : "继续上传" }}
+ {{ uploading && activeUploadKey === slot.key ? '上传中' : '继续上传' }}
+
移除
- 卖家价格 = 纯币基础价 + 额外消耗品。 -
+卖家价格 = 纯币基础价 + 额外消耗品。
-
+
重置
-
+
([])
const status = ref('')
-const sellerOrders = computed(() => orders.value.filter((order) => order.owner_id === session.userId))
+const sellerOrders = computed(() => orders.value.filter(order => order.owner_id === session.userId))
const todoOrders = computed(() =>
- sellerOrders.value.filter((order) =>
+ sellerOrders.value.filter(order =>
[
'pending_handoff',
'renting',
@@ -24,22 +24,28 @@ const todoOrders = computed(() =>
'checkout_disputing',
'disputing',
'abnormal',
- ].includes(order.status),
- ),
+ ].includes(order.status)
+ )
)
const displayOrders = computed(() => {
const source = todoOrders.value
if (!status.value) return source
- return source.filter((order) => order.status === status.value)
+ return source.filter(order => order.status === status.value)
})
const pendingHandoffCount = computed(
- () => sellerOrders.value.filter((order) => order.status === 'pending_handoff' && order.handoff_status === 'pending_owner').length,
+ () =>
+ sellerOrders.value.filter(
+ order => order.status === 'pending_handoff' && order.handoff_status === 'pending_owner'
+ ).length
)
const pendingCheckoutCount = computed(
- () => sellerOrders.value.filter((order) => order.status === 'pending_checkout_confirm').length,
+ () => sellerOrders.value.filter(order => order.status === 'pending_checkout_confirm').length
)
const abnormalCount = computed(
- () => sellerOrders.value.filter((order) => ['overdue', 'checkout_disputing', 'disputing', 'abnormal'].includes(order.status)).length,
+ () =>
+ sellerOrders.value.filter(order =>
+ ['overdue', 'checkout_disputing', 'disputing', 'abnormal'].includes(order.status)
+ ).length
)
onMounted(loadOrders)
@@ -54,10 +60,12 @@ async function loadOrders() {
}
function actionText(order: Order) {
- if (order.status === 'pending_handoff' && order.handoff_status === 'pending_owner') return '提交交接'
+ if (order.status === 'pending_handoff' && order.handoff_status === 'pending_owner')
+ return '提交交接'
if (order.status === 'pending_checkout_confirm') return '处理结账'
if (order.status === 'pending_checkout_accept') return '等待租客'
- if (['overdue', 'checkout_disputing', 'disputing', 'abnormal'].includes(order.status)) return '查看处理'
+ if (['overdue', 'checkout_disputing', 'disputing', 'abnormal'].includes(order.status))
+ return '查看处理'
return '详情'
}
diff --git a/frontend/src/features/seller/views/SellerListingCreateView.css b/frontend/src/features/seller/views/SellerListingCreateView.css
index 3b87d16..5b2c8c9 100644
--- a/frontend/src/features/seller/views/SellerListingCreateView.css
+++ b/frontend/src/features/seller/views/SellerListingCreateView.css
@@ -27,7 +27,9 @@
.publish-page :deep(.el-textarea__inner) {
border-radius: var(--radius-8);
box-shadow: 0 0 0 1px var(--color-border) inset;
- transition: box-shadow 0.2s ease, background-color 0.2s ease;
+ transition:
+ box-shadow 0.2s ease,
+ background-color 0.2s ease;
}
.publish-page :deep(.el-input__wrapper:hover),
@@ -37,7 +39,9 @@
.publish-page :deep(.el-input__wrapper.is-focus),
.publish-page :deep(.el-textarea__inner:focus) {
- box-shadow: 0 0 0 1px var(--color-orange-primary) inset, 0 0 8px rgba(255, 106, 0, 0.15) !important;
+ box-shadow:
+ 0 0 0 1px var(--color-orange-primary) inset,
+ 0 0 8px rgba(255, 106, 0, 0.15) !important;
}
.publish-layout {
@@ -211,7 +215,11 @@
color: #596474;
font-weight: 800;
cursor: pointer;
- transition: border-color 0.16s ease, background 0.16s ease, color 0.16s ease, transform 0.16s ease;
+ transition:
+ border-color 0.16s ease,
+ background 0.16s ease,
+ color 0.16s ease,
+ transform 0.16s ease;
}
.level-btn,
@@ -246,7 +254,9 @@
.quantity-header,
.quantity-item {
display: grid;
- grid-template-columns: minmax(180px, 3fr) minmax(80px, 1fr) minmax(100px, 1.2fr) minmax(140px, 1.5fr) minmax(80px, 1fr);
+ grid-template-columns:
+ minmax(180px, 3fr) minmax(80px, 1fr) minmax(100px, 1.2fr) minmax(140px, 1.5fr)
+ minmax(80px, 1fr);
gap: 10px;
align-items: center;
}
@@ -397,7 +407,10 @@
font-size: 12px;
font-weight: 800;
cursor: pointer;
- transition: border-color 0.16s ease, background 0.16s ease, color 0.16s ease;
+ transition:
+ border-color 0.16s ease,
+ background 0.16s ease,
+ color 0.16s ease;
}
.time-chip.active {
@@ -548,7 +561,9 @@
font-size: 12px;
font-weight: 800;
cursor: pointer;
- transition: border-color 0.2s ease, background 0.2s ease;
+ transition:
+ border-color 0.2s ease,
+ background 0.2s ease;
}
.recommend-button:hover {
@@ -636,7 +651,10 @@
gap: 6px;
border: 1px solid #d6dce5;
background: #fff;
- transition: border-color 0.16s ease, background 0.16s ease, color 0.16s ease;
+ transition:
+ border-color 0.16s ease,
+ background 0.16s ease,
+ color 0.16s ease;
}
.ratio-custom-card strong {
@@ -676,7 +694,9 @@
min-height: 82px;
padding: 14px;
border-left: 4px solid #ccd5e0;
- transition: transform 0.2s ease, box-shadow 0.2s ease;
+ transition:
+ transform 0.2s ease,
+ box-shadow 0.2s ease;
}
.price-cell:hover {
@@ -883,7 +903,9 @@
background-color: var(--color-orange-primary);
color: #fff;
font-weight: 800;
- transition: border-color 0.2s ease, background-color 0.2s ease;
+ transition:
+ border-color 0.2s ease,
+ background-color 0.2s ease;
}
.summary-actions :deep(.el-button.btn-publish:hover) {
diff --git a/frontend/src/features/seller/views/SellerListingCreateView.vue b/frontend/src/features/seller/views/SellerListingCreateView.vue
index efe6d2e..844e236 100644
--- a/frontend/src/features/seller/views/SellerListingCreateView.vue
+++ b/frontend/src/features/seller/views/SellerListingCreateView.vue
@@ -1,5 +1,11 @@
@@ -100,12 +100,7 @@ async function handleSupportClick() {
{{ activeAgreementTitle }}
{{ activeAgreementContent }}
diff --git a/frontend/src/features/seller/views/SellerHandoffsView.vue b/frontend/src/features/seller/views/SellerHandoffsView.vue
index bc9f4d1..f154be6 100644
--- a/frontend/src/features/seller/views/SellerHandoffsView.vue
+++ b/frontend/src/features/seller/views/SellerHandoffsView.vue
@@ -12,9 +12,9 @@ const loading = ref(false)
const orders = ref
-
+
- {{ supportLoading ? "接入中" : "客服" }}
+ {{ supportLoading ? '接入中' : '客服' }}
-
+
发布账号
@@ -157,33 +160,23 @@ async function handleSupportClick() {
{{ realnameBadge.text }}
-
+
-
+
个人资料
-
+
我的订单
-
+
我的钱包
@@ -808,7 +801,9 @@ async function handleSupportClick() {
/* 下拉动画 */
.dropdown-enter-active,
.dropdown-leave-active {
- transition: opacity 0.2s ease, transform 0.2s ease;
+ transition:
+ opacity 0.2s ease,
+ transform 0.2s ease;
}
.dropdown-enter-from {
opacity: 0;
diff --git a/frontend/src/main.ts b/frontend/src/main.ts
index a68bf46..0580cda 100644
--- a/frontend/src/main.ts
+++ b/frontend/src/main.ts
@@ -1,33 +1,33 @@
-import "element-plus/theme-chalk/el-message.css";
-import "element-plus/theme-chalk/el-message-box.css";
-import "vant/lib/index.css";
-import "./styles/base.css";
-import "./styles/pc.css";
-import "./styles/admin.css";
+import 'element-plus/theme-chalk/el-message.css'
+import 'element-plus/theme-chalk/el-message-box.css'
+import 'vant/lib/index.css'
+import './styles/base.css'
+import './styles/pc.css'
+import './styles/admin.css'
-import { createPinia } from "pinia";
-import { createApp } from "vue";
+import { createPinia } from 'pinia'
+import { createApp } from 'vue'
-import App from "./App.vue";
-import router from "./router";
-import { useAdminSessionStore } from "./stores/adminSession";
-import { useSessionStore } from "./stores/session";
+import App from './App.vue'
+import router from './router'
+import { useAdminSessionStore } from './stores/adminSession'
+import { useSessionStore } from './stores/session'
-const app = createApp(App);
-const pinia = createPinia();
+const app = createApp(App)
+const pinia = createPinia()
-app.use(pinia).use(router);
+app.use(pinia).use(router)
-window.addEventListener("auth-storage-changed", () => {
- useSessionStore(pinia).syncFromStorage();
- useAdminSessionStore(pinia).syncFromStorage();
-});
+window.addEventListener('auth-storage-changed', () => {
+ useSessionStore(pinia).syncFromStorage()
+ useAdminSessionStore(pinia).syncFromStorage()
+})
-window.addEventListener("storage", (event) => {
+window.addEventListener('storage', event => {
if (event.storageArea === localStorage) {
- useSessionStore(pinia).syncFromStorage();
- useAdminSessionStore(pinia).syncFromStorage();
+ useSessionStore(pinia).syncFromStorage()
+ useAdminSessionStore(pinia).syncFromStorage()
}
-});
+})
-router.isReady().then(() => app.mount("#app"));
+router.isReady().then(() => app.mount('#app'))
diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts
index c2b8abb..7401af9 100644
--- a/frontend/src/router/index.ts
+++ b/frontend/src/router/index.ts
@@ -12,16 +12,10 @@ import { sellerRoutes } from './sellerRoutes'
const router = createRouter({
history: createWebHistory(),
- routes: [
- ...adminRoutes,
- ...mobileRoutes,
- ...publicRoutes,
- ...accountRoutes,
- ...sellerRoutes,
- ],
+ routes: [...adminRoutes, ...mobileRoutes, ...publicRoutes, ...accountRoutes, ...sellerRoutes],
})
-router.beforeEach(async (to) => {
+router.beforeEach(async to => {
const isMobile = isMobileHost() || isMobileDevice()
if (isMobile) {
diff --git a/frontend/src/router/mobileHost.ts b/frontend/src/router/mobileHost.ts
index 98168d3..90c36c7 100644
--- a/frontend/src/router/mobileHost.ts
+++ b/frontend/src/router/mobileHost.ts
@@ -1,77 +1,77 @@
export function isMobileHost(hostname = window.location.hostname) {
- return hostname === "m.localhost" || hostname.startsWith("m.");
+ return hostname === 'm.localhost' || hostname.startsWith('m.')
}
export function isMobileDevice() {
- if (typeof window === "undefined" || !window.navigator) return false;
- const ua = window.navigator.userAgent;
- const isMobileUA = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua);
- const isSmallScreen = window.innerWidth < 768;
- return isMobileUA || isSmallScreen;
+ if (typeof window === 'undefined' || !window.navigator) return false
+ const ua = window.navigator.userAgent
+ const isMobileUA = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua)
+ const isSmallScreen = window.innerWidth < 768
+ return isMobileUA || isSmallScreen
}
export function getMobilePath(path: string): string {
- if (path === "/" || path === "") {
- return "/m";
+ if (path === '/' || path === '') {
+ return '/m'
}
- if (path === "/m" || path.startsWith("/m/") || path.startsWith("/admin")) {
- return path;
+ if (path === '/m' || path.startsWith('/m/') || path.startsWith('/admin')) {
+ return path
}
-
- if (path === "/listings" || path.startsWith("/listings/")) {
- return `/m${path}`;
+
+ if (path === '/listings' || path.startsWith('/listings/')) {
+ return `/m${path}`
}
- if (path === "/orders" || path.startsWith("/orders/")) {
- return `/m${path}`;
+ if (path === '/orders' || path.startsWith('/orders/')) {
+ return `/m${path}`
}
- if (path === "/realname") return "/m/realname";
- if (path === "/login") return "/m/login";
- if (path === "/register") return "/m/register";
-
- if (path === "/seller/listings/create") {
- return "/m/seller/listings/create";
+ if (path === '/realname') return '/m/realname'
+ if (path === '/login') return '/m/login'
+ if (path === '/register') return '/m/register'
+
+ if (path === '/seller/listings/create') {
+ return '/m/seller/listings/create'
}
- if (path === "/messages" || path.startsWith("/messages/")) {
- return "/m/messages";
+ if (path === '/messages' || path.startsWith('/messages/')) {
+ return '/m/messages'
}
-
+
// PC-only views that don't have mobile counterparts go to mobile profile as fallback
- if (path === "/wallet" || path === "/notifications" || path.startsWith("/seller")) {
- return "/m/profile";
+ if (path === '/wallet' || path === '/notifications' || path.startsWith('/seller')) {
+ return '/m/profile'
}
-
- return `/m${path}`;
+
+ return `/m${path}`
}
export function getPcPath(path: string): string {
- if ((path !== "/m" && !path.startsWith("/m/")) || path.startsWith("/admin")) {
- return path;
+ if ((path !== '/m' && !path.startsWith('/m/')) || path.startsWith('/admin')) {
+ return path
}
-
- if (path === "/m" || path === "/m/") {
- return "/";
+
+ if (path === '/m' || path === '/m/') {
+ return '/'
}
-
- const subPath = path.substring(2);
-
- if (subPath === "/listings" || subPath.startsWith("/listings/")) {
- return subPath;
+
+ const subPath = path.substring(2)
+
+ if (subPath === '/listings' || subPath.startsWith('/listings/')) {
+ return subPath
}
- if (subPath === "/orders" || subPath.startsWith("/orders/")) {
- return subPath;
+ if (subPath === '/orders' || subPath.startsWith('/orders/')) {
+ return subPath
}
- if (subPath === "/login") return "/login";
- if (subPath === "/register") return "/login"; // PC uses /login for both auth actions
- if (subPath === "/realname") return "/realname";
- if (subPath === "/seller/listings/create") return "/seller/listings/create";
-
- if (subPath === "/messages") return "/messages";
- if (subPath.startsWith("/chats/")) return "/messages/" + subPath.substring(7);
- if (subPath === "/profile") return "/wallet";
-
- return subPath || "/";
+ if (subPath === '/login') return '/login'
+ if (subPath === '/register') return '/login' // PC uses /login for both auth actions
+ if (subPath === '/realname') return '/realname'
+ if (subPath === '/seller/listings/create') return '/seller/listings/create'
+
+ if (subPath === '/messages') return '/messages'
+ if (subPath.startsWith('/chats/')) return '/messages/' + subPath.substring(7)
+ if (subPath === '/profile') return '/wallet'
+
+ return subPath || '/'
}
export function toMobilePath(path: string) {
- return getMobilePath(path);
+ return getMobilePath(path)
}
diff --git a/frontend/src/shared/api/client.ts b/frontend/src/shared/api/client.ts
index 80b7741..6cfe703 100644
--- a/frontend/src/shared/api/client.ts
+++ b/frontend/src/shared/api/client.ts
@@ -37,13 +37,14 @@ function logRequest(config: InternalAxiosRequestConfig) {
function logResponse(response: AxiosResponse) {
if (import.meta.env.DEV) {
- const duration = response.config._startTime
- ? Date.now() - response.config._startTime
- : 0
- console.log(`[API Response] ${response.config.method?.toUpperCase()} ${response.config.url} (${duration}ms)`, {
- status: response.status,
- data: response.data,
- })
+ const duration = response.config._startTime ? Date.now() - response.config._startTime : 0
+ console.log(
+ `[API Response] ${response.config.method?.toUpperCase()} ${response.config.url} (${duration}ms)`,
+ {
+ status: response.status,
+ data: response.data,
+ }
+ )
}
// 记录性能指标
@@ -61,14 +62,15 @@ function logResponse(response: AxiosResponse) {
function logError(error: AxiosError) {
if (import.meta.env.DEV) {
- const duration = error.config?._startTime
- ? Date.now() - error.config._startTime
- : 0
- console.error(`[API Error] ${error.config?.method?.toUpperCase()} ${error.config?.url} (${duration}ms)`, {
- status: error.response?.status,
- message: error.message,
- data: error.response?.data,
- })
+ const duration = error.config?._startTime ? Date.now() - error.config._startTime : 0
+ console.error(
+ `[API Error] ${error.config?.method?.toUpperCase()} ${error.config?.url} (${duration}ms)`,
+ {
+ status: error.response?.status,
+ message: error.message,
+ data: error.response?.data,
+ }
+ )
}
// 记录错误指标
@@ -181,29 +183,32 @@ function isRefreshRequest(url = '') {
return url.endsWith('/auth/refresh') || url.endsWith('/admin/auth/refresh')
}
-apiClient.interceptors.request.use((config) => {
- const scope = getRequestScope(config.url || '')
- const token = getAccessToken(scope)
- if (token) {
- config.headers.Authorization = `Bearer ${token}`
+apiClient.interceptors.request.use(
+ config => {
+ const scope = getRequestScope(config.url || '')
+ const token = getAccessToken(scope)
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`
+ }
+
+ // 记录请求日志
+ logRequest(config)
+
+ return config
+ },
+ error => {
+ logError(error)
+ return Promise.reject(error)
}
-
- // 记录请求日志
- logRequest(config)
-
- return config
-}, (error) => {
- logError(error)
- return Promise.reject(error)
-})
+)
apiClient.interceptors.response.use(
- (response) => {
+ response => {
// 记录响应日志
logResponse(response)
return response
},
- async (error) => {
+ async error => {
// 记录错误日志
logError(error)
@@ -231,7 +236,7 @@ apiClient.interceptors.response.use(
if (state.refreshing) {
return new Promise((resolve, reject) => {
state.pendingRequests.push({ resolve, reject })
- }).then((newToken) => {
+ }).then(newToken => {
originalRequest._retry = true
originalRequest.headers.Authorization = `Bearer ${newToken}`
return apiClient(originalRequest)
@@ -255,5 +260,5 @@ apiClient.interceptors.response.use(
} finally {
state.refreshing = false
}
- },
+ }
)
diff --git a/frontend/src/shared/api/helpers.ts b/frontend/src/shared/api/helpers.ts
index 86cc583..fe263c5 100644
--- a/frontend/src/shared/api/helpers.ts
+++ b/frontend/src/shared/api/helpers.ts
@@ -36,11 +36,7 @@ export async function post(
/**
* PUT 请求
*/
-export async function put(
- url: string,
- data?: unknown,
- config?: AxiosRequestConfig
-): Promise {
+export async function put(url: string, data?: unknown, config?: AxiosRequestConfig): Promise {
return request({ ...config, method: 'PUT', url, data })
}
@@ -91,7 +87,7 @@ export async function requestWithRetry(
}
// 等待后重试
- await new Promise((resolve) => setTimeout(resolve, retryDelay * (attempt + 1)))
+ await new Promise(resolve => setTimeout(resolve, retryDelay * (attempt + 1)))
}
}
@@ -110,9 +106,7 @@ export async function concurrent(
/**
* 串行请求 (按顺序执行)
*/
-export async function sequential(
- requestFns: Array<() => Promise>
-): Promise {
+export async function sequential(requestFns: Array<() => Promise>): Promise {
const results: T[] = []
for (const requestFn of requestFns) {
diff --git a/frontend/src/shared/api/monitor.ts b/frontend/src/shared/api/monitor.ts
index c9a1df7..c1e8548 100644
--- a/frontend/src/shared/api/monitor.ts
+++ b/frontend/src/shared/api/monitor.ts
@@ -54,7 +54,7 @@ class ApiMonitor {
*/
getSuccessRate(): number {
if (this.metrics.length === 0) return 100
- const successCount = this.metrics.filter((m) => m.success).length
+ const successCount = this.metrics.filter(m => m.success).length
return Math.round((successCount / this.metrics.length) * 100)
}
@@ -62,9 +62,7 @@ class ApiMonitor {
* 获取最慢的请求
*/
getSlowestRequests(count = 5): RequestMetrics[] {
- return [...this.metrics]
- .sort((a, b) => b.duration - a.duration)
- .slice(0, count)
+ return [...this.metrics].sort((a, b) => b.duration - a.duration).slice(0, count)
}
/**
@@ -73,7 +71,7 @@ class ApiMonitor {
getStatsByUrl(): Record {
const urlStats: Record = {}
- this.metrics.forEach((metric) => {
+ this.metrics.forEach(metric => {
if (!urlStats[metric.url]) {
urlStats[metric.url] = []
}
@@ -84,7 +82,7 @@ class ApiMonitor {
Object.entries(urlStats).forEach(([url, metrics]) => {
const totalDuration = metrics.reduce((sum, m) => sum + m.duration, 0)
- const successCount = metrics.filter((m) => m.success).length
+ const successCount = metrics.filter(m => m.success).length
result[url] = {
count: metrics.length,
@@ -113,7 +111,7 @@ class ApiMonitor {
console.log('成功率:', this.getSuccessRate(), '%')
console.log('最慢的 5 个请求:')
console.table(
- this.getSlowestRequests(5).map((m) => ({
+ this.getSlowestRequests(5).map(m => ({
方法: m.method,
URL: m.url,
耗时: `${m.duration}ms`,
diff --git a/frontend/src/shared/components/business/ChatAttachmentImage.vue b/frontend/src/shared/components/business/ChatAttachmentImage.vue
index 7df66ed..63de32b 100644
--- a/frontend/src/shared/components/business/ChatAttachmentImage.vue
+++ b/frontend/src/shared/components/business/ChatAttachmentImage.vue
@@ -34,9 +34,8 @@ async function loadImage() {
}
try {
const key = extractObjectKey(props.source)
- const blob = props.admin && key
- ? await fetchAdminFileBlob(key)
- : await fetchFileBlobByURL(props.source)
+ const blob =
+ props.admin && key ? await fetchAdminFileBlob(key) : await fetchFileBlobByURL(props.source)
objectURL.value = URL.createObjectURL(blob)
} catch {
failed.value = true
@@ -55,7 +54,7 @@ onBeforeUnmount(revokeCurrentURL)
-
+
{{ failed ? '图片加载失败' : '图片加载中' }}
diff --git a/frontend/src/shared/components/layout/MobileBottomNav.vue b/frontend/src/shared/components/layout/MobileBottomNav.vue
index 47d9f44..e4cf7b8 100644
--- a/frontend/src/shared/components/layout/MobileBottomNav.vue
+++ b/frontend/src/shared/components/layout/MobileBottomNav.vue
@@ -11,19 +11,11 @@ function isNavActive(path: string) {