feat: 优化后台商品管理与移动端个人中心
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { ArrowLeft, ArrowRight, DArrowLeft, DArrowRight } from '@element-plus/icons-vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
interface Props {
|
||||
currentPage: number
|
||||
@@ -23,6 +24,18 @@ const currentCount = computed(() => {
|
||||
const start = (props.currentPage - 1) * props.pageSize
|
||||
return Math.min(props.pageSize, props.total - start)
|
||||
})
|
||||
const jumpPage = ref<number | undefined>(props.currentPage)
|
||||
|
||||
watch(
|
||||
() => props.currentPage,
|
||||
page => {
|
||||
jumpPage.value = page
|
||||
}
|
||||
)
|
||||
|
||||
watch(totalPages, pages => {
|
||||
if (jumpPage.value && jumpPage.value > pages) jumpPage.value = pages
|
||||
})
|
||||
|
||||
function handleSizeChange(size: number) {
|
||||
emit('update:pageSize', size)
|
||||
@@ -36,36 +49,120 @@ function prevPage() {
|
||||
emit('pageChange')
|
||||
}
|
||||
|
||||
function firstPage() {
|
||||
if (props.currentPage <= 1) return
|
||||
emit('update:currentPage', 1)
|
||||
emit('pageChange')
|
||||
}
|
||||
|
||||
function nextPage() {
|
||||
if (props.currentPage >= totalPages.value) return
|
||||
emit('update:currentPage', props.currentPage + 1)
|
||||
emit('pageChange')
|
||||
}
|
||||
|
||||
function lastPage() {
|
||||
if (props.currentPage >= totalPages.value) return
|
||||
emit('update:currentPage', totalPages.value)
|
||||
emit('pageChange')
|
||||
}
|
||||
|
||||
function jumpToPage() {
|
||||
const requested = Number(jumpPage.value)
|
||||
if (!Number.isFinite(requested)) {
|
||||
jumpPage.value = props.currentPage
|
||||
return
|
||||
}
|
||||
const page = Math.min(Math.max(Math.trunc(requested), 1), totalPages.value)
|
||||
jumpPage.value = page
|
||||
if (page === props.currentPage) return
|
||||
emit('update:currentPage', page)
|
||||
emit('pageChange')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="table-pagination">
|
||||
<span class="pagination-summary">当前页 {{ currentCount }} 条,共 {{ total }} 条</span>
|
||||
<span class="pagination-summary">
|
||||
共 <strong>{{ total }}</strong> 条
|
||||
<em>本页 {{ currentCount }} 条</em>
|
||||
</span>
|
||||
<div class="pagination-controls">
|
||||
<span class="pagination-size-label">每页</span>
|
||||
<el-select
|
||||
:model-value="pageSize"
|
||||
class="page-size-select"
|
||||
size="small"
|
||||
:disabled="loading"
|
||||
@update:model-value="handleSizeChange"
|
||||
>
|
||||
<el-option :value="10" label="10条" />
|
||||
<el-option :value="20" label="20条" />
|
||||
<el-option :value="50" label="50条" />
|
||||
<el-option :value="100" label="100条" />
|
||||
<el-option :value="10" label="10 条" />
|
||||
<el-option :value="20" label="20 条" />
|
||||
<el-option :value="50" label="50 条" />
|
||||
<el-option :value="100" label="100 条" />
|
||||
</el-select>
|
||||
<span class="pagination-page">{{ currentPage }}/{{ totalPages }}页</span>
|
||||
<el-button size="small" :disabled="currentPage <= 1 || loading" @click="prevPage"
|
||||
>上页</el-button
|
||||
>
|
||||
<el-button size="small" :disabled="currentPage >= totalPages || loading" @click="nextPage"
|
||||
>下页</el-button
|
||||
>
|
||||
<div class="pagination-nav">
|
||||
<el-button
|
||||
class="pagination-nav-btn"
|
||||
size="small"
|
||||
:icon="DArrowLeft"
|
||||
:disabled="currentPage <= 1 || loading"
|
||||
aria-label="第一页"
|
||||
@click="firstPage"
|
||||
/>
|
||||
<el-button
|
||||
class="pagination-nav-btn"
|
||||
size="small"
|
||||
:icon="ArrowLeft"
|
||||
:disabled="currentPage <= 1 || loading"
|
||||
aria-label="上一页"
|
||||
@click="prevPage"
|
||||
/>
|
||||
<span class="pagination-page">
|
||||
<strong>{{ currentPage }}</strong>
|
||||
<i>/</i>
|
||||
{{ totalPages }}
|
||||
</span>
|
||||
<el-button
|
||||
class="pagination-nav-btn"
|
||||
size="small"
|
||||
:icon="ArrowRight"
|
||||
:disabled="currentPage >= totalPages || loading"
|
||||
aria-label="下一页"
|
||||
@click="nextPage"
|
||||
/>
|
||||
<el-button
|
||||
class="pagination-nav-btn"
|
||||
size="small"
|
||||
:icon="DArrowRight"
|
||||
:disabled="currentPage >= totalPages || loading"
|
||||
aria-label="最后一页"
|
||||
@click="lastPage"
|
||||
/>
|
||||
</div>
|
||||
<div class="pagination-jump">
|
||||
<span>前往</span>
|
||||
<el-input-number
|
||||
v-model="jumpPage"
|
||||
class="pagination-jump-input"
|
||||
size="small"
|
||||
:min="1"
|
||||
:max="totalPages"
|
||||
:precision="0"
|
||||
:controls="false"
|
||||
:disabled="loading || totalPages <= 1"
|
||||
@keyup.enter="jumpToPage"
|
||||
/>
|
||||
<span>页</span>
|
||||
<el-button
|
||||
class="pagination-jump-btn"
|
||||
size="small"
|
||||
:disabled="loading || totalPages <= 1"
|
||||
@click="jumpToPage"
|
||||
>
|
||||
跳转
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
type AdminListingQuery,
|
||||
type Listing,
|
||||
} from '@/features/listings'
|
||||
import AdminTablePagination from '@/features/admin/components/AdminTablePagination.vue'
|
||||
import { useAdminTable } from '@/features/admin/composables/useAdminTable'
|
||||
import { adminPath } from '@/shared/utils/adminPath'
|
||||
import { formatCentWithSymbol } from '@/shared/utils/money'
|
||||
@@ -79,14 +80,6 @@ const {
|
||||
|
||||
const listings = computed(() => listingPage.value.items)
|
||||
const totalListings = computed(() => listingPage.value.total)
|
||||
const publishedCount = computed(
|
||||
() => listings.value.filter(item => item.status === 'published').length
|
||||
)
|
||||
const rentedCount = computed(() => listings.value.filter(item => item.status === 'rented').length)
|
||||
const pendingCount = computed(
|
||||
() => listings.value.filter(item => item.review_status === 'pending').length
|
||||
)
|
||||
const totalPages = computed(() => Math.max(1, Math.ceil(totalListings.value / pageSize.value)))
|
||||
const insuranceOptions = computed(() =>
|
||||
publishOptions.value.insurance_options.length
|
||||
? publishOptions.value.insurance_options
|
||||
@@ -372,36 +365,6 @@ function levelSlotLabel(value: string) {
|
||||
return value ? value.replace(/级$/, '') : '-'
|
||||
}
|
||||
|
||||
function setStatusFilter(status: string) {
|
||||
filters.status = (filters.status === status ? '' : status) as AdminListingQuery['status']
|
||||
filters.review_status = ''
|
||||
void queryListings()
|
||||
}
|
||||
|
||||
function setReviewStatusFilter(reviewStatus: string) {
|
||||
filters.review_status = (
|
||||
filters.review_status === reviewStatus ? '' : reviewStatus
|
||||
) as AdminListingQuery['review_status']
|
||||
filters.status = ''
|
||||
void queryListings()
|
||||
}
|
||||
|
||||
async function handlePageSizeChange(size: number) {
|
||||
pageSize.value = size
|
||||
currentPage.value = 1
|
||||
await loadListings()
|
||||
}
|
||||
|
||||
async function prevPage() {
|
||||
currentPage.value = Math.max(1, currentPage.value - 1)
|
||||
await loadListings()
|
||||
}
|
||||
|
||||
async function nextPage() {
|
||||
currentPage.value = Math.min(totalPages.value, currentPage.value + 1)
|
||||
await loadListings()
|
||||
}
|
||||
|
||||
async function copyTableScreenshot() {
|
||||
try {
|
||||
const blob = await createTableScreenshotBlob()
|
||||
@@ -690,43 +653,25 @@ function formatQuantity(value: number) {
|
||||
</div>
|
||||
|
||||
<div class="listing-control-panel">
|
||||
<!-- 统计指标行 -->
|
||||
<div class="listing-stats-row">
|
||||
<div
|
||||
class="stat-card clickable"
|
||||
:class="{ active: !filters.status && !filters.review_status }"
|
||||
@click="resetFilters"
|
||||
>
|
||||
<span class="stat-label">全部商品</span>
|
||||
<strong class="stat-value">{{ totalListings }}</strong>
|
||||
<div class="listing-filter-toolbar">
|
||||
<div class="listing-filter-summary">
|
||||
<strong>筛选条件</strong>
|
||||
<span>
|
||||
共 <b>{{ totalListings }}</b> 件商品
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="stat-card clickable"
|
||||
:class="{ active: filters.status === 'published' }"
|
||||
@click="setStatusFilter('published')"
|
||||
>
|
||||
<span class="stat-label">已上架</span>
|
||||
<strong class="stat-value">{{ publishedCount }}</strong>
|
||||
</div>
|
||||
<div
|
||||
class="stat-card clickable"
|
||||
:class="{ active: filters.status === 'rented' }"
|
||||
@click="setStatusFilter('rented')"
|
||||
>
|
||||
<span class="stat-label">已锁定</span>
|
||||
<strong class="stat-value">{{ rentedCount }}</strong>
|
||||
</div>
|
||||
<div
|
||||
class="stat-card clickable"
|
||||
:class="{ active: filters.review_status === 'pending' }"
|
||||
@click="setReviewStatusFilter('pending')"
|
||||
>
|
||||
<span class="stat-label">待审核</span>
|
||||
<strong class="stat-value">{{ pendingCount }}</strong>
|
||||
<div class="listing-action-strip">
|
||||
<el-button @click="resetFilters">重置</el-button>
|
||||
<el-button type="primary" :icon="Search" :loading="loading" @click="queryListings"
|
||||
>查询</el-button
|
||||
>
|
||||
<el-button :disabled="!listings.length" @click="copyTableScreenshot">复制截图</el-button>
|
||||
<el-button :disabled="!listings.length" @click="downloadTableScreenshot"
|
||||
>下载截图</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选和操作行 -->
|
||||
<div class="listing-filter-row">
|
||||
<el-form class="listing-filter-bar" label-position="top">
|
||||
<el-form-item class="filter-keyword" label="编号">
|
||||
@@ -813,7 +758,7 @@ function formatQuantity(value: number) {
|
||||
<el-option label="极品专区" value="premium" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item class="filter-select" label="资产">
|
||||
<el-form-item class="filter-select filter-asset" label="资产">
|
||||
<el-popover
|
||||
:visible="activeFilterPopover === 'asset'"
|
||||
trigger="click"
|
||||
@@ -980,16 +925,6 @@ function formatQuantity(value: number) {
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="listing-action-strip">
|
||||
<el-button @click="resetFilters">重置</el-button>
|
||||
<el-button type="primary" :icon="Search" :loading="loading" @click="queryListings"
|
||||
>查询</el-button
|
||||
>
|
||||
<el-button :disabled="!listings.length" @click="copyTableScreenshot">复制截图</el-button>
|
||||
<el-button :disabled="!listings.length" @click="downloadTableScreenshot"
|
||||
>下载截图</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1095,29 +1030,13 @@ function formatQuantity(value: number) {
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="table-pagination">
|
||||
<span class="pagination-summary"
|
||||
>当前页 {{ listings.length }} 条,共 {{ totalListings }} 条</span
|
||||
>
|
||||
<div class="pagination-controls">
|
||||
<span class="pagination-size-label">每页</span>
|
||||
<el-select
|
||||
:model-value="pageSize"
|
||||
class="page-size-select"
|
||||
size="small"
|
||||
@update:model-value="handlePageSizeChange"
|
||||
>
|
||||
<el-option :value="10" label="10条" />
|
||||
<el-option :value="20" label="20条" />
|
||||
<el-option :value="50" label="50条" />
|
||||
<el-option :value="100" label="100条" />
|
||||
</el-select>
|
||||
<span class="pagination-page">{{ currentPage }}/{{ totalPages }}页</span>
|
||||
<el-button size="small" :disabled="currentPage <= 1" @click="prevPage">上页</el-button>
|
||||
<el-button size="small" :disabled="currentPage >= totalPages" @click="nextPage"
|
||||
>下页</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<AdminTablePagination
|
||||
v-if="totalListings > 0"
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:total="totalListings"
|
||||
:loading="loading"
|
||||
@page-change="loadListings"
|
||||
/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -12,31 +12,6 @@
|
||||
background: linear-gradient(180deg, #fff8f3 0%, #ffffff 82%);
|
||||
}
|
||||
|
||||
.hero-top-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
display: grid;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.app-title {
|
||||
font-size: 17px;
|
||||
font-weight: 900;
|
||||
color: #111827;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.hero-title small {
|
||||
color: #8b95a5;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.hero-setting-btn {
|
||||
display: grid;
|
||||
width: 34px;
|
||||
@@ -953,6 +928,528 @@
|
||||
min-width: 132px;
|
||||
}
|
||||
|
||||
/* ========== Compact mobile account center ========== */
|
||||
.profile-hero {
|
||||
position: relative;
|
||||
padding: 15px 16px 14px;
|
||||
border: 0;
|
||||
border-radius: 0 0 18px 18px;
|
||||
background: linear-gradient(165deg, #fff8f1 0%, #ffffff 76%);
|
||||
box-shadow: 0 5px 18px rgba(154, 89, 25, 0.045);
|
||||
}
|
||||
|
||||
.hero-setting-btn {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 16px;
|
||||
z-index: 1;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-color: #f2dfd0;
|
||||
}
|
||||
|
||||
.profile-hero-content {
|
||||
padding-right: 38px;
|
||||
}
|
||||
|
||||
.avatar-circle {
|
||||
width: 62px;
|
||||
height: 62px;
|
||||
border: 2px solid #ffffff;
|
||||
box-shadow:
|
||||
0 0 0 1px #f0d8c6,
|
||||
0 5px 12px rgba(185, 104, 34, 0.1);
|
||||
}
|
||||
|
||||
.user-nickname {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.user-meta {
|
||||
margin-top: 4px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.owner-quality-tag {
|
||||
border-color: #edcd91;
|
||||
background: #fff3da !important;
|
||||
color: #995b08 !important;
|
||||
}
|
||||
|
||||
.hero-status-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
margin-top: 13px;
|
||||
padding-top: 11px;
|
||||
border-top: 1px solid #f4e9df;
|
||||
}
|
||||
|
||||
.hero-level-pill,
|
||||
.hero-benefits-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 28px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.hero-level-pill {
|
||||
min-width: 0;
|
||||
gap: 5px;
|
||||
padding: 0 9px;
|
||||
border: 1px solid #f1dccb;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.82);
|
||||
}
|
||||
|
||||
.hero-level-pill span {
|
||||
color: #8d7c70;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.hero-level-pill strong {
|
||||
overflow: hidden;
|
||||
color: #b65308;
|
||||
font-size: 12px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.hero-level-pill .van-icon {
|
||||
flex: 0 0 auto;
|
||||
color: #b98e6d;
|
||||
}
|
||||
|
||||
.hero-level-pill.is-store {
|
||||
border-color: #ccebd9;
|
||||
background: #f3fbf6;
|
||||
}
|
||||
|
||||
.hero-level-pill.is-store strong {
|
||||
color: #13864c;
|
||||
}
|
||||
|
||||
.hero-benefits-btn {
|
||||
flex: 0 0 auto;
|
||||
gap: 2px;
|
||||
padding: 0;
|
||||
color: #7e8794;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.asset-overview-card {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
|
||||
margin: 12px 16px 0;
|
||||
overflow: hidden;
|
||||
border: 1px solid #eceff3;
|
||||
border-radius: 14px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 4px 14px rgba(15, 23, 42, 0.03);
|
||||
}
|
||||
|
||||
.asset-points {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
min-width: 0;
|
||||
padding: 13px 11px;
|
||||
border: 0;
|
||||
border-right: 1px solid #f0f2f5;
|
||||
background: linear-gradient(135deg, #fff9f3 0%, #ffffff 88%);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.asset-icon {
|
||||
display: grid;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
flex: 0 0 auto;
|
||||
place-items: center;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.points-icon {
|
||||
background: #ffeadc;
|
||||
color: #ef6200;
|
||||
}
|
||||
|
||||
.asset-copy {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.asset-copy small {
|
||||
color: #8b95a3;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.asset-copy strong {
|
||||
color: #252d3b;
|
||||
font-size: 18px;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.asset-copy em {
|
||||
overflow: hidden;
|
||||
color: #9aa2ae;
|
||||
font-size: 8px;
|
||||
font-style: normal;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.asset-arrow {
|
||||
color: #b9aea3;
|
||||
}
|
||||
|
||||
.asset-wallet {
|
||||
display: grid;
|
||||
align-content: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
padding: 12px 13px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.asset-wallet-head,
|
||||
.asset-wallet-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.asset-wallet-head > span {
|
||||
color: #8b95a3;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.asset-wallet-head button {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: #b27035;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.asset-wallet-main > strong {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
color: #222a37;
|
||||
font-size: 19px;
|
||||
line-height: 1.2;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.asset-withdraw {
|
||||
flex: 0 0 auto;
|
||||
min-height: 26px;
|
||||
padding: 0 9px;
|
||||
border: 1px solid #f3c8aa;
|
||||
border-radius: 999px;
|
||||
background: #fff8f2;
|
||||
color: #d95b00;
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.quality-entry-card {
|
||||
display: grid;
|
||||
gap: 9px;
|
||||
margin: 12px 16px 0;
|
||||
padding: 12px 13px;
|
||||
border: 1px solid #eee2d3;
|
||||
border-radius: 14px;
|
||||
background: linear-gradient(135deg, #fffaf4 0%, #ffffff 74%);
|
||||
box-shadow: 0 4px 14px rgba(134, 85, 34, 0.025);
|
||||
}
|
||||
|
||||
.quality-entry-card.is-active {
|
||||
border-color: #e6c17c;
|
||||
background: linear-gradient(135deg, #fff8e9 0%, #ffffff 74%);
|
||||
}
|
||||
|
||||
.quality-entry-head,
|
||||
.quality-entry-title,
|
||||
.quality-entry-actions,
|
||||
.quality-entry-active {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.quality-entry-head {
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.quality-entry-title {
|
||||
min-width: 0;
|
||||
gap: 9px;
|
||||
}
|
||||
|
||||
.quality-entry-icon {
|
||||
display: grid;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
flex: 0 0 auto;
|
||||
place-items: center;
|
||||
border: 1px solid #efd7b9;
|
||||
border-radius: 9px;
|
||||
background: #fff1df;
|
||||
color: #cf670c;
|
||||
}
|
||||
|
||||
.quality-entry-copy {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.quality-entry-copy span {
|
||||
color: #8c7c6e;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.quality-entry-copy strong {
|
||||
overflow: hidden;
|
||||
color: #2d3340;
|
||||
font-size: 13px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.quality-entry-card :deep(.van-tag) {
|
||||
flex: 0 0 auto;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.quality-entry-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px 10px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid #f3eadf;
|
||||
}
|
||||
|
||||
.quality-entry-summary span {
|
||||
color: #877a6e;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.quality-entry-summary button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
margin-left: auto;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: #bd681b;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.quality-entry-actions,
|
||||
.quality-entry-active {
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid #f3eadf;
|
||||
}
|
||||
|
||||
.quality-entry-actions > span,
|
||||
.quality-entry-active > span {
|
||||
min-width: 0;
|
||||
color: #8a8178;
|
||||
font-size: 9px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.quality-entry-actions > div {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.quality-entry-actions :deep(.van-button) {
|
||||
height: 28px;
|
||||
padding: 0 9px;
|
||||
font-size: 9px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.quality-cancel-link {
|
||||
flex: 0 0 auto;
|
||||
min-height: 26px;
|
||||
padding: 0 8px;
|
||||
border: 1px solid #f4c4cb;
|
||||
border-radius: 999px;
|
||||
background: #fff7f8;
|
||||
color: #c63d50;
|
||||
font-size: 9px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.quality-cancel-link:disabled {
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
.service-panel,
|
||||
.more-services-card {
|
||||
margin: 12px 16px 0;
|
||||
border: 1px solid #eceff3;
|
||||
border-radius: 14px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 4px 14px rgba(15, 23, 42, 0.03);
|
||||
}
|
||||
|
||||
.service-panel {
|
||||
padding: 13px 14px;
|
||||
}
|
||||
|
||||
.service-section-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.service-section-head h3 {
|
||||
margin: 0;
|
||||
color: #202733;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.service-section-head button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 1px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: #8a929f;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.service-grid {
|
||||
display: grid;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.service-grid.five {
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.service-grid.three {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.service-item {
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
gap: 5px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.service-item:active .service-icon {
|
||||
transform: scale(0.92);
|
||||
}
|
||||
|
||||
.service-icon {
|
||||
display: grid;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
place-items: center;
|
||||
border-radius: 10px;
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
|
||||
.service-icon.warning {
|
||||
background: #fff5de;
|
||||
color: #d89000;
|
||||
}
|
||||
|
||||
.service-icon.info {
|
||||
background: #edf4ff;
|
||||
color: #3178d9;
|
||||
}
|
||||
|
||||
.service-icon.primary {
|
||||
background: #eef4ff;
|
||||
color: #1d6dd1;
|
||||
}
|
||||
|
||||
.service-icon.success {
|
||||
background: #ebf9f3;
|
||||
color: #159a6b;
|
||||
}
|
||||
|
||||
.service-icon.orange {
|
||||
background: #fff0e7;
|
||||
color: #e96508;
|
||||
}
|
||||
|
||||
.service-icon.gold {
|
||||
background: #fff6e6;
|
||||
color: #c78a21;
|
||||
}
|
||||
|
||||
.service-icon.teal {
|
||||
background: #e9f8f7;
|
||||
color: #13938d;
|
||||
}
|
||||
|
||||
.service-item > span {
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
color: #4a5361;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.service-divider {
|
||||
height: 1px;
|
||||
margin: 13px 0;
|
||||
background: #f0f2f5;
|
||||
}
|
||||
|
||||
.more-services-card {
|
||||
overflow: hidden;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.more-services-title {
|
||||
padding: 13px 14px 8px;
|
||||
color: #4e5867;
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.more-services-card :deep(.van-cell) {
|
||||
padding: 11px 14px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.more-services-card :deep(.van-cell__left-icon) {
|
||||
margin-right: 9px;
|
||||
color: #9a7454;
|
||||
}
|
||||
|
||||
/* ========== 响应式适配 ========== */
|
||||
@media (min-width: 520px) {
|
||||
.mobile-profile-shell {
|
||||
|
||||
@@ -139,6 +139,7 @@ const settingsGroups = [
|
||||
title: '账号与安全',
|
||||
items: [
|
||||
{ label: '资料更改', icon: 'edit', action: 'profile' },
|
||||
{ label: '修改密码', icon: 'lock', action: 'password' },
|
||||
{ label: '实名认证', icon: 'idcard', action: 'realname' },
|
||||
{ label: '注销账号', icon: 'delete-o', action: 'cancel-account' },
|
||||
],
|
||||
@@ -328,6 +329,9 @@ function onSettingClick(action: string) {
|
||||
case 'profile':
|
||||
openProfileEditor()
|
||||
break
|
||||
case 'password':
|
||||
openPasswordEditor()
|
||||
break
|
||||
case 'realname':
|
||||
router.push('/m/realname')
|
||||
break
|
||||
@@ -464,15 +468,14 @@ function resolveAvatarURL(url: string | undefined | null) {
|
||||
<template v-if="isLoggedIn">
|
||||
<!-- Hero 顶部账户概览 -->
|
||||
<section class="profile-hero">
|
||||
<div class="hero-top-row">
|
||||
<div class="hero-title">
|
||||
<span class="app-title">个人中心</span>
|
||||
<small>账户、成长与权益管理</small>
|
||||
</div>
|
||||
<button class="hero-setting-btn" @click="showSettings = true">
|
||||
<van-icon name="setting-o" :size="20" color="#374151" />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="hero-setting-btn"
|
||||
type="button"
|
||||
aria-label="打开设置"
|
||||
@click="showSettings = true"
|
||||
>
|
||||
<van-icon name="setting-o" :size="20" color="#374151" />
|
||||
</button>
|
||||
<div class="profile-hero-content">
|
||||
<div class="avatar-wrap">
|
||||
<div class="avatar-circle">
|
||||
@@ -518,200 +521,196 @@ function resolveAvatarURL(url: string | undefined | null) {
|
||||
<p class="user-meta">ID: {{ displayId }} · {{ maskedPhone }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-status-row">
|
||||
<button
|
||||
v-if="!siteStore.isDistribution"
|
||||
type="button"
|
||||
class="hero-level-pill"
|
||||
@click="router.push('/m/benefits')"
|
||||
>
|
||||
<span>成长等级</span>
|
||||
<strong>{{ session.renterGrowthLevelName }}</strong>
|
||||
<van-icon name="arrow" :size="10" />
|
||||
</button>
|
||||
<div v-else class="hero-level-pill is-store">
|
||||
<span>所属商城</span>
|
||||
<strong>{{ siteStore.siteName || '官方商城' }}</strong>
|
||||
</div>
|
||||
<button type="button" class="hero-benefits-btn" @click="router.push('/m/benefits')">
|
||||
VIP 与权益
|
||||
<van-icon name="arrow" :size="10" />
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-if="siteStore.isDistribution" class="growth-card is-distribution">
|
||||
<div>
|
||||
<span>所属商城</span>
|
||||
<strong>{{ siteStore.siteName || '官方商城' }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>会员特权</span>
|
||||
<strong>渠道直租专享</strong>
|
||||
</div>
|
||||
</section>
|
||||
<section v-else class="growth-card">
|
||||
<div>
|
||||
<span>成长等级</span>
|
||||
<strong>{{ session.renterGrowthLevelName }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>累计积分</span>
|
||||
<strong>{{ session.renterGrowthPoints }}</strong>
|
||||
</div>
|
||||
<button type="button" class="growth-more" @click="router.push('/m/benefits')">
|
||||
<span>查看 VIP 与优质号主权益</span>
|
||||
<van-icon name="arrow" :size="11" />
|
||||
<section class="asset-overview-card">
|
||||
<button type="button" class="asset-points" @click="router.push('/m/points-mall')">
|
||||
<span class="asset-icon points-icon">
|
||||
<van-icon name="point-gift-o" :size="20" />
|
||||
</span>
|
||||
<span class="asset-copy">
|
||||
<small>可用积分</small>
|
||||
<strong>{{ session.pointsAvailable }}</strong>
|
||||
<em>累计 {{ session.renterGrowthPoints }}</em>
|
||||
</span>
|
||||
<van-icon name="arrow" :size="10" class="asset-arrow" />
|
||||
</button>
|
||||
|
||||
<div class="asset-wallet">
|
||||
<div class="asset-wallet-head">
|
||||
<span>账户余额</span>
|
||||
<button type="button" @click="openLedgers">明细</button>
|
||||
</div>
|
||||
<div class="asset-wallet-main">
|
||||
<strong>¥{{ formatCent(availableBalanceCent) }}</strong>
|
||||
<button type="button" class="asset-withdraw" @click="handleWithdraw">提现</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="quality-owner-card" :class="{ 'is-active': qualityOwnerStatus?.active }">
|
||||
<div class="quality-owner-heading">
|
||||
<div class="quality-owner-title">
|
||||
<span class="quality-owner-icon">
|
||||
<van-icon name="award-o" :size="18" />
|
||||
<section class="quality-entry-card" :class="{ 'is-active': qualityOwnerStatus?.active }">
|
||||
<div class="quality-entry-head">
|
||||
<div class="quality-entry-title">
|
||||
<span class="quality-entry-icon">
|
||||
<van-icon name="award-o" :size="16" />
|
||||
</span>
|
||||
<div>
|
||||
<div class="quality-entry-copy">
|
||||
<span>优质号主</span>
|
||||
<strong>{{ qualityOwnerStatus?.active ? '已开通' : '解锁极品专区发布权限' }}</strong>
|
||||
<strong>{{
|
||||
qualityOwnerStatus?.active ? '已开通极品专区权益' : '解锁极品专区发布权限'
|
||||
}}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<van-tag :type="qualityOwnerStatus?.active ? 'success' : 'default'" round>
|
||||
{{ qualityOwnerStatus?.active ? '优质号主' : '未开通' }}
|
||||
{{ qualityOwnerStatus?.active ? '已开通' : '未开通' }}
|
||||
</van-tag>
|
||||
</div>
|
||||
<div class="quality-owner-info">
|
||||
<div>
|
||||
<span>保证金</span>
|
||||
<strong>¥{{ formatCent(qualityOwnerStatus?.required_deposit_cent || 20000) }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>极品专区保险</span>
|
||||
<strong
|
||||
>{{
|
||||
((qualityOwnerStatus?.premium_insurance_fee_bps || 400) / 100).toFixed(1)
|
||||
}}%</strong
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="qualityOwnerStatus?.active" class="quality-owner-footer">
|
||||
<p>
|
||||
<template v-if="qualityOwnerStatus.active_premium_listing_count > 0">
|
||||
极品专区还有 {{ qualityOwnerStatus.active_premium_listing_count }}
|
||||
个进行中账号,需处理后可取消。
|
||||
</template>
|
||||
<template v-else-if="qualityOwnerStatus.deposit_source === 'manual'">
|
||||
已由后台开通优质号主,可随时取消。
|
||||
</template>
|
||||
<template v-else>保证金已冻结,取消后退回可用余额。</template>
|
||||
</p>
|
||||
<van-button
|
||||
size="small"
|
||||
round
|
||||
plain
|
||||
type="danger"
|
||||
class="quality-cancel-btn"
|
||||
:loading="qualityOwnerCancelling"
|
||||
:disabled="qualityOwnerStatus.active_premium_listing_count > 0"
|
||||
@click="handleCancelQualityOwner"
|
||||
>
|
||||
取消资格
|
||||
</van-button>
|
||||
</div>
|
||||
<div v-else class="quality-owner-actions">
|
||||
<van-button
|
||||
block
|
||||
round
|
||||
type="primary"
|
||||
color="#7c3aed"
|
||||
:loading="qualityOwnerLoading"
|
||||
:disabled="
|
||||
(qualityOwnerStatus?.available_balance_cent || 0) <
|
||||
(qualityOwnerStatus?.required_deposit_cent || 20000)
|
||||
"
|
||||
@click="handleActivateQualityOwner"
|
||||
>
|
||||
余额支付开通
|
||||
</van-button>
|
||||
<van-button
|
||||
block
|
||||
round
|
||||
plain
|
||||
type="primary"
|
||||
:loading="qualityOwnerOnlineLoading"
|
||||
@click="handleOnlineQualityOwnerPayment"
|
||||
>
|
||||
微信 / 支付宝支付开通
|
||||
</van-button>
|
||||
<p>开通后可发布到极品专区,并享受该专区专享保险费率。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mobile-points-entry" @click="router.push('/m/points-mall')">
|
||||
<div class="points-entry-left">
|
||||
<span class="points-entry-icon">
|
||||
<van-icon name="point-gift-o" :size="22" color="#ff6a00" />
|
||||
<div class="quality-entry-summary">
|
||||
<span>保证金 ¥{{ formatCent(qualityOwnerStatus?.required_deposit_cent || 20000) }}</span>
|
||||
<span>
|
||||
专区保险
|
||||
{{ ((qualityOwnerStatus?.premium_insurance_fee_bps || 400) / 100).toFixed(1) }}%
|
||||
</span>
|
||||
<div class="points-entry-copy">
|
||||
<strong>{{ session.pointsAvailable }} 可用积分</strong>
|
||||
<small>积分商城 · {{ session.renterGrowthLevelName }}等级</small>
|
||||
</div>
|
||||
</div>
|
||||
<van-icon name="arrow" />
|
||||
</section>
|
||||
|
||||
<!-- 余额卡片 - 银行卡质感暗色卡片 -->
|
||||
<div class="balance-card">
|
||||
<div class="balance-row">
|
||||
<div class="balance-info">
|
||||
<span class="balance-label">账户可用余额</span>
|
||||
<strong class="balance-value">¥{{ formatCent(availableBalanceCent) }}</strong>
|
||||
</div>
|
||||
<button class="withdraw-btn" @click="handleWithdraw">提现</button>
|
||||
</div>
|
||||
<div class="balance-footer">
|
||||
<button class="detail-btn" @click="openLedgers">
|
||||
查看收支明细 <van-icon name="arrow" :size="10" />
|
||||
<button type="button" @click="router.push('/m/benefits')">
|
||||
权益说明 <van-icon name="arrow" :size="9" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 买家服务面板 -->
|
||||
<div class="menu-card">
|
||||
<div class="card-header">
|
||||
<h3>买家服务</h3>
|
||||
<span class="see-all" @click="goOrders('all')">
|
||||
全部订单 <van-icon name="arrow" :size="10" />
|
||||
<div v-if="qualityOwnerStatus?.active" class="quality-entry-active">
|
||||
<span>
|
||||
<template v-if="qualityOwnerStatus.active_premium_listing_count > 0">
|
||||
还有 {{ qualityOwnerStatus.active_premium_listing_count }} 个进行中专区账号
|
||||
</template>
|
||||
<template v-else-if="qualityOwnerStatus.deposit_source === 'manual'">
|
||||
当前资格由后台开通
|
||||
</template>
|
||||
<template v-else>保证金已冻结,取消后自动退回余额</template>
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
class="quality-cancel-link"
|
||||
:disabled="
|
||||
qualityOwnerCancelling || qualityOwnerStatus.active_premium_listing_count > 0
|
||||
"
|
||||
@click="handleCancelQualityOwner"
|
||||
>
|
||||
{{ qualityOwnerCancelling ? '取消中...' : '取消资格' }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid-menu col-5">
|
||||
<div class="grid-item" @click="goOrders('pending_payment')">
|
||||
<div class="icon-wrap warning"><van-icon name="bill-o" :size="22" /></div>
|
||||
<span>待支付</span>
|
||||
</div>
|
||||
<div class="grid-item" @click="goOrders('pending_handoff')">
|
||||
<div class="icon-wrap info"><van-icon name="logistics" :size="22" /></div>
|
||||
<span>待交接</span>
|
||||
</div>
|
||||
<div class="grid-item" @click="goOrders('renting')">
|
||||
<div class="icon-wrap primary"><van-icon name="play-circle-o" :size="22" /></div>
|
||||
<span>使用中</span>
|
||||
</div>
|
||||
<div class="grid-item" @click="goOrders('completed')">
|
||||
<div class="icon-wrap success"><van-icon name="smile-o" :size="22" /></div>
|
||||
<span>已完成</span>
|
||||
</div>
|
||||
<div class="grid-item" @click="router.push('/m/listings/collections')">
|
||||
<div class="icon-wrap orange"><van-icon name="star-o" :size="22" /></div>
|
||||
<span>我的足迹</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卖家服务面板 -->
|
||||
<div class="menu-card">
|
||||
<div class="card-header">
|
||||
<h3>卖家服务</h3>
|
||||
</div>
|
||||
<div class="grid-menu col-3">
|
||||
<div class="grid-item" @click="router.push('/m/seller/listings/create')">
|
||||
<div class="icon-wrap orange"><van-icon name="plus" :size="22" /></div>
|
||||
<span>发布商品</span>
|
||||
</div>
|
||||
<div class="grid-item" @click="router.push('/m/seller/listings')">
|
||||
<div class="icon-wrap purple"><van-icon name="shop-o" :size="22" /></div>
|
||||
<span>我的商品</span>
|
||||
</div>
|
||||
<div class="grid-item" @click="router.push('/m/wallet/withdrawal')">
|
||||
<div class="icon-wrap teal"><van-icon name="balance-o" :size="22" /></div>
|
||||
<span>提现/账单</span>
|
||||
<div v-else class="quality-entry-actions">
|
||||
<span> 可用余额 ¥{{ formatCent(qualityOwnerStatus?.available_balance_cent || 0) }} </span>
|
||||
<div>
|
||||
<van-button
|
||||
size="small"
|
||||
round
|
||||
type="primary"
|
||||
color="#ff6a00"
|
||||
:loading="qualityOwnerLoading"
|
||||
:disabled="
|
||||
(qualityOwnerStatus?.available_balance_cent || 0) <
|
||||
(qualityOwnerStatus?.required_deposit_cent || 20000)
|
||||
"
|
||||
@click="handleActivateQualityOwner"
|
||||
>
|
||||
余额开通
|
||||
</van-button>
|
||||
<van-button
|
||||
size="small"
|
||||
round
|
||||
plain
|
||||
type="primary"
|
||||
color="#a45b16"
|
||||
:loading="qualityOwnerOnlineLoading"
|
||||
@click="handleOnlineQualityOwnerPayment"
|
||||
>
|
||||
在线支付
|
||||
</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 消息与公告 -->
|
||||
<div class="menu-card cell-card">
|
||||
<div class="cell-card-title">消息与公告</div>
|
||||
<section class="service-panel">
|
||||
<div class="service-section">
|
||||
<div class="service-section-head">
|
||||
<h3>买家服务</h3>
|
||||
<button type="button" @click="goOrders('all')">
|
||||
全部订单 <van-icon name="arrow" :size="10" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="service-grid five">
|
||||
<div class="service-item" @click="goOrders('pending_payment')">
|
||||
<div class="service-icon warning"><van-icon name="bill-o" :size="20" /></div>
|
||||
<span>待支付</span>
|
||||
</div>
|
||||
<div class="service-item" @click="goOrders('pending_handoff')">
|
||||
<div class="service-icon info"><van-icon name="logistics" :size="20" /></div>
|
||||
<span>待交接</span>
|
||||
</div>
|
||||
<div class="service-item" @click="goOrders('renting')">
|
||||
<div class="service-icon primary"><van-icon name="play-circle-o" :size="20" /></div>
|
||||
<span>使用中</span>
|
||||
</div>
|
||||
<div class="service-item" @click="goOrders('completed')">
|
||||
<div class="service-icon success"><van-icon name="smile-o" :size="20" /></div>
|
||||
<span>已完成</span>
|
||||
</div>
|
||||
<div class="service-item" @click="router.push('/m/listings/collections')">
|
||||
<div class="service-icon orange"><van-icon name="star-o" :size="20" /></div>
|
||||
<span>我的足迹</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="service-divider"></div>
|
||||
|
||||
<div class="service-section">
|
||||
<div class="service-section-head">
|
||||
<h3>卖家服务</h3>
|
||||
<button type="button" @click="router.push('/m/seller/listings')">
|
||||
商品管理 <van-icon name="arrow" :size="10" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="service-grid three">
|
||||
<div class="service-item" @click="router.push('/m/seller/listings/create')">
|
||||
<div class="service-icon orange"><van-icon name="plus" :size="20" /></div>
|
||||
<span>发布商品</span>
|
||||
</div>
|
||||
<div class="service-item" @click="router.push('/m/seller/listings')">
|
||||
<div class="service-icon gold"><van-icon name="shop-o" :size="20" /></div>
|
||||
<span>我的商品</span>
|
||||
</div>
|
||||
<div class="service-item" @click="router.push('/m/wallet/withdrawal')">
|
||||
<div class="service-icon teal"><van-icon name="balance-o" :size="20" /></div>
|
||||
<span>提现/账单</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="more-services-card">
|
||||
<div class="more-services-title">消息、账户与服务</div>
|
||||
<van-cell-group :border="false">
|
||||
<van-cell title="消息中心" icon="chat-o" is-link to="/m/messages" />
|
||||
<van-cell title="站内信" icon="envelop-o" is-link to="/m/notifications">
|
||||
@@ -722,22 +721,6 @@ function resolveAvatarURL(url: string | undefined | null) {
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-cell title="公告中心" icon="volume-o" is-link to="/m/announcements" />
|
||||
</van-cell-group>
|
||||
</div>
|
||||
|
||||
<!-- 账户与常用服务 -->
|
||||
<div class="menu-card cell-card">
|
||||
<div class="cell-card-title">账户与服务</div>
|
||||
<van-cell-group :border="false">
|
||||
<van-cell title="资料修改" icon="edit" is-link @click="openProfileEditor" />
|
||||
<van-cell title="修改密码" icon="lock" is-link @click="openPasswordEditor" />
|
||||
<van-cell title="实名认证" icon="idcard" is-link to="/m/realname">
|
||||
<template #value>
|
||||
<span :class="isRealnameVerified ? 'verified-color' : 'unverified-color'">
|
||||
{{ isRealnameVerified ? '已认证' : isRealnamePending ? '审核中' : '未认证' }}
|
||||
</span>
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-cell
|
||||
title="邀请返利"
|
||||
icon="gold-coin-o"
|
||||
@@ -745,11 +728,10 @@ function resolveAvatarURL(url: string | undefined | null) {
|
||||
to="/m/invitations"
|
||||
:value="inviteRateText || '阶梯返利'"
|
||||
/>
|
||||
<van-cell title="权益中心" icon="medal-o" is-link to="/m/benefits" />
|
||||
<van-cell title="租后须知" icon="info-o" is-link @click="openPostRentalNotice" />
|
||||
<van-cell title="系统设置" icon="setting-o" is-link @click="showSettings = true" />
|
||||
<van-cell title="资料与安全" icon="setting-o" is-link @click="showSettings = true" />
|
||||
</van-cell-group>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<MobilePayWaySelectPopup
|
||||
|
||||
+204
-103
@@ -727,94 +727,71 @@ h1 {
|
||||
.listing-control-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
gap: 14px;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e7ebf1;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
padding: 16px 18px 18px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* 统计卡片行 */
|
||||
.listing-stats-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
.listing-filter-toolbar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 16px 18px;
|
||||
border: 1px solid #e8edf4;
|
||||
border-radius: 10px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
|
||||
transition: all 0.2s ease;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding-bottom: 14px;
|
||||
border-bottom: 1px solid #eef1f5;
|
||||
}
|
||||
|
||||
.stat-card.clickable {
|
||||
cursor: pointer;
|
||||
.listing-filter-summary {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.stat-card.clickable:hover {
|
||||
border-color: #1477ff;
|
||||
box-shadow: 0 2px 8px rgba(20, 119, 255, 0.15);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.stat-card.active {
|
||||
border-color: #1477ff;
|
||||
background: #1477ff;
|
||||
box-shadow: 0 2px 8px rgba(20, 119, 255, 0.25);
|
||||
}
|
||||
|
||||
.stat-card .stat-label {
|
||||
color: #6b7785;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.stat-card.active .stat-label {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.stat-card .stat-value {
|
||||
color: #17233d;
|
||||
font-size: 28px;
|
||||
.listing-filter-summary strong {
|
||||
color: #1f2a3d;
|
||||
font-size: 15px;
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.stat-card.active .stat-value {
|
||||
color: #ffffff;
|
||||
.listing-filter-summary span {
|
||||
color: #8b95a4;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.listing-filter-summary b {
|
||||
color: #2d5bd1;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
/* 筛选和操作行 */
|
||||
.listing-filter-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: end;
|
||||
gap: 16px 24px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #f0f3f7;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.listing-filter-bar {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(220px, 1.2fr) minmax(150px, 0.78fr) minmax(220px, 1fr) minmax(
|
||||
220px,
|
||||
1fr
|
||||
);
|
||||
grid-template-columns: repeat(8, minmax(0, 1fr));
|
||||
align-items: end;
|
||||
gap: 12px 16px;
|
||||
gap: 12px;
|
||||
margin: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.listing-filter-bar .filter-keyword {
|
||||
grid-column: span 3;
|
||||
}
|
||||
|
||||
.listing-filter-bar .filter-range,
|
||||
.listing-filter-bar .filter-asset,
|
||||
.listing-filter-bar .filter-skin {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.listing-filter-bar .el-form-item {
|
||||
margin-bottom: 0;
|
||||
min-width: 0;
|
||||
@@ -879,7 +856,6 @@ h1 {
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-shrink: 0;
|
||||
padding-bottom: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@@ -1212,98 +1188,223 @@ h1 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
border: none;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.table-pagination {
|
||||
justify-content: flex-end;
|
||||
margin-top: 12px;
|
||||
border-radius: 12px;
|
||||
padding: 12px 16px;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px 18px;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
margin-top: 12px;
|
||||
padding: 10px 14px;
|
||||
border: 1px solid #e7ebf1;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
box-shadow: none;
|
||||
color: #66728f;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.pagination-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pagination-summary strong {
|
||||
color: #263247;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.pagination-summary em {
|
||||
padding-left: 8px;
|
||||
border-left: 1px solid #e8ecf2;
|
||||
color: #99a2af;
|
||||
font-size: 11px;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.pagination-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.pagination-summary,
|
||||
.pagination-page,
|
||||
.pagination-size-label {
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.page-size-select {
|
||||
width: 86px;
|
||||
flex: 0 0 86px;
|
||||
width: 82px;
|
||||
flex: 0 0 82px;
|
||||
}
|
||||
|
||||
.page-size-select .el-select__wrapper {
|
||||
min-height: 30px;
|
||||
padding: 4px 8px;
|
||||
min-height: 28px;
|
||||
border-radius: 5px;
|
||||
padding: 2px 7px;
|
||||
}
|
||||
|
||||
.page-size-select .el-select__selected-item {
|
||||
min-width: max-content;
|
||||
}
|
||||
|
||||
.admin-listings-page .table-pagination {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.pagination-controls .el-button--small {
|
||||
min-width: 44px;
|
||||
padding: 5px 8px;
|
||||
.pagination-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.pagination-controls .el-button + .el-button {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.pagination-nav-btn.el-button {
|
||||
width: 28px;
|
||||
min-width: 28px;
|
||||
height: 28px;
|
||||
padding: 0;
|
||||
border-color: #e2e7ee;
|
||||
border-radius: 5px;
|
||||
color: #66728a;
|
||||
}
|
||||
|
||||
.pagination-nav-btn.el-button:hover:not(.is-disabled) {
|
||||
border-color: #9db6ed;
|
||||
background: #f4f8ff;
|
||||
color: #2c61d5;
|
||||
}
|
||||
|
||||
.pagination-page {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
gap: 3px;
|
||||
min-width: 56px;
|
||||
padding: 0 6px;
|
||||
color: #8d97a6;
|
||||
font-size: 11px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pagination-page strong {
|
||||
color: #2b5fd2;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.pagination-page i {
|
||||
color: #c0c6cf;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.pagination-jump {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding-left: 8px;
|
||||
border-left: 1px solid #e8ecf2;
|
||||
color: #818b9a;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pagination-jump-input {
|
||||
width: 52px;
|
||||
}
|
||||
|
||||
.pagination-jump-input .el-input__wrapper {
|
||||
min-height: 28px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.pagination-jump-input .el-input__inner {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pagination-jump-btn.el-button {
|
||||
height: 28px;
|
||||
padding: 0 9px;
|
||||
border-color: #dce3ec;
|
||||
border-radius: 5px;
|
||||
color: #3f5f97;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.admin-listings-page .table-pagination {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.table-pagination {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.pagination-controls {
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.pagination-jump {
|
||||
padding-left: 0;
|
||||
border-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
.listing-filter-row {
|
||||
grid-template-columns: 1fr;
|
||||
.listing-filter-toolbar {
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.listing-filter-bar {
|
||||
grid-template-columns: repeat(2, minmax(180px, 1fr));
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.listing-action-strip {
|
||||
justify-content: flex-end;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.listing-control-panel {
|
||||
grid-template-columns: 1fr;
|
||||
align-items: stretch;
|
||||
.listing-filter-bar {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.listing-filter-row {
|
||||
grid-template-columns: 1fr;
|
||||
.listing-filter-bar .filter-keyword {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.listing-action-strip {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.listing-control-panel {
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.listing-filter-summary {
|
||||
display: grid;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.listing-filter-bar {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.listing-action-strip {
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
.listing-filter-bar .filter-keyword,
|
||||
.listing-filter-bar .filter-range,
|
||||
.listing-filter-bar .filter-asset,
|
||||
.listing-filter-bar .filter-skin {
|
||||
grid-column: span 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user