906 lines
30 KiB
Vue
906 lines
30 KiB
Vue
<script setup lang="ts">
|
||
import { computed, onMounted, reactive, ref } from 'vue'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
import { EditPen, Plus, Refresh, Search } from '@element-plus/icons-vue'
|
||
import {
|
||
cancelAdminPickup,
|
||
confirmAdminPickupOfflineSettlement,
|
||
completeAdminPickup,
|
||
createAdminPickupFinancialAdjustment,
|
||
createAdminPickup,
|
||
fetchAdminPickups,
|
||
fetchAdminPickupShopOptions,
|
||
fetchAvailableListings,
|
||
updateAdminPickupProfit,
|
||
type AdminPickup,
|
||
type AvailableListing,
|
||
type AvailableListingSourceType,
|
||
type PickupAccountSource,
|
||
type PickupOfflineSettlementStatus,
|
||
} from '@/features/admin/api/adminPickup'
|
||
import { centToYuan, formatCentWithSymbol, yuanToCent } from '@/shared/utils/money'
|
||
import { formatDateTime } from '@/shared/utils/time'
|
||
import { pickupStatusLabel } from '@/shared/utils/statusLabels'
|
||
import { adminPath } from '@/shared/utils/adminPath'
|
||
|
||
const loading = ref(false)
|
||
const items = ref<AdminPickup[]>([])
|
||
const total = ref(0)
|
||
const filters = reactive({
|
||
page: 1,
|
||
page_size: 20,
|
||
keyword: '',
|
||
status: '',
|
||
shop_name: '',
|
||
account_source: '' as PickupAccountSource | '',
|
||
offline_settlement_status: '' as PickupOfflineSettlementStatus | '',
|
||
})
|
||
|
||
const createDialogVisible = ref(false)
|
||
const completeDialogVisible = ref(false)
|
||
const profitDialogVisible = ref(false)
|
||
const financialAdjustmentDialogVisible = ref(false)
|
||
const activePickupId = ref(0)
|
||
const activePickup = ref<AdminPickup | null>(null)
|
||
const profitSaving = ref(false)
|
||
const financialAdjustmentSaving = ref(false)
|
||
|
||
const createForm = reactive({
|
||
listing_id: null as number | null,
|
||
platform: '',
|
||
shop_name: '',
|
||
profit_amount: 0,
|
||
remark: '',
|
||
})
|
||
const completeForm = reactive({ settle_amount: 0, profit_amount: 0, complete_remark: '' })
|
||
const profitForm = reactive({ profit_amount: 0, reason: '' })
|
||
const financialAdjustmentForm = reactive({ profit_amount: 0, settle_amount: 0, reason: '' })
|
||
|
||
const listingOptions = ref<AvailableListing[]>([])
|
||
const listingLoading = ref(false)
|
||
const listingKeyword = ref('')
|
||
const listingSourceType = ref<AvailableListingSourceType>('')
|
||
let listingRequestID = 0
|
||
const shopOptions = ref<string[]>([])
|
||
const shopOptionsLoading = ref(false)
|
||
const selectedListing = computed(
|
||
() => listingOptions.value.find(item => item.id === createForm.listing_id) || null
|
||
)
|
||
const selectedPriceSnapshot = computed(() => {
|
||
const item = selectedListing.value
|
||
if (!item) return null
|
||
const totalCent = normalizeCent(item.owner_total_price_cent || item.owner_price_cent)
|
||
let pureCent = normalizeCent(item.owner_pure_price_cent)
|
||
let extraCent = normalizeCent(item.owner_extra_item_price_cent)
|
||
if (pureCent <= 0 && extraCent <= 0) {
|
||
pureCent = totalCent
|
||
} else if (pureCent <= 0) {
|
||
pureCent = Math.max(totalCent - extraCent, 0)
|
||
} else if (extraCent <= 0) {
|
||
extraCent = Math.max(totalCent - pureCent, 0)
|
||
}
|
||
return { pureCent, extraCent, totalCent }
|
||
})
|
||
|
||
onMounted(() => {
|
||
loadList()
|
||
loadShopOptions()
|
||
})
|
||
|
||
async function loadList() {
|
||
loading.value = true
|
||
try {
|
||
const result = await fetchAdminPickups(filters)
|
||
items.value = result.items
|
||
total.value = result.total
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function handleSearch() {
|
||
filters.page = 1
|
||
loadList()
|
||
}
|
||
|
||
function resetFilters() {
|
||
filters.keyword = ''
|
||
filters.status = ''
|
||
filters.shop_name = ''
|
||
filters.account_source = ''
|
||
filters.offline_settlement_status = ''
|
||
filters.page = 1
|
||
loadList()
|
||
}
|
||
function handlePageChange(p: number) {
|
||
filters.page = p
|
||
loadList()
|
||
}
|
||
function handleSizeChange(s: number) {
|
||
filters.page_size = s
|
||
filters.page = 1
|
||
loadList()
|
||
}
|
||
|
||
function openCreateDialog() {
|
||
createForm.listing_id = null
|
||
createForm.platform = ''
|
||
createForm.shop_name = ''
|
||
createForm.profit_amount = 0
|
||
createForm.remark = ''
|
||
listingOptions.value = []
|
||
listingKeyword.value = ''
|
||
listingSourceType.value = ''
|
||
createDialogVisible.value = true
|
||
void searchListings('')
|
||
}
|
||
|
||
async function loadShopOptions() {
|
||
shopOptionsLoading.value = true
|
||
try {
|
||
shopOptions.value = await fetchAdminPickupShopOptions()
|
||
} finally {
|
||
shopOptionsLoading.value = false
|
||
}
|
||
}
|
||
|
||
async function searchListings(keyword: string) {
|
||
listingKeyword.value = keyword.trim()
|
||
const requestID = ++listingRequestID
|
||
listingLoading.value = true
|
||
try {
|
||
const result = await fetchAvailableListings(
|
||
listingKeyword.value,
|
||
1,
|
||
20,
|
||
listingSourceType.value
|
||
)
|
||
if (requestID === listingRequestID) {
|
||
listingOptions.value = result.items
|
||
}
|
||
} finally {
|
||
if (requestID === listingRequestID) {
|
||
listingLoading.value = false
|
||
}
|
||
}
|
||
}
|
||
|
||
function handleListingSourceChange() {
|
||
createForm.listing_id = null
|
||
listingOptions.value = []
|
||
void searchListings(listingKeyword.value)
|
||
}
|
||
|
||
async function handleCreate() {
|
||
if (!createForm.listing_id) {
|
||
ElMessage.warning('请选择要提号的账号')
|
||
return
|
||
}
|
||
if (createForm.profit_amount < 0) {
|
||
ElMessage.warning('线下利润不能为负数')
|
||
return
|
||
}
|
||
loading.value = true
|
||
try {
|
||
await createAdminPickup({
|
||
listing_id: createForm.listing_id,
|
||
platform: createForm.platform,
|
||
shop_name: createForm.shop_name,
|
||
profit_amount_cent: yuanToCent(createForm.profit_amount),
|
||
remark: createForm.remark,
|
||
})
|
||
ElMessage.success('提号订单已创建')
|
||
createDialogVisible.value = false
|
||
loadShopOptions()
|
||
loadList()
|
||
} catch (e: unknown) {
|
||
ElMessage.error(errorMessage(e) || '创建失败')
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function openCompleteDialog(row: AdminPickup) {
|
||
activePickupId.value = row.id
|
||
activePickup.value = row
|
||
completeForm.settle_amount = 0
|
||
completeForm.profit_amount = centToYuan(row.profit_amount_cent)
|
||
completeForm.complete_remark = ''
|
||
completeDialogVisible.value = true
|
||
}
|
||
|
||
async function handleConfirmOfflineSettlement(row: AdminPickup) {
|
||
try {
|
||
const { value } = await ElMessageBox.prompt(
|
||
`确认已向卖家线下支付 ${formatCentWithSymbol(effectiveSettleCent(row))}?`,
|
||
'确认线下结算',
|
||
{
|
||
confirmButtonText: '确认已打款',
|
||
cancelButtonText: '取消',
|
||
inputType: 'textarea',
|
||
inputPlaceholder: '结算备注(可选)',
|
||
inputValidator: input => input.length <= 255 || '备注不能超过 255 个字符',
|
||
}
|
||
)
|
||
loading.value = true
|
||
try {
|
||
await confirmAdminPickupOfflineSettlement(row.id, { remark: value })
|
||
ElMessage.success('已确认线下结算')
|
||
loadList()
|
||
} catch (e: unknown) {
|
||
ElMessage.error(errorMessage(e) || '确认失败')
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
} catch {
|
||
/* 用户放弃 */
|
||
}
|
||
}
|
||
|
||
function openProfitDialog(row: AdminPickup) {
|
||
activePickupId.value = row.id
|
||
profitForm.profit_amount = centToYuan(row.profit_amount_cent)
|
||
profitForm.reason = ''
|
||
profitDialogVisible.value = true
|
||
}
|
||
|
||
function openFinancialAdjustmentDialog(row: AdminPickup) {
|
||
activePickupId.value = row.id
|
||
activePickup.value = row
|
||
financialAdjustmentForm.profit_amount = centToYuan(effectiveProfitCent(row))
|
||
financialAdjustmentForm.settle_amount = centToYuan(effectiveSettleCent(row))
|
||
financialAdjustmentForm.reason = ''
|
||
financialAdjustmentDialogVisible.value = true
|
||
}
|
||
|
||
async function handleFinancialAdjustment() {
|
||
if (!financialAdjustmentForm.reason.trim()) {
|
||
ElMessage.warning('请输入调整原因')
|
||
return
|
||
}
|
||
if (financialAdjustmentForm.profit_amount < 0 || financialAdjustmentForm.settle_amount < 0) {
|
||
ElMessage.warning('金额不能小于 0')
|
||
return
|
||
}
|
||
financialAdjustmentSaving.value = true
|
||
try {
|
||
await createAdminPickupFinancialAdjustment(activePickupId.value, {
|
||
profit_amount_cent: yuanToCent(financialAdjustmentForm.profit_amount),
|
||
settle_amount_cent: yuanToCent(financialAdjustmentForm.settle_amount),
|
||
reason: financialAdjustmentForm.reason.trim(),
|
||
})
|
||
ElMessage.success(
|
||
activePickup.value?.settlement_mode === 'platform_managed'
|
||
? '财务调整已创建,请确认线下补款或追回'
|
||
: '财务调整已创建'
|
||
)
|
||
financialAdjustmentDialogVisible.value = false
|
||
loadList()
|
||
} catch (e: unknown) {
|
||
ElMessage.error(errorMessage(e) || '调整失败')
|
||
} finally {
|
||
financialAdjustmentSaving.value = false
|
||
}
|
||
}
|
||
|
||
async function handleComplete() {
|
||
if (completeForm.settle_amount <= 0) {
|
||
ElMessage.warning('请输入结算金额')
|
||
return
|
||
}
|
||
if (completeForm.profit_amount < 0) {
|
||
ElMessage.warning('线下利润不能为负数')
|
||
return
|
||
}
|
||
loading.value = true
|
||
try {
|
||
await completeAdminPickup(activePickupId.value, {
|
||
settle_amount_cent: yuanToCent(completeForm.settle_amount),
|
||
profit_amount_cent: yuanToCent(completeForm.profit_amount),
|
||
complete_remark: completeForm.complete_remark,
|
||
})
|
||
ElMessage.success(
|
||
activePickup.value?.settlement_mode === 'platform_managed'
|
||
? '提号已完成,待线下结算'
|
||
: '提号已完成,已给卖家结算'
|
||
)
|
||
completeDialogVisible.value = false
|
||
loadList()
|
||
} catch (e: unknown) {
|
||
ElMessage.error(errorMessage(e) || '完成失败')
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
async function handleUpdateProfit() {
|
||
if (profitForm.profit_amount < 0) {
|
||
ElMessage.warning('线下利润不能为负数')
|
||
return
|
||
}
|
||
profitSaving.value = true
|
||
try {
|
||
await updateAdminPickupProfit(activePickupId.value, {
|
||
profit_amount_cent: yuanToCent(profitForm.profit_amount),
|
||
reason: profitForm.reason,
|
||
})
|
||
ElMessage.success('线下利润已更新')
|
||
profitDialogVisible.value = false
|
||
loadList()
|
||
} catch (e: unknown) {
|
||
ElMessage.error(errorMessage(e) || '修改失败')
|
||
} finally {
|
||
profitSaving.value = false
|
||
}
|
||
}
|
||
|
||
async function handleCancel(id: number) {
|
||
try {
|
||
const { value } = await ElMessageBox.prompt('请输入取消原因', '取消提号', {
|
||
confirmButtonText: '确认取消',
|
||
cancelButtonText: '返回',
|
||
inputPattern: /.+/,
|
||
inputErrorMessage: '取消原因不能为空',
|
||
})
|
||
loading.value = true
|
||
try {
|
||
await cancelAdminPickup(id, value)
|
||
ElMessage.success('提号已取消')
|
||
loadList()
|
||
} catch (e: unknown) {
|
||
ElMessage.error(errorMessage(e) || '取消失败')
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
} catch {
|
||
/* 用户放弃 */
|
||
}
|
||
}
|
||
|
||
function statusType(status: string) {
|
||
if (status === 'picking_up') return 'warning'
|
||
if (status === 'completed') return 'success'
|
||
if (status === 'cancelled') return 'info'
|
||
return ''
|
||
}
|
||
|
||
function errorMessage(e: unknown) {
|
||
if (typeof e === 'object' && e !== null && 'message' in e) {
|
||
return String((e as { message?: unknown }).message)
|
||
}
|
||
return ''
|
||
}
|
||
|
||
function ratioText(value: number) {
|
||
const ratio = Number(value || 0)
|
||
if (ratio <= 0) return '-'
|
||
const rounded = Math.round(ratio * 100) / 100
|
||
return `1:${Number.isInteger(rounded) ? rounded : rounded.toFixed(2)}`
|
||
}
|
||
|
||
function normalizeCent(value: number | undefined | null) {
|
||
const cent = Number(value || 0)
|
||
if (!Number.isFinite(cent)) return 0
|
||
return Math.max(Math.round(cent), 0)
|
||
}
|
||
|
||
function effectiveProfitCent(row: AdminPickup) {
|
||
return Number.isFinite(row.effective_profit_amount_cent)
|
||
? row.effective_profit_amount_cent
|
||
: row.profit_amount_cent
|
||
}
|
||
|
||
function effectiveSettleCent(row: AdminPickup) {
|
||
return Number.isFinite(row.effective_settle_amount_cent)
|
||
? row.effective_settle_amount_cent
|
||
: row.settle_amount_cent
|
||
}
|
||
|
||
function listingOwnerTotalCent(item: AvailableListing) {
|
||
return normalizeCent(item.owner_total_price_cent || item.owner_price_cent)
|
||
}
|
||
|
||
function isExternalSource(source: PickupAccountSource) {
|
||
return source === 'external_platform_managed'
|
||
}
|
||
|
||
function pickupSourceLabel(source: PickupAccountSource) {
|
||
return isExternalSource(source) ? '外部上传 · 平台代管' : '站内上传'
|
||
}
|
||
|
||
function pickupSourceTagType(source: PickupAccountSource) {
|
||
return isExternalSource(source) ? 'primary' : 'info'
|
||
}
|
||
|
||
function isPlatformManaged(row: AdminPickup) {
|
||
return row.settlement_mode === 'platform_managed'
|
||
}
|
||
|
||
function offlineSettlementLabel(row: AdminPickup) {
|
||
if (!isPlatformManaged(row)) return '钱包已结算'
|
||
if (row.offline_settlement_status === 'pending') return '待线下结算'
|
||
if (row.offline_settlement_status === 'settled') return '已线下结算'
|
||
return '待完成提号'
|
||
}
|
||
|
||
function offlineSettlementTagType(row: AdminPickup) {
|
||
if (!isPlatformManaged(row)) return 'success'
|
||
if (row.offline_settlement_status === 'pending') return 'warning'
|
||
if (row.offline_settlement_status === 'settled') return 'success'
|
||
return 'info'
|
||
}
|
||
|
||
function listingSourceText(item: AvailableListing) {
|
||
const label = pickupSourceLabel(item.account_source)
|
||
return item.source_channel ? `${label} · ${item.source_channel}` : label
|
||
}
|
||
|
||
function listingOptionLabel(item: AvailableListing) {
|
||
return `${listingSourceText(item)} · ${item.listing_no} · ${item.account_title} · ${formatCentWithSymbol(listingOwnerTotalCent(item))} / ${formatCentWithSymbol(item.listing_price_cent)}`
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<section class="page">
|
||
<div class="page-header-row">
|
||
<div class="page-header">
|
||
<p class="eyebrow">Admin Pickup</p>
|
||
<h1>线下提号</h1>
|
||
<p>管理员线下提号,站内上传结算到钱包,平台代管通过线下结算确认打款。</p>
|
||
</div>
|
||
<div class="toolbar-actions">
|
||
<el-button type="primary" :icon="Plus" @click="openCreateDialog">创建提号</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="pickup-filter-bar">
|
||
<el-form inline @submit.prevent>
|
||
<el-form-item label="关键词" class="pickup-keyword-filter">
|
||
<el-input
|
||
v-model="filters.keyword"
|
||
placeholder="提号编号 / 上架编号 / 账号标题 / 手机号"
|
||
clearable
|
||
@keyup.enter="handleSearch"
|
||
@clear="handleSearch"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="提号状态">
|
||
<el-select v-model="filters.status" clearable placeholder="全部" @change="handleSearch">
|
||
<el-option label="提号中" value="picking_up" />
|
||
<el-option label="已完成" value="completed" />
|
||
<el-option label="已取消" value="cancelled" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="店铺">
|
||
<el-select
|
||
v-model="filters.shop_name"
|
||
filterable
|
||
clearable
|
||
placeholder="全部"
|
||
@change="handleSearch"
|
||
>
|
||
<el-option v-for="name in shopOptions" :key="name" :label="name" :value="name" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="账号来源">
|
||
<el-select
|
||
v-model="filters.account_source"
|
||
clearable
|
||
placeholder="全部"
|
||
@change="handleSearch"
|
||
>
|
||
<el-option label="外部上传 · 平台代管" value="external_platform_managed" />
|
||
<el-option label="站内上传" value="internal" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="线下结算">
|
||
<el-select
|
||
v-model="filters.offline_settlement_status"
|
||
clearable
|
||
placeholder="全部"
|
||
@change="handleSearch"
|
||
>
|
||
<el-option label="待确认" value="pending" />
|
||
<el-option label="已确认" value="settled" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item class="pickup-filter-actions">
|
||
<el-button type="primary" :icon="Search" @click="handleSearch">查询</el-button>
|
||
<el-button @click="resetFilters">重置</el-button>
|
||
<el-button :icon="Refresh" @click="loadList">刷新</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</div>
|
||
|
||
<el-table v-loading="loading" :data="items" class="table-panel">
|
||
<el-table-column prop="pickup_no" label="提号编号" width="190" />
|
||
<el-table-column prop="account_title" label="账号" min-width="180" />
|
||
<el-table-column label="区服/平台" width="150">
|
||
<template #default="{ row }"
|
||
>{{ row.server_region || '-' }} / {{ row.login_platform || '-' }}</template
|
||
>
|
||
</el-table-column>
|
||
<el-table-column label="上架编号" width="120">
|
||
<template #default="{ row }">{{ row.listing_no }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="卖家" width="140">
|
||
<template #default="{ row }">{{ row.owner_phone }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="账号来源" width="180">
|
||
<template #default="{ row }">
|
||
<div class="source-cell">
|
||
<el-tag :type="pickupSourceTagType(row.account_source)" effect="light" size="small">
|
||
{{ pickupSourceLabel(row.account_source) }}
|
||
</el-tag>
|
||
<small v-if="row.source_channel">{{ row.source_channel }}</small>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="交易渠道" width="110">
|
||
<template #default="{ row }">{{ row.platform || '-' }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="店铺名称" min-width="130">
|
||
<template #default="{ row }">{{ row.shop_name || '-' }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="结算金额" width="130">
|
||
<template #default="{ row }">
|
||
<span v-if="row.status === 'completed'">{{
|
||
formatCentWithSymbol(effectiveSettleCent(row))
|
||
}}</span>
|
||
<span v-else class="text-muted">待结算</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="线下利润" width="120">
|
||
<template #default="{ row }">{{ formatCentWithSymbol(effectiveProfitCent(row)) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="状态" width="100">
|
||
<template #default="{ row }">
|
||
<el-tag :type="statusType(row.status)" effect="light">
|
||
{{ pickupStatusLabel(row.status) }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="结算状态" width="125">
|
||
<template #default="{ row }">
|
||
<el-tag :type="offlineSettlementTagType(row)" effect="light">
|
||
{{ offlineSettlementLabel(row) }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="创建时间" width="170">
|
||
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="340" fixed="right">
|
||
<template #default="{ row }">
|
||
<div class="action-buttons">
|
||
<RouterLink :to="adminPath(`pickup/${row.id}`)">
|
||
<el-button size="small">详情</el-button>
|
||
</RouterLink>
|
||
<el-button
|
||
v-if="row.status === 'picking_up'"
|
||
type="success"
|
||
size="small"
|
||
plain
|
||
:icon="EditPen"
|
||
@click="openProfitDialog(row)"
|
||
>
|
||
改利润
|
||
</el-button>
|
||
<el-button
|
||
v-if="row.status === 'picking_up'"
|
||
type="primary"
|
||
size="small"
|
||
@click="openCompleteDialog(row)"
|
||
>
|
||
完成提号
|
||
</el-button>
|
||
<el-button
|
||
v-if="row.status === 'completed' && row.offline_settlement_status === 'pending'"
|
||
type="primary"
|
||
size="small"
|
||
plain
|
||
@click="handleConfirmOfflineSettlement(row)"
|
||
>
|
||
确认线下结算
|
||
</el-button>
|
||
<el-button
|
||
v-if="row.status === 'completed'"
|
||
size="small"
|
||
plain
|
||
:icon="EditPen"
|
||
@click="openFinancialAdjustmentDialog(row)"
|
||
>
|
||
财务调整
|
||
</el-button>
|
||
<el-button
|
||
v-if="row.status === 'picking_up'"
|
||
type="danger"
|
||
size="small"
|
||
plain
|
||
@click="handleCancel(row.id)"
|
||
>
|
||
取消
|
||
</el-button>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<div class="pagination-wrapper">
|
||
<el-pagination
|
||
:current-page="filters.page"
|
||
:page-size="filters.page_size"
|
||
:page-sizes="[20, 50, 100]"
|
||
:total="total"
|
||
layout="total, sizes, prev, pager, next, jumper"
|
||
@size-change="handleSizeChange"
|
||
@current-change="handlePageChange"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 创建提号 -->
|
||
<el-dialog
|
||
v-model="createDialogVisible"
|
||
title="创建线下提号"
|
||
width="min(640px, calc(100vw - 32px))"
|
||
>
|
||
<el-form label-width="110px">
|
||
<el-form-item label="账号来源">
|
||
<el-radio-group
|
||
v-model="listingSourceType"
|
||
size="small"
|
||
@change="handleListingSourceChange"
|
||
>
|
||
<el-radio-button value="">全部</el-radio-button>
|
||
<el-radio-button value="external">外部上传 · 平台代管</el-radio-button>
|
||
<el-radio-button value="internal">站内上传</el-radio-button>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<el-form-item label="选择账号" required>
|
||
<el-select
|
||
v-model="createForm.listing_id"
|
||
filterable
|
||
remote
|
||
clearable
|
||
:remote-method="searchListings"
|
||
:loading="listingLoading"
|
||
placeholder="输入上架编号或账号标题搜索"
|
||
style="width: 100%"
|
||
>
|
||
<el-option
|
||
v-for="item in listingOptions"
|
||
:key="item.id"
|
||
:label="listingOptionLabel(item)"
|
||
:value="item.id"
|
||
>
|
||
<div class="listing-option">
|
||
<el-tag
|
||
:type="pickupSourceTagType(item.account_source)"
|
||
effect="light"
|
||
size="small"
|
||
>
|
||
{{ pickupSourceLabel(item.account_source) }}
|
||
</el-tag>
|
||
<span class="listing-option-title"
|
||
>{{ item.listing_no }} · {{ item.account_title }}</span
|
||
>
|
||
<span class="listing-option-price">
|
||
{{ formatCentWithSymbol(listingOwnerTotalCent(item)) }} /
|
||
{{ formatCentWithSymbol(item.listing_price_cent) }}
|
||
</span>
|
||
</div>
|
||
</el-option>
|
||
</el-select>
|
||
<div class="form-hint">仅显示已发布、已审核、未在交易中的账号</div>
|
||
</el-form-item>
|
||
<el-form-item v-if="selectedListing" label="来源确认">
|
||
<div class="selected-source">
|
||
<el-tag :type="pickupSourceTagType(selectedListing.account_source)" effect="light">
|
||
{{ pickupSourceLabel(selectedListing.account_source) }}
|
||
</el-tag>
|
||
<span v-if="selectedListing.source_channel">
|
||
来源渠道:{{ selectedListing.source_channel }}
|
||
</span>
|
||
<span v-if="selectedListing.is_external_upload">
|
||
交接:{{
|
||
selectedListing.handoff_mode === 'platform'
|
||
? '平台代管'
|
||
: selectedListing.handoff_mode
|
||
}}
|
||
</span>
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item v-if="selectedListing && selectedPriceSnapshot" label="价格快照">
|
||
<div class="price-snapshot">
|
||
<span>纯比价格 {{ formatCentWithSymbol(selectedPriceSnapshot.pureCent) }}</span>
|
||
<span>额外物品价格 {{ formatCentWithSymbol(selectedPriceSnapshot.extraCent) }}</span>
|
||
<span>号主总价 {{ formatCentWithSymbol(selectedPriceSnapshot.totalCent) }}</span>
|
||
<span>网站售价 {{ formatCentWithSymbol(selectedListing.listing_price_cent) }}</span>
|
||
<span>网站加价 {{ formatCentWithSymbol(selectedListing.website_profit_cent) }}</span>
|
||
<span>回收 {{ ratioText(selectedListing.seller_ratio) }}</span>
|
||
<span>售卖 {{ ratioText(selectedListing.buyer_ratio) }}</span>
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="交易渠道">
|
||
<el-input v-model="createForm.platform" placeholder="如 微信 / QQ / 闲鱼" />
|
||
</el-form-item>
|
||
<el-form-item label="店铺名称">
|
||
<el-select
|
||
v-model="createForm.shop_name"
|
||
filterable
|
||
allow-create
|
||
default-first-option
|
||
clearable
|
||
:loading="shopOptionsLoading"
|
||
placeholder="选择历史店铺或输入新店铺"
|
||
style="width: 100%"
|
||
>
|
||
<el-option v-for="name in shopOptions" :key="name" :label="name" :value="name" />
|
||
</el-select>
|
||
<div class="form-hint">首次使用的店铺可直接输入名称创建</div>
|
||
</el-form-item>
|
||
<el-form-item label="线下利润(元)">
|
||
<el-input-number
|
||
v-model="createForm.profit_amount"
|
||
:min="0"
|
||
:step="1"
|
||
:precision="2"
|
||
controls-position="right"
|
||
style="width: 100%"
|
||
/>
|
||
<div class="form-hint">电商平台实际成交利润,财务统计按该金额汇总</div>
|
||
</el-form-item>
|
||
<el-form-item label="备注">
|
||
<el-input
|
||
v-model="createForm.remark"
|
||
type="textarea"
|
||
:rows="3"
|
||
placeholder="线下交易说明(可选)"
|
||
/>
|
||
</el-form-item>
|
||
<el-alert type="info" :closable="false">
|
||
创建后账号将被锁定为"提号中",卖家收到通知;不会立即结算,需手动完成结算。
|
||
</el-alert>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="createDialogVisible = false">取消</el-button>
|
||
<el-button type="primary" :loading="loading" @click="handleCreate">创建提号</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 完成提号 -->
|
||
<el-dialog v-model="completeDialogVisible" title="完成提号" width="480px">
|
||
<el-form label-width="110px">
|
||
<el-form-item label="结算金额(元)" required>
|
||
<el-input-number
|
||
v-model="completeForm.settle_amount"
|
||
:min="0.01"
|
||
:step="1"
|
||
:precision="2"
|
||
controls-position="right"
|
||
style="width: 100%"
|
||
/>
|
||
<div class="form-hint">
|
||
{{
|
||
activePickup?.settlement_mode === 'platform_managed'
|
||
? '记录待线下支付的结算金额'
|
||
: '给卖家钱包增加的可用余额'
|
||
}}
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="线下利润(元)">
|
||
<el-input-number
|
||
v-model="completeForm.profit_amount"
|
||
:min="0"
|
||
:step="1"
|
||
:precision="2"
|
||
controls-position="right"
|
||
style="width: 100%"
|
||
/>
|
||
<div class="form-hint">可在结算时修正为最终利润,财务统计使用该值</div>
|
||
</el-form-item>
|
||
<el-form-item label="完成备注">
|
||
<el-input
|
||
v-model="completeForm.complete_remark"
|
||
type="textarea"
|
||
:rows="3"
|
||
placeholder="完成说明(可选)"
|
||
/>
|
||
</el-form-item>
|
||
<el-alert type="warning" :closable="false">
|
||
完成后账号下架不可再上架。站内上传会立即入账卖家钱包;平台代管会转为待线下结算,实际打款后需单独确认。
|
||
</el-alert>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="completeDialogVisible = false">取消</el-button>
|
||
<el-button type="primary" :loading="loading" @click="handleComplete">确认完成</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<el-dialog v-model="profitDialogVisible" title="修改线下利润" width="460px">
|
||
<el-form label-width="110px">
|
||
<el-form-item label="线下利润(元)" required>
|
||
<el-input-number
|
||
v-model="profitForm.profit_amount"
|
||
:min="0"
|
||
:step="1"
|
||
:precision="2"
|
||
controls-position="right"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="修改原因">
|
||
<el-input
|
||
v-model="profitForm.reason"
|
||
type="textarea"
|
||
:rows="3"
|
||
maxlength="255"
|
||
show-word-limit
|
||
placeholder="可选"
|
||
/>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="profitDialogVisible = false">取消</el-button>
|
||
<el-button type="primary" :loading="profitSaving" @click="handleUpdateProfit">
|
||
保存
|
||
</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<el-dialog v-model="financialAdjustmentDialogVisible" title="完成后财务调整" width="500px">
|
||
<el-form label-width="120px">
|
||
<el-form-item label="目标利润(元)">
|
||
<el-input-number
|
||
v-model="financialAdjustmentForm.profit_amount"
|
||
:min="0"
|
||
:step="1"
|
||
:precision="2"
|
||
controls-position="right"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="目标打款金额(元)">
|
||
<el-input-number
|
||
v-model="financialAdjustmentForm.settle_amount"
|
||
:min="0"
|
||
:step="1"
|
||
:precision="2"
|
||
controls-position="right"
|
||
style="width: 100%"
|
||
/>
|
||
<div class="form-hint">
|
||
{{
|
||
activePickup?.settlement_mode === 'platform_managed'
|
||
? '代管账号会生成待线下补款或待追回记录'
|
||
: '增加金额会立即补入号主钱包,减少金额会生成待追回记录'
|
||
}}
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="调整原因" required>
|
||
<el-input
|
||
v-model="financialAdjustmentForm.reason"
|
||
type="textarea"
|
||
:rows="3"
|
||
maxlength="255"
|
||
show-word-limit
|
||
placeholder="请输入调整原因"
|
||
/>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="financialAdjustmentDialogVisible = false">取消</el-button>
|
||
<el-button
|
||
type="primary"
|
||
:loading="financialAdjustmentSaving"
|
||
@click="handleFinancialAdjustment"
|
||
>
|
||
确认调整
|
||
</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
</section>
|
||
</template>
|
||
|
||
<style scoped src="./AdminPickupView.css"></style>
|