优化商品管理筛选功能
This commit is contained in:
@@ -36,6 +36,10 @@ const filters = reactive<AdminListingQuery>({
|
||||
load: '',
|
||||
skin_group: '',
|
||||
skin_name: '',
|
||||
min_coin: undefined,
|
||||
max_coin: undefined,
|
||||
min_deposit: undefined,
|
||||
max_deposit: undefined,
|
||||
})
|
||||
|
||||
type FilterPopover = 'asset' | 'skin'
|
||||
@@ -148,6 +152,10 @@ function resetFilters() {
|
||||
filters.owner_id = ''
|
||||
filters.status = ''
|
||||
filters.review_status = ''
|
||||
filters.min_coin = undefined
|
||||
filters.max_coin = undefined
|
||||
filters.min_deposit = undefined
|
||||
filters.max_deposit = undefined
|
||||
clearAssetFilters(false)
|
||||
clearSkinFilters(false)
|
||||
void queryListings()
|
||||
@@ -551,32 +559,63 @@ function formatQuantity(value: number) {
|
||||
|
||||
<!-- 筛选和操作行 -->
|
||||
<div class="listing-filter-row">
|
||||
<el-form class="listing-filter-bar" inline>
|
||||
<el-form-item label="编号">
|
||||
<el-form class="listing-filter-bar" label-position="top">
|
||||
<el-form-item class="filter-keyword" label="编号">
|
||||
<el-input
|
||||
v-model="filters.keyword"
|
||||
clearable
|
||||
placeholder="商品编号 / 订单编号"
|
||||
style="width: 210px"
|
||||
@keyup.enter="queryListings"
|
||||
@clear="queryListings"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="号主 ID">
|
||||
<el-input
|
||||
v-model="filters.owner_id"
|
||||
clearable
|
||||
placeholder="筛选号主"
|
||||
style="width: 160px"
|
||||
/>
|
||||
<el-form-item class="filter-owner" label="号主 ID">
|
||||
<el-input v-model="filters.owner_id" clearable placeholder="筛选号主" />
|
||||
</el-form-item>
|
||||
<el-form-item label="商品状态">
|
||||
<el-select
|
||||
v-model="filters.status"
|
||||
clearable
|
||||
placeholder="全部状态"
|
||||
style="width: 140px"
|
||||
>
|
||||
<el-form-item class="filter-range" label="纯币(M)">
|
||||
<div class="admin-range-filter">
|
||||
<el-input-number
|
||||
v-model="filters.min_coin"
|
||||
:min="0"
|
||||
:precision="1"
|
||||
:step="10"
|
||||
:controls="false"
|
||||
placeholder="最低"
|
||||
/>
|
||||
<span class="admin-range-separator">-</span>
|
||||
<el-input-number
|
||||
v-model="filters.max_coin"
|
||||
:min="0"
|
||||
:precision="1"
|
||||
:step="10"
|
||||
:controls="false"
|
||||
placeholder="最高"
|
||||
/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item class="filter-range" label="押金(元)">
|
||||
<div class="admin-range-filter">
|
||||
<el-input-number
|
||||
v-model="filters.min_deposit"
|
||||
:min="0"
|
||||
:precision="1"
|
||||
:step="100"
|
||||
:controls="false"
|
||||
placeholder="最低"
|
||||
/>
|
||||
<span class="admin-range-separator">-</span>
|
||||
<el-input-number
|
||||
v-model="filters.max_deposit"
|
||||
:min="0"
|
||||
:precision="1"
|
||||
:step="100"
|
||||
:controls="false"
|
||||
placeholder="最高"
|
||||
/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item class="filter-select" label="商品状态">
|
||||
<el-select v-model="filters.status" clearable placeholder="全部状态">
|
||||
<el-option label="草稿" value="draft" />
|
||||
<el-option label="已上架" value="published" />
|
||||
<el-option label="已锁定" value="rented" />
|
||||
@@ -585,20 +624,15 @@ function formatQuantity(value: number) {
|
||||
<el-option label="异常" value="abnormal" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="审核状态">
|
||||
<el-select
|
||||
v-model="filters.review_status"
|
||||
clearable
|
||||
placeholder="全部审核"
|
||||
style="width: 140px"
|
||||
>
|
||||
<el-form-item class="filter-select" label="审核状态">
|
||||
<el-select v-model="filters.review_status" clearable placeholder="全部审核">
|
||||
<el-option label="未提交" value="none" />
|
||||
<el-option label="待审核" value="pending" />
|
||||
<el-option label="已通过" value="approved" />
|
||||
<el-option label="已拒绝" value="rejected" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="资产">
|
||||
<el-form-item class="filter-select" label="资产">
|
||||
<el-popover
|
||||
:visible="activeFilterPopover === 'asset'"
|
||||
trigger="click"
|
||||
@@ -692,7 +726,7 @@ function formatQuantity(value: number) {
|
||||
</div>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
<el-form-item label="皮肤">
|
||||
<el-form-item class="filter-skin" label="皮肤">
|
||||
<el-popover
|
||||
:visible="activeFilterPopover === 'skin'"
|
||||
trigger="click"
|
||||
|
||||
@@ -171,6 +171,10 @@ export interface AdminListingQuery {
|
||||
load?: string
|
||||
skin_group?: string
|
||||
skin_name?: string
|
||||
min_coin?: number
|
||||
max_coin?: number
|
||||
min_deposit?: number
|
||||
max_deposit?: number
|
||||
limit?: number
|
||||
page?: number
|
||||
page_size?: number
|
||||
@@ -185,7 +189,9 @@ export interface AdminListingPage {
|
||||
|
||||
export async function fetchAdminListings(query: AdminListingQuery = {}) {
|
||||
const params = Object.fromEntries(
|
||||
Object.entries(query).filter(([, value]) => value !== '' && value !== undefined)
|
||||
Object.entries(query).filter(
|
||||
([, value]) => value !== '' && value !== undefined && value !== null
|
||||
)
|
||||
)
|
||||
const { data } = await apiClient.get<ApiResponse<AdminListingPage>>('/admin/listings', { params })
|
||||
return normalizeAdminListingPage(data.data, query)
|
||||
|
||||
@@ -131,6 +131,7 @@ const detailScreenshots = computed(() => {
|
||||
url,
|
||||
}))
|
||||
})
|
||||
const detailScreenshotUrls = computed(() => detailScreenshots.value.map(shot => shot.url))
|
||||
|
||||
function readGroupedScreenshots(item: Listing) {
|
||||
const groups = item.asset_summary?.screenshot_groups
|
||||
@@ -462,8 +463,20 @@ function handleToggleFavorite() {
|
||||
<span>{{ detailScreenshots.length }} 张</span>
|
||||
</div>
|
||||
<div class="detail-screenshot-grid">
|
||||
<figure v-for="shot in detailScreenshots" :key="shot.url" class="detail-screenshot">
|
||||
<AuthImage :source="shot.url" :alt="shot.label" />
|
||||
<figure
|
||||
v-for="(shot, index) in detailScreenshots"
|
||||
:key="shot.url"
|
||||
class="detail-screenshot"
|
||||
>
|
||||
<AuthImage
|
||||
:source="shot.url"
|
||||
:alt="shot.label"
|
||||
image-class="detail-screenshot-image"
|
||||
fallback-class="detail-screenshot-image"
|
||||
fit="cover"
|
||||
:preview-src-list="detailScreenshotUrls"
|
||||
:preview-initial-index="index"
|
||||
/>
|
||||
<figcaption>{{ shot.label }}</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
@@ -908,12 +921,20 @@ function handleToggleFavorite() {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.detail-screenshot img {
|
||||
.detail-screenshot-image {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 10;
|
||||
object-fit: cover;
|
||||
overflow: hidden;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #edf0f4;
|
||||
background: #f8fafc;
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
.detail-screenshot-image :deep(.el-image__inner) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.detail-screenshot figcaption {
|
||||
@@ -996,8 +1017,7 @@ function handleToggleFavorite() {
|
||||
margin-bottom: 0;
|
||||
border: 1px solid rgba(245, 158, 11, 0.2);
|
||||
background:
|
||||
linear-gradient(135deg, rgba(255, 251, 235, 0.96), rgba(255, 247, 237, 0.92)),
|
||||
#fffbeb;
|
||||
linear-gradient(135deg, rgba(255, 251, 235, 0.96), rgba(255, 247, 237, 0.92)), #fffbeb;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.86);
|
||||
}
|
||||
|
||||
|
||||
@@ -133,6 +133,7 @@ const detailScreenshots = computed(() => {
|
||||
url,
|
||||
}))
|
||||
})
|
||||
const detailScreenshotUrls = computed(() => detailScreenshots.value.map(shot => shot.url))
|
||||
|
||||
function readGroupedScreenshots(item: Listing) {
|
||||
const groups = item.asset_summary?.screenshot_groups
|
||||
@@ -328,7 +329,9 @@ function handleToggleFavorite() {
|
||||
:source="detailScreenshots[0].url"
|
||||
:alt="getListingTitle(listing)"
|
||||
image-class="cover-img"
|
||||
fit="cover"
|
||||
loading="eager"
|
||||
:preview-src-list="detailScreenshotUrls"
|
||||
/>
|
||||
<div v-else class="cover-placeholder">
|
||||
<van-icon name="photo-o" :size="40" color="#ccc" />
|
||||
@@ -449,8 +452,20 @@ function handleToggleFavorite() {
|
||||
<div v-if="detailScreenshots.length" class="info-card">
|
||||
<h3 class="card-subtitle">账号截图</h3>
|
||||
<div class="screenshot-grid">
|
||||
<figure v-for="shot in detailScreenshots" :key="shot.url" class="screenshot-item">
|
||||
<AuthImage :source="shot.url" :alt="shot.label" image-class="screenshot-thumb" />
|
||||
<figure
|
||||
v-for="(shot, index) in detailScreenshots"
|
||||
:key="shot.url"
|
||||
class="screenshot-item"
|
||||
>
|
||||
<AuthImage
|
||||
:source="shot.url"
|
||||
:alt="shot.label"
|
||||
image-class="screenshot-thumb"
|
||||
fallback-class="screenshot-thumb"
|
||||
fit="cover"
|
||||
:preview-src-list="detailScreenshotUrls"
|
||||
:preview-initial-index="index"
|
||||
/>
|
||||
<figcaption>{{ shot.label }}</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
@@ -630,6 +645,12 @@ function handleToggleFavorite() {
|
||||
}
|
||||
|
||||
.cover-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
.cover-img :deep(.el-image__inner) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
@@ -876,11 +897,18 @@ function handleToggleFavorite() {
|
||||
.screenshot-thumb {
|
||||
width: 100%;
|
||||
aspect-ratio: 4 / 3;
|
||||
object-fit: cover;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
background: #f8fafc;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.screenshot-thumb :deep(.el-image__inner) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.screenshot-item figcaption {
|
||||
margin-top: 4px;
|
||||
color: #6b7280;
|
||||
|
||||
Reference in New Issue
Block a user