区分线下提号账号来源
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import { apiClient } from '@/shared/api/client'
|
||||
import type { ApiResponse, PaginatedResult } from '@/shared/types/types'
|
||||
|
||||
export type PickupAccountSource = 'internal' | 'external_platform_managed'
|
||||
export type AvailableListingSourceType = '' | 'external' | 'internal'
|
||||
|
||||
export interface AdminPickup {
|
||||
id: number
|
||||
pickup_no: string
|
||||
@@ -15,6 +18,8 @@ export interface AdminPickup {
|
||||
admin_id: number
|
||||
platform: string
|
||||
shop_name: string
|
||||
account_source: PickupAccountSource
|
||||
source_channel: string
|
||||
listing_price_cent: number
|
||||
owner_price_cent: number
|
||||
website_profit_cent: number
|
||||
@@ -48,6 +53,11 @@ export interface AvailableListing {
|
||||
website_profit_cent: number
|
||||
seller_ratio: number
|
||||
buyer_ratio: number
|
||||
account_source: PickupAccountSource
|
||||
is_external_upload: boolean
|
||||
source_channel: string
|
||||
handoff_mode: string
|
||||
settlement_mode: string
|
||||
}
|
||||
|
||||
export interface AdminPickupCreateRequest {
|
||||
@@ -75,6 +85,7 @@ export interface AdminPickupListQuery {
|
||||
keyword?: string
|
||||
status?: string
|
||||
shop_name?: string
|
||||
account_source?: PickupAccountSource | ''
|
||||
}
|
||||
|
||||
function cleanParams(query: object) {
|
||||
@@ -109,10 +120,15 @@ export async function fetchAdminPickupShopOptions(platform = '') {
|
||||
return Array.isArray(data.data) ? data.data : []
|
||||
}
|
||||
|
||||
export async function fetchAvailableListings(keyword: string, page = 1, page_size = 20) {
|
||||
export async function fetchAvailableListings(
|
||||
keyword = '',
|
||||
page = 1,
|
||||
page_size = 20,
|
||||
source_type: AvailableListingSourceType = ''
|
||||
) {
|
||||
const { data } = await apiClient.get<ApiResponse<PaginatedResult<AvailableListing>>>(
|
||||
'/admin/pickups/available-listings',
|
||||
{ params: cleanParams({ keyword, page, page_size }) }
|
||||
{ params: cleanParams({ keyword, page, page_size, source_type }) }
|
||||
)
|
||||
const result = data.data
|
||||
return {
|
||||
|
||||
@@ -149,6 +149,14 @@ function statusType(status: string) {
|
||||
return ''
|
||||
}
|
||||
|
||||
function pickupSourceLabel(source: AdminPickup['account_source']) {
|
||||
return source === 'external_platform_managed' ? '外部上传 · 平台代管' : '站内上传'
|
||||
}
|
||||
|
||||
function pickupSourceTagType(source: AdminPickup['account_source']) {
|
||||
return source === 'external_platform_managed' ? 'primary' : 'info'
|
||||
}
|
||||
|
||||
function ratioText(value: number) {
|
||||
const ratio = Number(value || 0)
|
||||
if (ratio <= 0) return '-'
|
||||
@@ -296,6 +304,19 @@ function readSnapshotResources(summary: Record<string, unknown> | null): Snapsho
|
||||
<dt>号主</dt>
|
||||
<dd>{{ pickup.owner_phone || pickup.owner_id }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>账号来源</dt>
|
||||
<dd class="source-detail">
|
||||
<el-tag
|
||||
:type="pickupSourceTagType(pickup.account_source)"
|
||||
effect="light"
|
||||
size="small"
|
||||
>
|
||||
{{ pickupSourceLabel(pickup.account_source) }}
|
||||
</el-tag>
|
||||
<span v-if="pickup.source_channel">{{ pickup.source_channel }}</span>
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>交易渠道</dt>
|
||||
<dd>{{ pickup.platform || '-' }}</dd>
|
||||
@@ -518,6 +539,13 @@ function readSnapshotResources(summary: Record<string, unknown> | null): Snapsho
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.source-detail {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 6px 8px;
|
||||
}
|
||||
|
||||
.money-list {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
updateAdminPickupProfit,
|
||||
type AdminPickup,
|
||||
type AvailableListing,
|
||||
type AvailableListingSourceType,
|
||||
type PickupAccountSource,
|
||||
} from '@/features/admin/api/adminPickup'
|
||||
import { centToYuan, formatCentWithSymbol, yuanToCent } from '@/shared/utils/money'
|
||||
import { formatDateTime } from '@/shared/utils/time'
|
||||
@@ -21,7 +23,14 @@ 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: '' })
|
||||
const filters = reactive({
|
||||
page: 1,
|
||||
page_size: 20,
|
||||
keyword: '',
|
||||
status: '',
|
||||
shop_name: '',
|
||||
account_source: '' as PickupAccountSource | '',
|
||||
})
|
||||
|
||||
const createDialogVisible = ref(false)
|
||||
const completeDialogVisible = ref(false)
|
||||
@@ -41,6 +50,9 @@ const profitForm = reactive({ profit_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(
|
||||
@@ -99,7 +111,10 @@ function openCreateDialog() {
|
||||
createForm.profit_amount = 0
|
||||
createForm.remark = ''
|
||||
listingOptions.value = []
|
||||
listingKeyword.value = ''
|
||||
listingSourceType.value = ''
|
||||
createDialogVisible.value = true
|
||||
void searchListings('')
|
||||
}
|
||||
|
||||
async function loadShopOptions() {
|
||||
@@ -112,19 +127,32 @@ async function loadShopOptions() {
|
||||
}
|
||||
|
||||
async function searchListings(keyword: string) {
|
||||
if (!keyword) {
|
||||
listingOptions.value = []
|
||||
return
|
||||
}
|
||||
listingKeyword.value = keyword.trim()
|
||||
const requestID = ++listingRequestID
|
||||
listingLoading.value = true
|
||||
try {
|
||||
const result = await fetchAvailableListings(keyword)
|
||||
listingOptions.value = result.items
|
||||
const result = await fetchAvailableListings(
|
||||
listingKeyword.value,
|
||||
1,
|
||||
20,
|
||||
listingSourceType.value
|
||||
)
|
||||
if (requestID === listingRequestID) {
|
||||
listingOptions.value = result.items
|
||||
}
|
||||
} finally {
|
||||
listingLoading.value = false
|
||||
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('请选择要提号的账号')
|
||||
@@ -269,6 +297,27 @@ function normalizeCent(value: number | undefined | null) {
|
||||
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 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>
|
||||
@@ -306,6 +355,15 @@ function listingOwnerTotalCent(item: AvailableListing) {
|
||||
>
|
||||
<el-option v-for="name in shopOptions" :key="name" :label="name" :value="name" />
|
||||
</el-select>
|
||||
<el-select
|
||||
v-model="filters.account_source"
|
||||
placeholder="账号来源"
|
||||
clearable
|
||||
style="width: 190px"
|
||||
>
|
||||
<el-option label="外部上传 · 平台代管" value="external_platform_managed" />
|
||||
<el-option label="站内上传" value="internal" />
|
||||
</el-select>
|
||||
<el-button type="primary" :icon="Search" @click="handleSearch">查询</el-button>
|
||||
<el-button :icon="Refresh" @click="loadList">刷新</el-button>
|
||||
</div>
|
||||
@@ -324,6 +382,16 @@ function listingOwnerTotalCent(item: AvailableListing) {
|
||||
<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>
|
||||
@@ -402,8 +470,23 @@ function listingOwnerTotalCent(item: AvailableListing) {
|
||||
</div>
|
||||
|
||||
<!-- 创建提号 -->
|
||||
<el-dialog v-model="createDialogVisible" title="创建线下提号" width="520px">
|
||||
<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"
|
||||
@@ -418,12 +501,46 @@ function listingOwnerTotalCent(item: AvailableListing) {
|
||||
<el-option
|
||||
v-for="item in listingOptions"
|
||||
:key="item.id"
|
||||
:label="`${item.listing_no} · ${item.account_title} · ${formatCentWithSymbol(listingOwnerTotalCent(item))} / ${formatCentWithSymbol(item.listing_price_cent)}`"
|
||||
: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>
|
||||
@@ -575,6 +692,41 @@ function listingOwnerTotalCent(item: AvailableListing) {
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.source-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 3px;
|
||||
}
|
||||
.source-cell small {
|
||||
color: #64748b;
|
||||
}
|
||||
.listing-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
.listing-option-title {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.listing-option-price {
|
||||
flex: none;
|
||||
color: #64748b;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.selected-source {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px 12px;
|
||||
color: #475569;
|
||||
font-size: 13px;
|
||||
}
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
Reference in New Issue
Block a user