[Snow Team] frontend-fixer-2: auto-commit work
This commit is contained in:
@@ -12,8 +12,8 @@ export interface AdminWalletLedger {
|
|||||||
order_id?: number
|
order_id?: number
|
||||||
order_no: string
|
order_no: string
|
||||||
direction: LedgerDirection
|
direction: LedgerDirection
|
||||||
amount: number
|
amount_cent: number
|
||||||
balance_after: number
|
balance_after_cent: number
|
||||||
balance_type: BalanceType
|
balance_type: BalanceType
|
||||||
biz_type: string
|
biz_type: string
|
||||||
biz_no: string
|
biz_no: string
|
||||||
|
|||||||
@@ -9,9 +9,12 @@ export interface WithdrawalDetail {
|
|||||||
user_id: number
|
user_id: number
|
||||||
user_nickname: string
|
user_nickname: string
|
||||||
user_phone: string
|
user_phone: string
|
||||||
amount: number
|
amount_cent: number
|
||||||
fee: number
|
fee_cent: number
|
||||||
actual_amount: number
|
actual_amount_cent: number
|
||||||
|
amount?: number
|
||||||
|
fee?: number
|
||||||
|
actual_amount?: number
|
||||||
payment_account_id: number | null
|
payment_account_id: number | null
|
||||||
account_type: string
|
account_type: string
|
||||||
account_name: string
|
account_name: string
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { ref } from 'vue'
|
|||||||
|
|
||||||
import type { WithdrawalDetail } from '@/features/admin/api/adminWithdrawal'
|
import type { WithdrawalDetail } from '@/features/admin/api/adminWithdrawal'
|
||||||
import { reviewWithdrawal, confirmPayment } from '@/features/admin/api/adminWithdrawal'
|
import { reviewWithdrawal, confirmPayment } from '@/features/admin/api/adminWithdrawal'
|
||||||
import { formatMoney } from '@/shared/utils/money'
|
import { formatCent } from '@/shared/utils/money'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: boolean
|
modelValue: boolean
|
||||||
@@ -161,15 +161,15 @@ function accountTypeLabel(type: string) {
|
|||||||
<el-descriptions :column="2" border>
|
<el-descriptions :column="2" border>
|
||||||
<el-descriptions-item label="提现金额">
|
<el-descriptions-item label="提现金额">
|
||||||
<span style="color: #f56c6c; font-weight: 600; font-size: 16px">
|
<span style="color: #f56c6c; font-weight: 600; font-size: 16px">
|
||||||
¥{{ formatMoney(withdrawal.amount) }}
|
¥{{ formatCent(withdrawal.amount_cent) }}
|
||||||
</span>
|
</span>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="手续费">
|
<el-descriptions-item label="手续费">
|
||||||
¥{{ formatMoney(withdrawal.fee) }}
|
¥{{ formatCent(withdrawal.fee_cent) }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="实际到账" :span="2">
|
<el-descriptions-item label="实际到账" :span="2">
|
||||||
<span style="color: #67c23a; font-weight: 600; font-size: 16px">
|
<span style="color: #67c23a; font-weight: 600; font-size: 16px">
|
||||||
¥{{ formatMoney(withdrawal.actual_amount) }}
|
¥{{ formatCent(withdrawal.actual_amount_cent) }}
|
||||||
</span>
|
</span>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
@@ -274,7 +274,7 @@ function accountTypeLabel(type: string) {
|
|||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<div style="font-size: 13px">
|
<div style="font-size: 13px">
|
||||||
请手动转账 <strong>¥{{ formatMoney(withdrawal.actual_amount) }}</strong> 到用户收款账号,
|
请手动转账 <strong>¥{{ formatCent(withdrawal.actual_amount_cent) }}</strong> 到用户收款账号,
|
||||||
完成后点击"确认打款"按钮。
|
完成后点击"确认打款"按钮。
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
type RefundStatus,
|
type RefundStatus,
|
||||||
} from '@/features/orders'
|
} from '@/features/orders'
|
||||||
import { fetchAdminPayments, type AdminPayment } from '@/features/admin/api/adminPayments'
|
import { fetchAdminPayments, type AdminPayment } from '@/features/admin/api/adminPayments'
|
||||||
import { formatMoney, formatMoneyWithSymbol } from '@/shared/utils/money'
|
import { centToYuan, formatMoney, formatMoneyWithSymbol } from '@/shared/utils/money'
|
||||||
import { handoffStatusLabel, orderStatusLabel } from '@/utils/statusLabels'
|
import { handoffStatusLabel, orderStatusLabel } from '@/utils/statusLabels'
|
||||||
import { formatDateTime } from '@/utils/time'
|
import { formatDateTime } from '@/utils/time'
|
||||||
import { formatListingNo } from '@/utils/listingDisplay'
|
import { formatListingNo } from '@/utils/listingDisplay'
|
||||||
@@ -111,6 +111,11 @@ function money(value: unknown) {
|
|||||||
return formatMoney(Number(value || 0))
|
return formatMoney(Number(value || 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function amountYuan(cent: unknown, legacyYuan?: unknown) {
|
||||||
|
if (cent !== undefined && cent !== null) return centToYuan(Number(cent || 0))
|
||||||
|
return Number(legacyYuan || 0)
|
||||||
|
}
|
||||||
|
|
||||||
async function handleRefund() {
|
async function handleRefund() {
|
||||||
if (!order.value) return
|
if (!order.value) return
|
||||||
refunding.value = true
|
refunding.value = true
|
||||||
@@ -227,15 +232,15 @@ function formatHandoffRecordType(type: string) {
|
|||||||
</div>
|
</div>
|
||||||
<div class="metric-card">
|
<div class="metric-card">
|
||||||
<span>订单金额</span>
|
<span>订单金额</span>
|
||||||
<strong>¥{{ money(order.rent_amount) }}</strong>
|
<strong>¥{{ money(amountYuan(order.rent_amount_cent, order.rent_amount)) }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="metric-card">
|
<div class="metric-card">
|
||||||
<span>平台费用</span>
|
<span>平台费用</span>
|
||||||
<strong>¥{{ money(order.platform_fee) }}</strong>
|
<strong>¥{{ money(amountYuan(order.platform_fee_cent, order.platform_fee)) }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="metric-card">
|
<div class="metric-card">
|
||||||
<span>押金</span>
|
<span>押金</span>
|
||||||
<strong>¥{{ money(order.deposit_amount) }}</strong>
|
<strong>¥{{ money(amountYuan(order.deposit_amount_cent, order.deposit_amount)) }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="refundStatus" class="metric-card">
|
<div v-if="refundStatus" class="metric-card">
|
||||||
<span>退款状态</span>
|
<span>退款状态</span>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { computed, onMounted, ref } from 'vue'
|
|||||||
import { fetchAdminOrders, type Order } from '@/features/orders'
|
import { fetchAdminOrders, type Order } from '@/features/orders'
|
||||||
import { handoffStatusLabel, orderStatusLabel, settlementStatusLabel } from '@/utils/statusLabels'
|
import { handoffStatusLabel, orderStatusLabel, settlementStatusLabel } from '@/utils/statusLabels'
|
||||||
import { formatDateTime } from '@/utils/time'
|
import { formatDateTime } from '@/utils/time'
|
||||||
|
import { centToYuan, formatMoney } from '@/shared/utils/money'
|
||||||
import { formatListingNo } from '@/utils/listingDisplay'
|
import { formatListingNo } from '@/utils/listingDisplay'
|
||||||
import AdminTablePagination from '../components/AdminTablePagination.vue'
|
import AdminTablePagination from '../components/AdminTablePagination.vue'
|
||||||
|
|
||||||
@@ -35,6 +36,15 @@ const filteredOrders = computed(() => {
|
|||||||
if (!status.value) return orders.value
|
if (!status.value) return orders.value
|
||||||
return orders.value.filter(item => item.status === status.value)
|
return orders.value.filter(item => item.status === status.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function amountYuan(cent: unknown, legacyYuan?: unknown) {
|
||||||
|
if (cent !== undefined && cent !== null) return centToYuan(Number(cent || 0))
|
||||||
|
return Number(legacyYuan || 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function money(value: unknown) {
|
||||||
|
return formatMoney(Number(value || 0))
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -84,8 +94,12 @@ const filteredOrders = computed(() => {
|
|||||||
<el-table-column label="结算" width="120">
|
<el-table-column label="结算" width="120">
|
||||||
<template #default="{ row }">{{ settlementStatusLabel(row.settlement_status) }}</template>
|
<template #default="{ row }">{{ settlementStatusLabel(row.settlement_status) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="rent_amount" label="订单金额" width="100" />
|
<el-table-column label="订单金额" width="100">
|
||||||
<el-table-column prop="deposit_amount" label="押金" width="100" />
|
<template #default="{ row }">¥{{ money(amountYuan(row.rent_amount_cent, row.rent_amount)) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="押金" width="100">
|
||||||
|
<template #default="{ row }">¥{{ money(amountYuan(row.deposit_amount_cent, row.deposit_amount)) }}</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="创建时间" min-width="180">
|
<el-table-column label="创建时间" min-width="180">
|
||||||
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
|
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Search } from '@element-plus/icons-vue'
|
|||||||
import { computed, onMounted, reactive, ref } from 'vue'
|
import { computed, onMounted, reactive, ref } from 'vue'
|
||||||
|
|
||||||
import { fetchAdminWalletLedger, type AdminWalletLedger } from '@/features/admin/api/adminWallet'
|
import { fetchAdminWalletLedger, type AdminWalletLedger } from '@/features/admin/api/adminWallet'
|
||||||
import { formatMoneyWithSymbol } from '@/shared/utils/money'
|
import { formatCentWithSymbol } from '@/shared/utils/money'
|
||||||
import { balanceTypeLabel, ledgerDirectionLabel } from '@/utils/statusLabels'
|
import { balanceTypeLabel, ledgerDirectionLabel } from '@/utils/statusLabels'
|
||||||
import { formatDateTime } from '@/utils/time'
|
import { formatDateTime } from '@/utils/time'
|
||||||
import AdminTablePagination from '../components/AdminTablePagination.vue'
|
import AdminTablePagination from '../components/AdminTablePagination.vue'
|
||||||
@@ -22,12 +22,12 @@ const filters = reactive({
|
|||||||
const inAmount = computed(() =>
|
const inAmount = computed(() =>
|
||||||
ledger.value
|
ledger.value
|
||||||
.filter(item => item.direction === 'in')
|
.filter(item => item.direction === 'in')
|
||||||
.reduce((sum, item) => sum + Number(item.amount || 0), 0)
|
.reduce((sum, item) => sum + Number(item.amount_cent || 0), 0)
|
||||||
)
|
)
|
||||||
const outAmount = computed(() =>
|
const outAmount = computed(() =>
|
||||||
ledger.value
|
ledger.value
|
||||||
.filter(item => item.direction === 'out')
|
.filter(item => item.direction === 'out')
|
||||||
.reduce((sum, item) => sum + Number(item.amount || 0), 0)
|
.reduce((sum, item) => sum + Number(item.amount_cent || 0), 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
onMounted(loadLedger)
|
onMounted(loadLedger)
|
||||||
@@ -65,7 +65,7 @@ async function handlePageChange() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function money(value: number) {
|
function money(value: number) {
|
||||||
return formatMoneyWithSymbol(value)
|
return formatCentWithSymbol(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
function directionType(direction: string) {
|
function directionType(direction: string) {
|
||||||
@@ -149,13 +149,13 @@ function directionLabel(direction: string) {
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="金额" width="110">
|
<el-table-column label="金额" width="110">
|
||||||
<template #default="{ row }">{{ money(Number(row.amount)) }}</template>
|
<template #default="{ row }">{{ money(Number(row.amount_cent)) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="余额类型" width="110">
|
<el-table-column label="余额类型" width="110">
|
||||||
<template #default="{ row }">{{ balanceTypeLabel(row.balance_type) }}</template>
|
<template #default="{ row }">{{ balanceTypeLabel(row.balance_type) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="变化后余额" width="130">
|
<el-table-column label="变化后余额" width="130">
|
||||||
<template #default="{ row }">{{ money(Number(row.balance_after)) }}</template>
|
<template #default="{ row }">{{ money(Number(row.balance_after_cent)) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="创建时间" min-width="180">
|
<el-table-column label="创建时间" min-width="180">
|
||||||
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
|
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
confirmPayment,
|
confirmPayment,
|
||||||
type WithdrawalDetail,
|
type WithdrawalDetail,
|
||||||
} from '@/features/admin/api/adminWithdrawal'
|
} from '@/features/admin/api/adminWithdrawal'
|
||||||
import { formatMoney } from '@/shared/utils/money'
|
import { formatCent } from '@/shared/utils/money'
|
||||||
|
|
||||||
import WithdrawalDetailDialog from '../components/WithdrawalDetailDialog.vue'
|
import WithdrawalDetailDialog from '../components/WithdrawalDetailDialog.vue'
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ function onDetailDialogSaved() {
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="提现金额" width="120" align="right">
|
<el-table-column label="提现金额" width="120" align="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span style="color: #f56c6c; font-weight: 600"> ¥{{ formatMoney(row.amount) }} </span>
|
<span style="color: #f56c6c; font-weight: 600"> ¥{{ formatCent(row.amount_cent) }} </span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="收款方式" min-width="180">
|
<el-table-column label="收款方式" min-width="180">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
import { fetchPostRentalNotice, type PostRentalNotice } from '@/features/orders/api/orders'
|
import { fetchPostRentalNotice, type PostRentalNotice } from '@/features/orders/api/orders'
|
||||||
import { formatDateMinute } from '@/utils/time'
|
import { formatDateMinute } from '@/utils/time'
|
||||||
import { uploadFile } from '@/shared/api/files'
|
import { uploadFile } from '@/shared/api/files'
|
||||||
import { formatMoney } from '@/shared/utils/money'
|
import { formatCent, formatMoney } from '@/shared/utils/money'
|
||||||
|
|
||||||
const session = useSessionStore()
|
const session = useSessionStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -101,7 +101,7 @@ onMounted(() => {
|
|||||||
async function loadBalance() {
|
async function loadBalance() {
|
||||||
try {
|
try {
|
||||||
const wallet = await fetchWalletBalance()
|
const wallet = await fetchWalletBalance()
|
||||||
balance.value = wallet.available_balance
|
balance.value = wallet.available_balance_cent
|
||||||
} catch {
|
} catch {
|
||||||
// 失败静默处理,显示为默认0
|
// 失败静默处理,显示为默认0
|
||||||
}
|
}
|
||||||
@@ -418,7 +418,7 @@ function resolveAvatarURL(url: string | undefined | null) {
|
|||||||
<span class="ledger-time">{{ formatDateMinute(item.created_at) }}</span>
|
<span class="ledger-time">{{ formatDateMinute(item.created_at) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="ledger-right" :class="item.direction === 'in' ? 'in-color' : 'out-color'">
|
<div class="ledger-right" :class="item.direction === 'in' ? 'in-color' : 'out-color'">
|
||||||
{{ item.direction === 'in' ? '+' : '-' }}¥{{ formatMoney(item.amount) }}
|
{{ item.direction === 'in' ? '+' : '-' }}¥{{ formatCent(item.amount_cent) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { apiClient } from '@/shared/api/client'
|
import { apiClient } from '@/shared/api/client'
|
||||||
import type { ApiResponse } from '@/shared/types/types'
|
import type { ApiResponse, PaginatedResult } from '@/shared/types/types'
|
||||||
import type { HandoffStatus, OrderStatus, SettlementStatus } from '@/shared/types/status'
|
import type { HandoffStatus, OrderStatus, SettlementStatus } from '@/shared/types/status'
|
||||||
|
import { yuanToCent } from '@/shared/utils/money'
|
||||||
|
|
||||||
export interface Order {
|
export interface Order {
|
||||||
id: number
|
id: number
|
||||||
@@ -25,6 +26,14 @@ export interface Order {
|
|||||||
deposit_original_amount_cent: number
|
deposit_original_amount_cent: number
|
||||||
deposit_waived_amount_cent: number
|
deposit_waived_amount_cent: number
|
||||||
platform_fee_cent?: number
|
platform_fee_cent?: number
|
||||||
|
// Legacy yuan fields may be present from cached/older API payloads during rollout.
|
||||||
|
display_amount?: number
|
||||||
|
rent_amount?: number
|
||||||
|
owner_rent_amount?: number
|
||||||
|
deposit_amount?: number
|
||||||
|
deposit_original_amount?: number
|
||||||
|
deposit_waived_amount?: number
|
||||||
|
platform_fee?: number
|
||||||
account_snapshot?: Record<string, unknown>
|
account_snapshot?: Record<string, unknown>
|
||||||
listing_snapshot?: string
|
listing_snapshot?: string
|
||||||
checkout_info?: string
|
checkout_info?: string
|
||||||
@@ -55,6 +64,16 @@ export interface Checkout {
|
|||||||
deposit_deduct_amount_cent: number
|
deposit_deduct_amount_cent: number
|
||||||
renter_refund_amount_cent?: number
|
renter_refund_amount_cent?: number
|
||||||
owner_income_amount_cent?: number
|
owner_income_amount_cent?: number
|
||||||
|
display_amount?: number
|
||||||
|
rent_amount?: number
|
||||||
|
owner_rent_amount?: number
|
||||||
|
platform_fee?: number
|
||||||
|
deposit_amount?: number
|
||||||
|
consumable_amount?: number
|
||||||
|
other_amount?: number
|
||||||
|
deposit_deduct_amount?: number
|
||||||
|
renter_refund_amount?: number
|
||||||
|
owner_income_amount?: number
|
||||||
content: string
|
content: string
|
||||||
evidence_urls: string[]
|
evidence_urls: string[]
|
||||||
owner_adjustment_reason: string
|
owner_adjustment_reason: string
|
||||||
@@ -98,13 +117,77 @@ export interface PaymentOrder {
|
|||||||
updated_at: string
|
updated_at: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// API functions below remain the same, TypeScript will catch any mismatches
|
export interface OrderAgreements {
|
||||||
|
virtual_asset_purchase: { title: string; content: string }
|
||||||
|
renter_agreement: { title: string; content: string }
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PostRentalNotice {
|
||||||
|
title: string
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SubmitCheckoutPayload {
|
||||||
|
content: string
|
||||||
|
consumable_amount?: number
|
||||||
|
coin_consumed_m: number
|
||||||
|
other_amount?: number
|
||||||
|
deposit_deduct_amount?: number
|
||||||
|
evidence_urls: string[]
|
||||||
|
reason?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SubmitCheckoutRequest {
|
||||||
|
content: string
|
||||||
|
consumable_amount_cent?: number
|
||||||
|
coin_consumed_m: number
|
||||||
|
other_amount_cent?: number
|
||||||
|
deposit_deduct_amount_cent?: number
|
||||||
|
evidence_urls: string[]
|
||||||
|
reason?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StartOrderPaymentRequest {
|
||||||
|
pay_way?: string
|
||||||
|
jspay_flag?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RefundStatus {
|
||||||
|
order_id: number
|
||||||
|
refund_status: string
|
||||||
|
refund_amount_cent: number
|
||||||
|
refund_no?: string
|
||||||
|
refunded_at?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function toCheckoutRequest(payload: SubmitCheckoutPayload): SubmitCheckoutRequest {
|
||||||
|
return {
|
||||||
|
content: payload.content,
|
||||||
|
consumable_amount_cent: yuanToCent(payload.consumable_amount),
|
||||||
|
coin_consumed_m: payload.coin_consumed_m,
|
||||||
|
other_amount_cent: yuanToCent(payload.other_amount),
|
||||||
|
deposit_deduct_amount_cent: yuanToCent(payload.deposit_deduct_amount),
|
||||||
|
evidence_urls: payload.evidence_urls,
|
||||||
|
reason: payload.reason,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchOrderAgreements() {
|
||||||
|
const { data } = await apiClient.get<ApiResponse<OrderAgreements>>('/order-agreements')
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchPostRentalNotice() {
|
||||||
|
const { data } = await apiClient.get<ApiResponse<PostRentalNotice>>('/post-rental-notice')
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchOrders() {
|
export async function fetchOrders() {
|
||||||
const { data } = await apiClient.get<ApiResponse<Order[]>>('/orders')
|
const { data } = await apiClient.get<ApiResponse<Order[]>>('/orders')
|
||||||
return data.data
|
return data.data
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchOrder(id: number) {
|
export async function fetchOrder(id: string | number) {
|
||||||
const { data } = await apiClient.get<ApiResponse<Order>>(`/orders/${id}`)
|
const { data } = await apiClient.get<ApiResponse<Order>>(`/orders/${id}`)
|
||||||
return data.data
|
return data.data
|
||||||
}
|
}
|
||||||
@@ -119,16 +202,8 @@ export async function cancelOrder(id: number) {
|
|||||||
return data.data
|
return data.data
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function startPayment(orderId: number, payWay: string, jsPayFlag?: string) {
|
export async function submitHandoff(id: number, content: string) {
|
||||||
const { data } = await apiClient.post<ApiResponse<PaymentOrder>>(`/orders/${orderId}/pay`, {
|
const { data } = await apiClient.post<ApiResponse<HandoffRecord>>(`/orders/${id}/handoffs`, { content })
|
||||||
pay_way: payWay,
|
|
||||||
jspay_flag: jsPayFlag || '',
|
|
||||||
})
|
|
||||||
return data.data
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function queryPayment(orderId: number) {
|
|
||||||
const { data } = await apiClient.post<ApiResponse<PaymentOrder>>(`/orders/${orderId}/pay/query`)
|
|
||||||
return data.data
|
return data.data
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,32 +215,108 @@ export async function confirmHandoff(orderId: number, type: string, content: str
|
|||||||
return data.data
|
return data.data
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createCheckout(
|
export async function fetchHandoffRecords(id: string | number) {
|
||||||
orderId: number,
|
const { data } = await apiClient.get<ApiResponse<HandoffRecord[]>>(`/orders/${id}/handoffs`)
|
||||||
payload: {
|
|
||||||
consumable_amount: number
|
|
||||||
coin_consumed_m: number
|
|
||||||
other_amount: number
|
|
||||||
deposit_deduct_amount: number
|
|
||||||
content: string
|
|
||||||
evidence_urls: string[]
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
const { data } = await apiClient.post<ApiResponse<Checkout>>(`/orders/${orderId}/checkout`, payload)
|
|
||||||
return data.data
|
return data.data
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function acceptCheckout(orderId: number) {
|
export async function confirmReceive(id: number) {
|
||||||
const { data } = await apiClient.post<ApiResponse<{ accepted: boolean }>>(`/orders/${orderId}/checkout/accept`)
|
const { data } = await apiClient.post<ApiResponse<{ confirmed: boolean }>>(`/orders/${id}/receive`)
|
||||||
return data.data
|
return data.data
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function rejectCheckout(orderId: number) {
|
export async function startOrderPayment(orderId: number, req: StartOrderPaymentRequest = {}) {
|
||||||
const { data } = await apiClient.post<ApiResponse<{ rejected: boolean }>>(`/orders/${orderId}/checkout/reject`)
|
const { data } = await apiClient.post<ApiResponse<PaymentOrder>>(`/orders/${orderId}/pay`, {
|
||||||
|
pay_way: req.pay_way || 'wechat',
|
||||||
|
jspay_flag: req.jspay_flag || '',
|
||||||
|
})
|
||||||
return data.data
|
return data.data
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function counterCheckout(orderId: number, checkoutId: number) {
|
export function startPayment(orderId: number, payWay: string, jsPayFlag?: string) {
|
||||||
const { data } = await apiClient.post<ApiResponse<Checkout>>(`/orders/${orderId}/checkout/${checkoutId}/counter`)
|
return startOrderPayment(orderId, { pay_way: payWay, jspay_flag: jsPayFlag || '' })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function queryOrderPayment(orderId: number) {
|
||||||
|
const { data } = await apiClient.post<ApiResponse<PaymentOrder>>(`/orders/${orderId}/pay/query`)
|
||||||
return data.data
|
return data.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function queryPayment(orderId: number) {
|
||||||
|
return queryOrderPayment(orderId)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function submitCheckout(id: number, payload: SubmitCheckoutPayload) {
|
||||||
|
const { data } = await apiClient.post<ApiResponse<Checkout>>(
|
||||||
|
`/orders/${id}/checkout`,
|
||||||
|
toCheckoutRequest(payload)
|
||||||
|
)
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCheckout(id: number, payload: SubmitCheckoutPayload) {
|
||||||
|
return submitCheckout(id, payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function confirmCheckout(id: number) {
|
||||||
|
const { data } = await apiClient.post<ApiResponse<{ confirmed: boolean }>>(
|
||||||
|
`/orders/${id}/checkout/confirm`
|
||||||
|
)
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function acceptCheckout(id: number) {
|
||||||
|
const { data } = await apiClient.post<ApiResponse<{ accepted: boolean }>>(`/orders/${id}/checkout/accept`)
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function rejectCheckout(id: number) {
|
||||||
|
const { data } = await apiClient.post<ApiResponse<{ rejected: boolean }>>(`/orders/${id}/checkout/reject`)
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function counterCheckout(id: number, payload: SubmitCheckoutPayload) {
|
||||||
|
const { data } = await apiClient.post<ApiResponse<Checkout>>(
|
||||||
|
`/orders/${id}/checkout/counter`,
|
||||||
|
toCheckoutRequest(payload)
|
||||||
|
)
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchAdminOrders(page = 1, pageSize = 20) {
|
||||||
|
const { data } = await apiClient.get<ApiResponse<PaginatedResult<Order>>>('/admin/orders', {
|
||||||
|
params: { page, page_size: pageSize },
|
||||||
|
})
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchAdminOrder(id: string | number) {
|
||||||
|
const { data } = await apiClient.get<ApiResponse<Order>>(`/admin/orders/${id}`)
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchAdminHandoffRecords(id: string | number) {
|
||||||
|
const { data } = await apiClient.get<ApiResponse<HandoffRecord[]>>(`/admin/orders/${id}/handoffs`)
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function adminCloseOrder(id: number, reason: string) {
|
||||||
|
const { data } = await apiClient.post<ApiResponse<Order>>(`/admin/orders/${id}/close`, { reason })
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function adminMarkOrderAbnormal(id: number, reason: string) {
|
||||||
|
const { data } = await apiClient.post<ApiResponse<Order>>(`/admin/orders/${id}/abnormal`, { reason })
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function adminRefundStatus(id: number) {
|
||||||
|
const { data } = await apiClient.get<ApiResponse<RefundStatus>>(`/admin/orders/${id}/refund-status`)
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function adminRefundOrder(id: number) {
|
||||||
|
const { data } = await apiClient.post<ApiResponse<RefundStatus>>(`/admin/orders/${id}/refund`)
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { showToast, showDialog } from 'vant'
|
|||||||
import { fetchOrderChat } from '@/features/chats/api/chats'
|
import { fetchOrderChat } from '@/features/chats/api/chats'
|
||||||
import { createDispute } from '@/features/disputes/api/disputes'
|
import { createDispute } from '@/features/disputes/api/disputes'
|
||||||
import { uploadFile } from '@/shared/api/files'
|
import { uploadFile } from '@/shared/api/files'
|
||||||
import { formatMoney } from '@/shared/utils/money'
|
import { centToYuan, formatMoney } from '@/shared/utils/money'
|
||||||
import {
|
import {
|
||||||
acceptCheckout,
|
acceptCheckout,
|
||||||
cancelOrder,
|
cancelOrder,
|
||||||
@@ -506,7 +506,9 @@ function checkoutContentWithSummary() {
|
|||||||
}
|
}
|
||||||
if (Number(checkoutForm.value.coin_consumed_m || 0) > 0) {
|
if (Number(checkoutForm.value.coin_consumed_m || 0) > 0) {
|
||||||
lines.push(
|
lines.push(
|
||||||
`哈夫币消耗:${quantity(Number(checkoutForm.value.coin_consumed_m))}M,预计剩余${quantity(remainingHafCoinM.value)}M`
|
`哈夫币消耗:${quantity(Number(checkoutForm.value.coin_consumed_m))}M,预计剩余${quantity(
|
||||||
|
remainingHafCoinM.value
|
||||||
|
)}M`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (lines.length === 0) {
|
if (lines.length === 0) {
|
||||||
@@ -553,17 +555,26 @@ function formatHandoffRecordType(type: string) {
|
|||||||
return typeMap[type] || type
|
return typeMap[type] || type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function amountYuan(cent: unknown, legacyYuan?: unknown) {
|
||||||
|
if (cent !== undefined && cent !== null) return centToYuan(readNumber(cent))
|
||||||
|
return readNumber(legacyYuan)
|
||||||
|
}
|
||||||
|
|
||||||
function orderRentAmount(item: Order) {
|
function orderRentAmount(item: Order) {
|
||||||
if (item.owner_id === session.userId)
|
if (item.owner_id === session.userId)
|
||||||
return Number(item.owner_rent_amount ?? item.display_amount ?? 0)
|
return amountYuan(item.owner_rent_amount_cent, item.owner_rent_amount ?? item.display_amount)
|
||||||
if (item.renter_id === session.userId) return Number(item.rent_amount ?? item.display_amount ?? 0)
|
if (item.renter_id === session.userId)
|
||||||
return Number(item.display_amount ?? 0)
|
return amountYuan(item.rent_amount_cent, item.rent_amount ?? item.display_amount)
|
||||||
|
return amountYuan(item.display_amount_cent, item.display_amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ownerActualIncome(item: Order) {
|
function ownerActualIncome(item: Order) {
|
||||||
if (item.owner_id !== session.userId) return null
|
if (item.owner_id !== session.userId) return null
|
||||||
const value = item.checkout?.owner_income_amount
|
const value = item.checkout?.owner_income_amount_cent
|
||||||
return typeof value === 'number' ? value : null
|
if (typeof value === 'number') return centToYuan(value)
|
||||||
|
return typeof item.checkout?.owner_income_amount === 'number'
|
||||||
|
? item.checkout.owner_income_amount
|
||||||
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
function quantity(value: unknown) {
|
function quantity(value: unknown) {
|
||||||
@@ -583,10 +594,16 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|||||||
function hydrateCounterForm() {
|
function hydrateCounterForm() {
|
||||||
if (!order.value?.checkout) return
|
if (!order.value?.checkout) return
|
||||||
const checkout = order.value.checkout
|
const checkout = order.value.checkout
|
||||||
counterForm.value.consumable_amount = checkout.consumable_amount
|
counterForm.value.consumable_amount = amountYuan(
|
||||||
|
checkout.consumable_amount_cent,
|
||||||
|
checkout.consumable_amount
|
||||||
|
)
|
||||||
counterForm.value.coin_consumed_m = checkout.coin_consumed_m
|
counterForm.value.coin_consumed_m = checkout.coin_consumed_m
|
||||||
counterForm.value.other_amount = checkout.other_amount
|
counterForm.value.other_amount = amountYuan(checkout.other_amount_cent, checkout.other_amount)
|
||||||
counterForm.value.deposit_deduct_amount = checkout.deposit_deduct_amount
|
counterForm.value.deposit_deduct_amount = amountYuan(
|
||||||
|
checkout.deposit_deduct_amount_cent,
|
||||||
|
checkout.deposit_deduct_amount
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function linesToList(value: string) {
|
function linesToList(value: string) {
|
||||||
@@ -676,9 +693,15 @@ async function copyListingCode() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="meta-item">
|
<div class="meta-item">
|
||||||
<span class="meta-label">押金</span>
|
<span class="meta-label">押金</span>
|
||||||
<strong class="meta-value">¥{{ money(order.deposit_amount) }}</strong>
|
<strong class="meta-value"
|
||||||
<span v-if="order.deposit_waived_amount > 0" class="meta-note"
|
>¥{{ money(amountYuan(order.deposit_amount_cent, order.deposit_amount)) }}</strong
|
||||||
>已免押 ¥{{ money(order.deposit_waived_amount) }}</span
|
>
|
||||||
|
<span
|
||||||
|
v-if="amountYuan(order.deposit_waived_amount_cent, order.deposit_waived_amount) > 0"
|
||||||
|
class="meta-note"
|
||||||
|
>已免押 ¥{{
|
||||||
|
money(amountYuan(order.deposit_waived_amount_cent, order.deposit_waived_amount))
|
||||||
|
}}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="meta-item">
|
<div class="meta-item">
|
||||||
@@ -929,30 +952,58 @@ async function copyListingCode() {
|
|||||||
>
|
>
|
||||||
<h3 class="section-title">结账结算账单</h3>
|
<h3 class="section-title">结账结算账单</h3>
|
||||||
<van-cell-group inset :border="false">
|
<van-cell-group inset :border="false">
|
||||||
<van-cell title="实际结算租金" :value="`¥${money(order.checkout.display_amount)}`" />
|
<van-cell
|
||||||
|
title="实际结算租金"
|
||||||
|
:value="`¥${money(
|
||||||
|
amountYuan(order.checkout.display_amount_cent, order.checkout.display_amount)
|
||||||
|
)}`"
|
||||||
|
/>
|
||||||
<van-cell
|
<van-cell
|
||||||
title="押金总额"
|
title="押金总额"
|
||||||
:label="
|
:label="
|
||||||
order.deposit_waived_amount > 0 ? `已免押 ¥${money(order.deposit_waived_amount)}` : ''
|
amountYuan(order.deposit_waived_amount_cent, order.deposit_waived_amount) > 0
|
||||||
|
? `已免押 ¥${money(
|
||||||
|
amountYuan(order.deposit_waived_amount_cent, order.deposit_waived_amount)
|
||||||
|
)}`
|
||||||
|
: ''
|
||||||
"
|
"
|
||||||
:value="`¥${money(order.checkout.deposit_amount)}`"
|
:value="`¥${money(
|
||||||
|
amountYuan(order.checkout.deposit_amount_cent, order.checkout.deposit_amount)
|
||||||
|
)}`"
|
||||||
|
/>
|
||||||
|
<van-cell
|
||||||
|
title="额外消耗品已用"
|
||||||
|
:value="`¥${money(
|
||||||
|
amountYuan(order.checkout.consumable_amount_cent, order.checkout.consumable_amount)
|
||||||
|
)}`"
|
||||||
/>
|
/>
|
||||||
<van-cell title="额外消耗品已用" :value="`¥${money(order.checkout.consumable_amount)}`" />
|
|
||||||
<van-cell
|
<van-cell
|
||||||
title="押金赔付扣除"
|
title="押金赔付扣除"
|
||||||
:value="`¥${money(order.checkout.deposit_deduct_amount)}`"
|
:value="`¥${money(
|
||||||
|
amountYuan(order.checkout.deposit_deduct_amount_cent, order.checkout.deposit_deduct_amount)
|
||||||
|
)}`"
|
||||||
value-class="red-text"
|
value-class="red-text"
|
||||||
/>
|
/>
|
||||||
<van-cell
|
<van-cell
|
||||||
v-if="isRenter"
|
v-if="isRenter"
|
||||||
title="退还租客"
|
title="退还租客"
|
||||||
:value="`¥${money(order.checkout.renter_refund_amount)}`"
|
:value="`¥${money(
|
||||||
|
amountYuan(
|
||||||
|
order.checkout.renter_refund_amount_cent,
|
||||||
|
order.checkout.renter_refund_amount
|
||||||
|
)
|
||||||
|
)}`"
|
||||||
value-class="green-text"
|
value-class="green-text"
|
||||||
/>
|
/>
|
||||||
<van-cell
|
<van-cell
|
||||||
v-if="isOwner"
|
v-if="isOwner"
|
||||||
title="号主最终收入"
|
title="号主最终收入"
|
||||||
:value="`¥${money(order.checkout.owner_income_amount)}`"
|
:value="`¥${money(
|
||||||
|
amountYuan(
|
||||||
|
order.checkout.owner_income_amount_cent,
|
||||||
|
order.checkout.owner_income_amount
|
||||||
|
)
|
||||||
|
)}`"
|
||||||
value-class="green-text"
|
value-class="green-text"
|
||||||
/>
|
/>
|
||||||
<van-cell
|
<van-cell
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
type Order,
|
type Order,
|
||||||
type PaymentOrder,
|
type PaymentOrder,
|
||||||
} from '@/features/orders/api/orders'
|
} from '@/features/orders/api/orders'
|
||||||
import { formatMoney } from '@/shared/utils/money'
|
import { centToYuan, formatMoney } from '@/shared/utils/money'
|
||||||
import { useSessionStore } from '@/stores/session'
|
import { useSessionStore } from '@/stores/session'
|
||||||
import { formatDateMinute } from '@/utils/time'
|
import { formatDateMinute } from '@/utils/time'
|
||||||
import { formatListingNo } from '@/utils/listingDisplay'
|
import { formatListingNo } from '@/utils/listingDisplay'
|
||||||
@@ -131,15 +131,21 @@ function amountLabel(order: Order) {
|
|||||||
return isOwner(order) ? '预计租金' : '支付租金'
|
return isOwner(order) ? '预计租金' : '支付租金'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function amountYuan(cent: unknown, legacyYuan?: unknown) {
|
||||||
|
if (cent !== undefined && cent !== null) return centToYuan(Number(cent || 0))
|
||||||
|
return Number(legacyYuan || 0)
|
||||||
|
}
|
||||||
|
|
||||||
function orderRentAmount(order: Order) {
|
function orderRentAmount(order: Order) {
|
||||||
if (isOwner(order)) return Number(order.owner_rent_amount ?? order.display_amount ?? 0)
|
if (isOwner(order)) return amountYuan(order.owner_rent_amount_cent, order.owner_rent_amount ?? order.display_amount)
|
||||||
return Number(order.rent_amount ?? order.display_amount ?? 0)
|
return amountYuan(order.rent_amount_cent, order.rent_amount ?? order.display_amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ownerActualIncome(order: Order) {
|
function ownerActualIncome(order: Order) {
|
||||||
if (!isOwner(order)) return null
|
if (!isOwner(order)) return null
|
||||||
const value = order.checkout?.owner_income_amount
|
const value = order.checkout?.owner_income_amount_cent
|
||||||
return typeof value === 'number' ? value : null
|
if (typeof value === 'number') return centToYuan(value)
|
||||||
|
return typeof order.checkout?.owner_income_amount === 'number' ? order.checkout.owner_income_amount : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatListingCode(order: Order) {
|
function formatListingCode(order: Order) {
|
||||||
@@ -232,9 +238,9 @@ async function copyListingCode(order: Order) {
|
|||||||
<div class="price-item">
|
<div class="price-item">
|
||||||
<span class="price-label">押金金额</span>
|
<span class="price-label">押金金额</span>
|
||||||
<span class="price-val deposit">
|
<span class="price-val deposit">
|
||||||
¥{{ money(order.deposit_amount) }}
|
¥{{ money(amountYuan(order.deposit_amount_cent, order.deposit_amount)) }}
|
||||||
<em v-if="order.deposit_waived_amount > 0"
|
<em v-if="amountYuan(order.deposit_waived_amount_cent, order.deposit_waived_amount) > 0"
|
||||||
>免 ¥{{ money(order.deposit_waived_amount) }}</em
|
>免 ¥{{ money(amountYuan(order.deposit_waived_amount_cent, order.deposit_waived_amount)) }}</em
|
||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import QRCode from 'qrcode'
|
|||||||
import { fetchOrderChat } from '@/features/chats/api/chats'
|
import { fetchOrderChat } from '@/features/chats/api/chats'
|
||||||
import { createDispute } from '@/features/disputes'
|
import { createDispute } from '@/features/disputes'
|
||||||
import { uploadFile } from '@/shared/api/files'
|
import { uploadFile } from '@/shared/api/files'
|
||||||
import { formatMoney } from '@/shared/utils/money'
|
import { centToYuan, formatMoney } from '@/shared/utils/money'
|
||||||
import {
|
import {
|
||||||
acceptCheckout,
|
acceptCheckout,
|
||||||
cancelOrder,
|
cancelOrder,
|
||||||
@@ -600,17 +600,24 @@ function money(value: unknown) {
|
|||||||
return formatMoney(readNumber(value))
|
return formatMoney(readNumber(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function amountYuan(cent: unknown, legacyYuan?: unknown) {
|
||||||
|
if (cent !== undefined && cent !== null) return centToYuan(readNumber(cent))
|
||||||
|
return readNumber(legacyYuan)
|
||||||
|
}
|
||||||
|
|
||||||
function orderRentAmount(item: Order) {
|
function orderRentAmount(item: Order) {
|
||||||
if (item.owner_id === session.userId)
|
if (item.owner_id === session.userId)
|
||||||
return Number(item.owner_rent_amount ?? item.display_amount ?? 0)
|
return amountYuan(item.owner_rent_amount_cent, item.owner_rent_amount ?? item.display_amount)
|
||||||
if (item.renter_id === session.userId) return Number(item.rent_amount ?? item.display_amount ?? 0)
|
if (item.renter_id === session.userId)
|
||||||
return Number(item.display_amount ?? 0)
|
return amountYuan(item.rent_amount_cent, item.rent_amount ?? item.display_amount)
|
||||||
|
return amountYuan(item.display_amount_cent, item.display_amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ownerActualIncome(item: Order) {
|
function ownerActualIncome(item: Order) {
|
||||||
if (item.owner_id !== session.userId) return null
|
if (item.owner_id !== session.userId) return null
|
||||||
const value = item.checkout?.owner_income_amount
|
const value = item.checkout?.owner_income_amount_cent
|
||||||
return typeof value === 'number' ? value : null
|
if (typeof value === 'number') return centToYuan(value)
|
||||||
|
return typeof item.checkout?.owner_income_amount === 'number' ? item.checkout.owner_income_amount : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function quantity(value: unknown) {
|
function quantity(value: unknown) {
|
||||||
@@ -630,10 +637,10 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|||||||
function hydrateCounterForm() {
|
function hydrateCounterForm() {
|
||||||
if (!order.value?.checkout) return
|
if (!order.value?.checkout) return
|
||||||
const checkout = order.value.checkout
|
const checkout = order.value.checkout
|
||||||
counterForm.value.consumable_amount = checkout.consumable_amount
|
counterForm.value.consumable_amount = amountYuan(checkout.consumable_amount_cent, checkout.consumable_amount)
|
||||||
counterForm.value.coin_consumed_m = checkout.coin_consumed_m
|
counterForm.value.coin_consumed_m = checkout.coin_consumed_m
|
||||||
counterForm.value.other_amount = checkout.other_amount
|
counterForm.value.other_amount = amountYuan(checkout.other_amount_cent, checkout.other_amount)
|
||||||
counterForm.value.deposit_deduct_amount = checkout.deposit_deduct_amount
|
counterForm.value.deposit_deduct_amount = amountYuan(checkout.deposit_deduct_amount_cent, checkout.deposit_deduct_amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
function linesToList(value: string) {
|
function linesToList(value: string) {
|
||||||
@@ -767,9 +774,9 @@ async function copyListingCode() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="metric-card">
|
<div class="metric-card">
|
||||||
<span class="metric-label">押金</span>
|
<span class="metric-label">押金</span>
|
||||||
<strong class="metric-value">¥{{ money(order.deposit_amount) }}</strong>
|
<strong class="metric-value">¥{{ money(amountYuan(order.deposit_amount_cent, order.deposit_amount)) }}</strong>
|
||||||
<span v-if="order.deposit_waived_amount > 0" class="metric-note"
|
<span v-if="amountYuan(order.deposit_waived_amount_cent, order.deposit_waived_amount) > 0" class="metric-note"
|
||||||
>已免押 ¥{{ money(order.deposit_waived_amount) }}</span
|
>已免押 ¥{{ money(amountYuan(order.deposit_waived_amount_cent, order.deposit_waived_amount)) }}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -951,32 +958,32 @@ async function copyListingCode() {
|
|||||||
<div class="checkout-summary-card">
|
<div class="checkout-summary-card">
|
||||||
<div class="summary-row">
|
<div class="summary-row">
|
||||||
<span>实际结算租金</span>
|
<span>实际结算租金</span>
|
||||||
<strong>¥{{ money(order.checkout.display_amount) }}</strong>
|
<strong>¥{{ money(amountYuan(order.checkout.display_amount_cent, order.checkout.display_amount)) }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-row">
|
<div class="summary-row">
|
||||||
<span>预收押金</span>
|
<span>预收押金</span>
|
||||||
<strong>
|
<strong>
|
||||||
¥{{ money(order.checkout.deposit_amount) }}
|
¥{{ money(amountYuan(order.checkout.deposit_amount_cent, order.checkout.deposit_amount)) }}
|
||||||
<em v-if="order.deposit_waived_amount > 0"
|
<em v-if="amountYuan(order.deposit_waived_amount_cent, order.deposit_waived_amount) > 0"
|
||||||
>已免押 ¥{{ money(order.deposit_waived_amount) }}</em
|
>已免押 ¥{{ money(amountYuan(order.deposit_waived_amount_cent, order.deposit_waived_amount)) }}</em
|
||||||
>
|
>
|
||||||
</strong>
|
</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-row">
|
<div class="summary-row">
|
||||||
<span>额外消耗品已用</span>
|
<span>额外消耗品已用</span>
|
||||||
<strong class="warning">¥{{ money(order.checkout.consumable_amount) }}</strong>
|
<strong class="warning">¥{{ money(amountYuan(order.checkout.consumable_amount_cent, order.checkout.consumable_amount)) }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-row">
|
<div class="summary-row">
|
||||||
<span>押金赔付扣除</span>
|
<span>押金赔付扣除</span>
|
||||||
<strong class="warning">¥{{ money(order.checkout.deposit_deduct_amount) }}</strong>
|
<strong class="warning">¥{{ money(amountYuan(order.checkout.deposit_deduct_amount_cent, order.checkout.deposit_deduct_amount)) }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isRenter" class="summary-row highlight">
|
<div v-if="isRenter" class="summary-row highlight">
|
||||||
<span>退还租客(未使用租金 + 剩余押金)</span>
|
<span>退还租客(未使用租金 + 剩余押金)</span>
|
||||||
<strong class="amount">¥{{ money(order.checkout.renter_refund_amount) }}</strong>
|
<strong class="amount">¥{{ money(amountYuan(order.checkout.renter_refund_amount_cent, order.checkout.renter_refund_amount)) }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isOwner" class="summary-row highlight">
|
<div v-if="isOwner" class="summary-row highlight">
|
||||||
<span>号主最终收入(租金 + 押金赔付)</span>
|
<span>号主最终收入(租金 + 押金赔付)</span>
|
||||||
<strong class="amount">¥{{ money(order.checkout.owner_income_amount) }}</strong>
|
<strong class="amount">¥{{ money(amountYuan(order.checkout.owner_income_amount_cent, order.checkout.owner_income_amount)) }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="order.checkout.content" class="summary-note">
|
<div v-if="order.checkout.content" class="summary-note">
|
||||||
<label>说明:</label>
|
<label>说明:</label>
|
||||||
@@ -1031,7 +1038,7 @@ async function copyListingCode() {
|
|||||||
v-model="counterForm.deposit_deduct_amount"
|
v-model="counterForm.deposit_deduct_amount"
|
||||||
class="full-control"
|
class="full-control"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="order.deposit_amount"
|
:max="amountYuan(order.deposit_amount_cent, order.deposit_amount)"
|
||||||
:precision="0"
|
:precision="0"
|
||||||
controls-position="right"
|
controls-position="right"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useRoute, useRouter } from 'vue-router'
|
|||||||
import { CopyDocument, Search } from '@element-plus/icons-vue'
|
import { CopyDocument, Search } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
import { fetchOrders, type Order } from '@/features/orders'
|
import { fetchOrders, type Order } from '@/features/orders'
|
||||||
import { formatMoney } from '@/shared/utils/money'
|
import { centToYuan, formatMoney } from '@/shared/utils/money'
|
||||||
import { useSessionStore } from '@/stores/session'
|
import { useSessionStore } from '@/stores/session'
|
||||||
import { orderStatusLabel } from '@/utils/statusLabels'
|
import { orderStatusLabel } from '@/utils/statusLabels'
|
||||||
import { formatDateTime } from '@/utils/time'
|
import { formatDateTime } from '@/utils/time'
|
||||||
@@ -127,16 +127,22 @@ function amountLabel(order: Order) {
|
|||||||
return isRenter(order) ? '支付租金' : '预计租金'
|
return isRenter(order) ? '支付租金' : '预计租金'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function amountYuan(cent: unknown, legacyYuan?: unknown) {
|
||||||
|
if (cent !== undefined && cent !== null) return centToYuan(Number(cent || 0))
|
||||||
|
return Number(legacyYuan || 0)
|
||||||
|
}
|
||||||
|
|
||||||
function orderRentAmount(order: Order) {
|
function orderRentAmount(order: Order) {
|
||||||
if (isOwner(order)) return Number(order.owner_rent_amount ?? order.display_amount ?? 0)
|
if (isOwner(order)) return amountYuan(order.owner_rent_amount_cent, order.owner_rent_amount ?? order.display_amount)
|
||||||
if (isRenter(order)) return Number(order.rent_amount ?? order.display_amount ?? 0)
|
if (isRenter(order)) return amountYuan(order.rent_amount_cent, order.rent_amount ?? order.display_amount)
|
||||||
return Number(order.display_amount ?? 0)
|
return amountYuan(order.display_amount_cent, order.display_amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ownerActualIncome(order: Order) {
|
function ownerActualIncome(order: Order) {
|
||||||
if (!isOwner(order)) return null
|
if (!isOwner(order)) return null
|
||||||
const value = order.checkout?.owner_income_amount
|
const value = order.checkout?.owner_income_amount_cent
|
||||||
return typeof value === 'number' ? value : null
|
if (typeof value === 'number') return centToYuan(value)
|
||||||
|
return typeof order.checkout?.owner_income_amount === 'number' ? order.checkout.owner_income_amount : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function money(value: unknown) {
|
function money(value: unknown) {
|
||||||
@@ -351,9 +357,9 @@ function getCountdownMinutes(order: Order) {
|
|||||||
<div class="meta-row">
|
<div class="meta-row">
|
||||||
<span class="meta-label">押金</span>
|
<span class="meta-label">押金</span>
|
||||||
<span class="meta-value">
|
<span class="meta-value">
|
||||||
¥{{ money(order.deposit_amount) }}
|
¥{{ money(amountYuan(order.deposit_amount_cent, order.deposit_amount)) }}
|
||||||
<em v-if="order.deposit_waived_amount > 0"
|
<em v-if="amountYuan(order.deposit_waived_amount_cent, order.deposit_waived_amount) > 0"
|
||||||
>免 ¥{{ money(order.deposit_waived_amount) }}</em
|
>免 ¥{{ money(amountYuan(order.deposit_waived_amount_cent, order.deposit_waived_amount)) }}</em
|
||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ export function useWallet() {
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const error = ref<string | null>(null)
|
const error = ref<string | null>(null)
|
||||||
|
|
||||||
const availableBalance = computed(() => balance.value?.available_balance ?? 0)
|
const availableBalance = computed(() => balance.value?.available_balance_cent ?? 0)
|
||||||
const frozenBalance = computed(() => balance.value?.frozen_balance ?? 0)
|
const frozenBalance = computed(() => balance.value?.frozen_balance_cent ?? 0)
|
||||||
const totalBalance = computed(() => availableBalance.value + frozenBalance.value)
|
const totalBalance = computed(() => availableBalance.value + frozenBalance.value)
|
||||||
|
|
||||||
async function loadBalance() {
|
async function loadBalance() {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
import type { PaymentOrder } from '@/features/orders'
|
import type { PaymentOrder } from '@/features/orders'
|
||||||
import { balanceTypeLabel, ledgerDirectionLabel, walletStatusLabel } from '@/utils/statusLabels'
|
import { balanceTypeLabel, ledgerDirectionLabel, walletStatusLabel } from '@/utils/statusLabels'
|
||||||
import { formatDateTime } from '@/utils/time'
|
import { formatDateTime } from '@/utils/time'
|
||||||
import { formatCent, formatCentWithSymbol } from '@/shared/utils/money'
|
import { formatCentWithSymbol, formatMoney } from '@/shared/utils/money'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
@@ -276,7 +276,7 @@ function amountPrefix(direction: string) {
|
|||||||
</div>
|
</div>
|
||||||
<div class="wallet-hero-action">
|
<div class="wallet-hero-action">
|
||||||
<span>当前可用</span>
|
<span>当前可用</span>
|
||||||
<strong>{{ account ? formatMoney(account.available_balance) : '¥0.00' }}</strong>
|
<strong>{{ account ? formatCentWithSymbol(account.available_balance_cent) : '¥0.0' }}</strong>
|
||||||
<el-button class="withdraw-button" :icon="Money" type="primary" @click="handleWithdraw">
|
<el-button class="withdraw-button" :icon="Money" type="primary" @click="handleWithdraw">
|
||||||
申请提现
|
申请提现
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -374,10 +374,10 @@ function amountPrefix(direction: string) {
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
</span>
|
</span>
|
||||||
<span class="ledger-cell align-right amount-cell" :class="`is-${row.direction}`">
|
<span class="ledger-cell align-right amount-cell" :class="`is-${row.direction}`">
|
||||||
{{ amountPrefix(row.direction) }}{{ formatMoney(row.amount) }}
|
{{ amountPrefix(row.direction) }}{{ formatCentWithSymbol(row.amount_cent) }}
|
||||||
</span>
|
</span>
|
||||||
<span class="ledger-cell muted-cell">{{ balanceTypeLabel(row.balance_type) }}</span>
|
<span class="ledger-cell muted-cell">{{ balanceTypeLabel(row.balance_type) }}</span>
|
||||||
<span class="ledger-cell align-right">{{ formatMoney(row.balance_after) }}</span>
|
<span class="ledger-cell align-right">{{ formatCentWithSymbol(row.balance_after_cent) }}</span>
|
||||||
<span class="ledger-cell" :title="row.remark">{{ row.remark || '-' }}</span>
|
<span class="ledger-cell" :title="row.remark">{{ row.remark || '-' }}</span>
|
||||||
<span class="ledger-cell">{{ formatDateTime(row.created_at) }}</span>
|
<span class="ledger-cell">{{ formatDateTime(row.created_at) }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
type WithdrawalRequest,
|
type WithdrawalRequest,
|
||||||
} from '../api/withdrawal'
|
} from '../api/withdrawal'
|
||||||
import { fetchWalletBalance, type WalletAccount } from '../api/wallet'
|
import { fetchWalletBalance, type WalletAccount } from '../api/wallet'
|
||||||
import { formatMoney, formatMoneyWithSymbol } from '@/shared/utils/money'
|
import { formatMoney, formatMoneyWithSymbol, formatCent, yuanToCent } from '@/shared/utils/money'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ const canWithdraw = computed(() => {
|
|||||||
withdrawForm.value.amount >= MIN_AMOUNT &&
|
withdrawForm.value.amount >= MIN_AMOUNT &&
|
||||||
withdrawForm.value.amount <= MAX_AMOUNT &&
|
withdrawForm.value.amount <= MAX_AMOUNT &&
|
||||||
account.value &&
|
account.value &&
|
||||||
withdrawForm.value.amount <= account.value.available_balance
|
yuanToCent(withdrawForm.value.amount) <= account.value.available_balance_cent
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -95,7 +95,10 @@ async function handleSubmit() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
await createWithdrawal(withdrawForm.value)
|
await createWithdrawal({
|
||||||
|
payment_account_id: withdrawForm.value.payment_account_id,
|
||||||
|
amount_cent: yuanToCent(withdrawForm.value.amount),
|
||||||
|
})
|
||||||
ElMessage.success('提现申请已提交')
|
ElMessage.success('提现申请已提交')
|
||||||
withdrawForm.value.amount = 0
|
withdrawForm.value.amount = 0
|
||||||
await loadData()
|
await loadData()
|
||||||
@@ -176,7 +179,7 @@ function accountTypeLabel(type: string) {
|
|||||||
<el-icon><Wallet /></el-icon>
|
<el-icon><Wallet /></el-icon>
|
||||||
可用余额
|
可用余额
|
||||||
</div>
|
</div>
|
||||||
<div class="balance-value">¥{{ formatMoney(account?.available_balance) }}</div>
|
<div class="balance-value">¥{{ formatCent(account?.available_balance_cent) }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="balance-item">
|
<div class="balance-item">
|
||||||
<div class="balance-label">
|
<div class="balance-label">
|
||||||
@@ -184,7 +187,7 @@ function accountTypeLabel(type: string) {
|
|||||||
冻结余额
|
冻结余额
|
||||||
</div>
|
</div>
|
||||||
<div class="balance-value frozen">
|
<div class="balance-value frozen">
|
||||||
¥{{ formatMoney(account?.frozen_balance) }}
|
¥{{ formatCent(account?.frozen_balance_cent) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -258,14 +261,14 @@ function accountTypeLabel(type: string) {
|
|||||||
|
|
||||||
<el-alert
|
<el-alert
|
||||||
v-if="
|
v-if="
|
||||||
withdrawForm.amount > 0 && account && withdrawForm.amount > account.available_balance
|
withdrawForm.amount > 0 && account && yuanToCent(withdrawForm.amount) > account.available_balance_cent
|
||||||
"
|
"
|
||||||
type="error"
|
type="error"
|
||||||
:closable="false"
|
:closable="false"
|
||||||
show-icon
|
show-icon
|
||||||
style="margin-bottom: 16px"
|
style="margin-bottom: 16px"
|
||||||
>
|
>
|
||||||
余额不足,可用余额:¥{{ formatMoney(account.available_balance) }}
|
余额不足,可用余额:¥{{ formatCent(account.available_balance_cent) }}
|
||||||
</el-alert>
|
</el-alert>
|
||||||
|
|
||||||
<el-alert type="info" :closable="false" show-icon style="margin-bottom: 16px">
|
<el-alert type="info" :closable="false" show-icon style="margin-bottom: 16px">
|
||||||
@@ -309,7 +312,7 @@ function accountTypeLabel(type: string) {
|
|||||||
<el-table-column prop="withdraw_no" label="提现单号" min-width="180" />
|
<el-table-column prop="withdraw_no" label="提现单号" min-width="180" />
|
||||||
<el-table-column label="提现金额" width="120">
|
<el-table-column label="提现金额" width="120">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span style="color: #f56c6c; font-weight: 600"> ¥{{ formatMoney(row.amount) }} </span>
|
<span style="color: #f56c6c; font-weight: 600"> ¥{{ formatCent(row.amount_cent) }} </span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="收款方式" width="150">
|
<el-table-column label="收款方式" width="150">
|
||||||
|
|||||||
Reference in New Issue
Block a user