前端死代码清理

This commit is contained in:
yml
2026-06-14 18:42:06 +08:00
parent 965832c3cb
commit 2dc5a8121e
18 changed files with 0 additions and 4864 deletions
@@ -1,89 +0,0 @@
<script setup lang="ts">
import { onBeforeUnmount, ref, watch } from 'vue'
import { fetchAdminFileBlob, fetchFileBlobByURL } from '@/shared/api/files'
const props = defineProps<{
source: string
admin?: boolean
}>()
const objectURL = ref('')
const failed = ref(false)
function extractObjectKey(value: string) {
try {
const parsed = new URL(value, window.location.origin)
return parsed.searchParams.get('key') || ''
} catch {
return ''
}
}
function revokeCurrentURL() {
if (!objectURL.value) return
URL.revokeObjectURL(objectURL.value)
objectURL.value = ''
}
async function loadImage() {
revokeCurrentURL()
failed.value = false
if (!props.source) {
failed.value = true
return
}
try {
const key = extractObjectKey(props.source)
const blob =
props.admin && key ? await fetchAdminFileBlob(key) : await fetchFileBlobByURL(props.source)
objectURL.value = URL.createObjectURL(blob)
} catch {
failed.value = true
}
}
function openImage() {
if (!objectURL.value) return
window.open(objectURL.value, '_blank')
}
watch(() => [props.source, props.admin] as const, loadImage, { immediate: true })
onBeforeUnmount(revokeCurrentURL)
</script>
<template>
<button v-if="objectURL" class="chat-image-button" type="button" @click="openImage">
<img :src="objectURL" alt="聊天图片" loading="lazy" decoding="async" />
</button>
<span v-else class="chat-image-fallback">{{ failed ? '图片加载失败' : '图片加载中' }}</span>
</template>
<style scoped>
.chat-image-button {
display: block;
max-width: 220px;
padding: 0;
overflow: hidden;
border: 0;
border-radius: 8px;
background: transparent;
cursor: zoom-in;
}
.chat-image-button img {
display: block;
width: 100%;
max-height: 260px;
object-fit: cover;
}
.chat-image-fallback {
display: inline-block;
padding: 8px 10px;
border-radius: 8px;
background: #eef2f7;
color: #6b7280;
font-size: 12px;
}
</style>
File diff suppressed because it is too large Load Diff
@@ -1,242 +0,0 @@
import { computed, ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import type { Order, HandoffRecord } from '../api/orders'
import {
fetchOrder,
fetchHandoffRecords,
cancelOrder,
startOrderPayment,
submitHandoff,
confirmReceive,
} from '../api/orders'
import { createDispute } from '@/features/disputes/api/disputes'
import { uploadFile } from '@/shared/api/files'
import { fetchOrderChat } from '@/features/chats/api/chats'
import { useSessionStore } from '@/stores/session'
import { usePaymentPolling } from './usePaymentPolling'
import { useSettlement } from './useSettlement'
/**
* 订单详情 Composable
* 负责订单的核心流程:加载、取消、支付、交接、收货
* 结算和支付轮询逻辑已拆分到独立 composables
*/
export function useOrderDetail() {
const route = useRoute()
const session = useSessionStore()
// Loading states
const loading = ref(false)
const cancelling = ref(false)
const startingPayment = ref(false)
const handoffing = ref(false)
const confirming = ref(false)
const disputing = ref(false)
const uploadingEvidence = ref(false)
const openingChat = ref(false)
// Data
const order = ref<Order | null>(null)
const handoffRecords = ref<HandoffRecord[]>([])
const handoffContent = ref('')
// Dispute form
const disputeType = ref('cannot_login')
const disputeDescription = ref('')
const disputeEvidenceText = ref('')
// 使用拆分的 composables
const paymentPolling = usePaymentPolling()
const settlement = useSettlement(order)
// Computed
const isOwner = computed(() => order.value?.owner_id === session.userId)
const isRenter = computed(() => order.value?.renter_id === session.userId)
const orderAmountLabel = computed(() => (isOwner.value ? '我的租金' : '订单金额'))
const canOpenDispute = computed(() => {
if (!order.value || (!isOwner.value && !isRenter.value)) return false
return ![
'completed',
'cancelled',
'closed',
'disputing',
'checkout_disputing',
'abnormal',
].includes(order.value.status)
})
const isCheckoutDisputeStage = computed(() => {
return (
!!order.value &&
['pending_checkout_confirm', 'pending_checkout_accept'].includes(order.value.status)
)
})
// Methods
async function loadOrder() {
loading.value = true
try {
order.value = await fetchOrder(String(route.params.id))
handoffRecords.value = await fetchHandoffRecords(String(route.params.id))
return order.value
} finally {
loading.value = false
}
}
async function handleCancel() {
if (!order.value) return false
cancelling.value = true
try {
await cancelOrder(order.value.id)
await loadOrder()
return true
} finally {
cancelling.value = false
}
}
async function handlePay() {
if (!order.value) return null
startingPayment.value = true
try {
const payment = await startOrderPayment(order.value.id)
// 开始支付轮询,成功后重新加载订单
paymentPolling.startPaymentPolling(payment, loadOrder)
return payment
} finally {
startingPayment.value = false
}
}
async function handleSubmitHandoff() {
if (!order.value || !handoffContent.value.trim()) return false
handoffing.value = true
try {
await submitHandoff(order.value.id, handoffContent.value.trim())
handoffContent.value = ''
await loadOrder()
return true
} finally {
handoffing.value = false
}
}
async function handleConfirmReceive() {
if (!order.value) return false
confirming.value = true
try {
await confirmReceive(order.value.id)
await loadOrder()
return true
} finally {
confirming.value = false
}
}
async function handleCreateDispute() {
if (!order.value) return false
disputing.value = true
try {
await createDispute(order.value.id, {
type: disputeType.value,
description: disputeDescription.value.trim(),
evidence_urls: linesToList(disputeEvidenceText.value),
})
await loadOrder()
return true
} finally {
disputing.value = false
}
}
async function handleUploadEvidence(file: File) {
uploadingEvidence.value = true
try {
const result = await uploadFile(file, 'evidence')
return result.url
} finally {
uploadingEvidence.value = false
}
}
async function handleOpenChat() {
if (!order.value) return null
openingChat.value = true
try {
const chat = await fetchOrderChat(order.value.id)
return chat
} finally {
openingChat.value = false
}
}
function getOrderStep(status: string) {
const stepMap: Record<string, number> = {
pending_payment: 0,
pending_handoff: 1,
renting: 2,
overdue: 2,
pending_checkout_confirm: 3,
pending_checkout_accept: 3,
completed: 5,
cancelled: 0,
closed: 4,
}
return stepMap[status] ?? 0
}
function linesToList(value: string) {
return value
.split('\n')
.map(line => line.trim())
.filter(Boolean)
}
onMounted(loadOrder)
return {
// States
loading,
cancelling,
startingPayment,
handoffing,
confirming,
disputing,
uploadingEvidence,
openingChat,
// Data
order,
handoffRecords,
handoffContent,
// Dispute form
disputeType,
disputeDescription,
disputeEvidenceText,
// Computed
isOwner,
isRenter,
orderAmountLabel,
canOpenDispute,
isCheckoutDisputeStage,
// Methods
loadOrder,
handleCancel,
handlePay,
handleSubmitHandoff,
handleConfirmReceive,
handleCreateDispute,
handleUploadEvidence,
handleOpenChat,
getOrderStep,
// 从拆分的 composables 导出
...paymentPolling,
...settlement,
}
}
@@ -1,65 +0,0 @@
import { ref, onBeforeUnmount } from 'vue'
import { queryOrderPayment, type PaymentOrder } from '../api/orders'
/**
* 支付轮询 Composable
* 负责轮询查询支付状态,直到支付完成
*/
export function usePaymentPolling() {
const activePayment = ref<PaymentOrder | null>(null)
const checkingPayment = ref(false)
let paymentPollingTimer: number | undefined
/**
* 开始轮询支付状态
*/
function startPaymentPolling(payment: PaymentOrder, onSuccess?: () => void) {
activePayment.value = payment
stopPaymentPolling()
paymentPollingTimer = window.setInterval(() => checkPaymentStatus(onSuccess), 2000)
}
/**
* 停止轮询
*/
function stopPaymentPolling() {
if (paymentPollingTimer !== undefined) {
clearInterval(paymentPollingTimer)
paymentPollingTimer = undefined
}
activePayment.value = null
}
/**
* 检查支付状态
*/
async function checkPaymentStatus(onSuccess?: () => void) {
if (!activePayment.value || checkingPayment.value) return false
checkingPayment.value = true
try {
const updated = await queryOrderPayment(activePayment.value.order_id)
if (updated.paid) {
stopPaymentPolling()
onSuccess?.()
return true
}
} catch {
// 忽略轮询错误,继续下次轮询
} finally {
checkingPayment.value = false
}
return false
}
// 组件卸载时停止轮询
onBeforeUnmount(stopPaymentPolling)
return {
activePayment,
checkingPayment,
startPaymentPolling,
stopPaymentPolling,
checkPaymentStatus,
}
}
@@ -1,178 +0,0 @@
import { ref, type Ref } from 'vue'
import { submitCheckout, acceptCheckout, counterCheckout, confirmCheckout } from '../api/orders'
import type { Order } from '../api/orders'
export interface CheckoutForm {
content: string
consumableAmountYuan: number
coin_consumed_m: number
otherAmountYuan: number
evidenceText: string
}
export interface CounterForm {
consumableAmountYuan: number
coin_consumed_m: number
otherAmountYuan: number
depositDeductAmountYuan: number
reason: string
evidenceText: string
}
/**
* 订单结算 Composable
* 负责处理订单结算流程:提交结算、接受结算、反驳结算、确认结算
*/
export function useSettlement(order: Ref<Order | null>) {
const returning = ref(false)
const acceptingCheckout = ref(false)
const countering = ref(false)
const rejectingCheckout = ref(false)
const completing = ref(false)
const checkoutForm = ref<CheckoutForm>({
content: '',
consumableAmountYuan: 0,
coin_consumed_m: 0,
otherAmountYuan: 0,
evidenceText: '',
})
const counterForm = ref<CounterForm>({
consumableAmountYuan: 0,
coin_consumed_m: 0,
otherAmountYuan: 0,
depositDeductAmountYuan: 0,
reason: '',
evidenceText: '',
})
const resourceUsage = ref<Record<string, number>>({})
/**
* 提交结算单
*/
async function handleSubmitCheckout(onSuccess?: () => void) {
if (!order.value) return false
returning.value = true
try {
await submitCheckout(order.value.id, {
content: checkoutForm.value.content.trim(),
consumableAmountYuan: checkoutForm.value.consumableAmountYuan,
coin_consumed_m: checkoutForm.value.coin_consumed_m,
otherAmountYuan: checkoutForm.value.otherAmountYuan,
evidence_urls: linesToList(checkoutForm.value.evidenceText),
})
onSuccess?.()
return true
} finally {
returning.value = false
}
}
/**
* 接受对方的结算
*/
async function handleAcceptCheckout(onSuccess?: () => void) {
if (!order.value) return false
acceptingCheckout.value = true
try {
await acceptCheckout(order.value.id)
onSuccess?.()
return true
} finally {
acceptingCheckout.value = false
}
}
/**
* 反驳对方的结算
*/
async function handleCounterCheckout(onSuccess?: () => void) {
if (!order.value) return false
countering.value = true
try {
const reasonText = counterForm.value.reason.trim()
await counterCheckout(order.value.id, {
content: reasonText,
consumableAmountYuan: counterForm.value.consumableAmountYuan,
coin_consumed_m: counterForm.value.coin_consumed_m,
otherAmountYuan: counterForm.value.otherAmountYuan,
depositDeductAmountYuan: counterForm.value.depositDeductAmountYuan,
reason: reasonText,
evidence_urls: linesToList(counterForm.value.evidenceText),
})
onSuccess?.()
return true
} finally {
countering.value = false
}
}
/**
* 拒绝对方的结算(简化版反驳)
*/
async function handleRejectCheckout(reason: string, onSuccess?: () => void) {
if (!order.value) return false
rejectingCheckout.value = true
try {
const reasonText = reason.trim()
await counterCheckout(order.value.id, {
content: reasonText,
consumableAmountYuan: 0,
coin_consumed_m: 0,
otherAmountYuan: 0,
depositDeductAmountYuan: 0,
reason: reasonText,
evidence_urls: [],
})
onSuccess?.()
return true
} finally {
rejectingCheckout.value = false
}
}
/**
* 确认最终结算
*/
async function handleConfirmCheckout(onSuccess?: () => void) {
if (!order.value) return false
completing.value = true
try {
await confirmCheckout(order.value.id)
onSuccess?.()
return true
} finally {
completing.value = false
}
}
function linesToList(value: string) {
return value
.split('\n')
.map(line => line.trim())
.filter(Boolean)
}
return {
// Loading states
returning,
acceptingCheckout,
countering,
rejectingCheckout,
completing,
// Forms
checkoutForm,
counterForm,
resourceUsage,
// Methods
handleSubmitCheckout,
handleAcceptCheckout,
handleCounterCheckout,
handleRejectCheckout,
handleConfirmCheckout,
}
}
-3
View File
@@ -1,9 +1,6 @@
// Orders 模块统一导出
export * from './api/orders'
export * from './composables/useOrderDetail'
export * from './composables/useOrderSnapshot'
export * from './composables/usePaymentPolling'
export * from './composables/useSettlement'
export { default as OrderCheckoutSummary } from './components/OrderCheckoutSummary.vue'
export { default as OrderHandoffTimeline } from './components/OrderHandoffTimeline.vue'
export { default as OrderResourceUsageEditor } from './components/OrderResourceUsageEditor.vue'
@@ -1,54 +0,0 @@
import { ref, computed } from 'vue'
import { fetchWalletBalance, fetchWalletLedger } from '../api/wallet'
import type { WalletAccount, WalletLedger } from '../api/wallet'
export function useWallet() {
const balance = ref<WalletAccount | null>(null)
const ledgers = ref<WalletLedger[]>([])
const loading = ref(false)
const error = ref<string | null>(null)
const availableBalance = computed(() => balance.value?.available_balance_cent ?? 0)
const frozenBalance = computed(() => balance.value?.frozen_balance_cent ?? 0)
const totalBalance = computed(() => availableBalance.value + frozenBalance.value)
async function loadBalance() {
loading.value = true
error.value = null
try {
balance.value = await fetchWalletBalance()
} catch (err) {
error.value = err instanceof Error ? err.message : '加载余额失败'
throw err
} finally {
loading.value = false
}
}
async function loadLedger(page = 1, pageSize = 20) {
loading.value = true
error.value = null
try {
const result = await fetchWalletLedger(page, pageSize)
ledgers.value = result.items
return result
} catch (err) {
error.value = err instanceof Error ? err.message : '加载账单失败'
throw err
} finally {
loading.value = false
}
}
return {
balance,
ledgers,
loading,
error,
availableBalance,
frozenBalance,
totalBalance,
loadBalance,
loadLedger,
}
}
-1
View File
@@ -1,4 +1,3 @@
// Wallet 模块统一导出
export * from './api/wallet'
export * from './api/withdrawal'
export * from './composables/useWallet'