新增商品编号搜索
This commit is contained in:
@@ -30,6 +30,7 @@ func (GameAccount) TableName() string {
|
|||||||
|
|
||||||
type RentalListing struct {
|
type RentalListing struct {
|
||||||
ID uint64 `gorm:"primaryKey" json:"id"`
|
ID uint64 `gorm:"primaryKey" json:"id"`
|
||||||
|
ListingNo string `gorm:"column:listing_no;size:20;not null;uniqueIndex" json:"listing_no"`
|
||||||
AccountID uint64 `gorm:"not null;index" json:"account_id"`
|
AccountID uint64 `gorm:"not null;index" json:"account_id"`
|
||||||
OwnerID uint64 `gorm:"not null;index" json:"owner_id"`
|
OwnerID uint64 `gorm:"not null;index" json:"owner_id"`
|
||||||
Price float64 `gorm:"type:decimal(12,2);not null;default:0" json:"price"`
|
Price float64 `gorm:"type:decimal(12,2);not null;default:0" json:"price"`
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
type ListingDTO struct {
|
type ListingDTO struct {
|
||||||
ID uint64 `json:"id"`
|
ID uint64 `json:"id"`
|
||||||
|
ListingNo string `json:"listing_no"`
|
||||||
AccountID uint64 `json:"account_id"`
|
AccountID uint64 `json:"account_id"`
|
||||||
OwnerID uint64 `json:"owner_id"`
|
OwnerID uint64 `json:"owner_id"`
|
||||||
OwnerPhone string `json:"owner_phone,omitempty"`
|
OwnerPhone string `json:"owner_phone,omitempty"`
|
||||||
@@ -187,6 +188,7 @@ type ExternalUploadMeta struct {
|
|||||||
type ExternalUploadResult struct {
|
type ExternalUploadResult struct {
|
||||||
Index int `json:"index"`
|
Index int `json:"index"`
|
||||||
ListingID uint64 `json:"listing_id,omitempty"`
|
ListingID uint64 `json:"listing_id,omitempty"`
|
||||||
|
ListingNo string `json:"listing_no,omitempty"`
|
||||||
AccountID uint64 `json:"account_id,omitempty"`
|
AccountID uint64 `json:"account_id,omitempty"`
|
||||||
Status string `json:"status,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
ReviewStatus string `json:"review_status,omitempty"`
|
ReviewStatus string `json:"review_status,omitempty"`
|
||||||
@@ -195,6 +197,7 @@ type ExternalUploadResult struct {
|
|||||||
|
|
||||||
type ExternalUploadResponse struct {
|
type ExternalUploadResponse struct {
|
||||||
ListingID uint64 `json:"listing_id,omitempty"`
|
ListingID uint64 `json:"listing_id,omitempty"`
|
||||||
|
ListingNo string `json:"listing_no,omitempty"`
|
||||||
AccountID uint64 `json:"account_id,omitempty"`
|
AccountID uint64 `json:"account_id,omitempty"`
|
||||||
Status string `json:"status,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
ReviewStatus string `json:"review_status,omitempty"`
|
ReviewStatus string `json:"review_status,omitempty"`
|
||||||
|
|||||||
@@ -50,6 +50,10 @@ func initialPublishState(reviewRequired bool) (string, string, *time.Time) {
|
|||||||
func (r *Repository) Create(ownerID uint64, req CreateRequest, reviewRequired bool) (*ListingDTO, error) {
|
func (r *Repository) Create(ownerID uint64, req CreateRequest, reviewRequired bool) (*ListingDTO, error) {
|
||||||
var dto *ListingDTO
|
var dto *ListingDTO
|
||||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
listingNo, err := r.nextListingNo(tx, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
screenshots, err := marshalScreenshots(req.ScreenshotURLS)
|
screenshots, err := marshalScreenshots(req.ScreenshotURLS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -78,6 +82,7 @@ func (r *Repository) Create(ownerID uint64, req CreateRequest, reviewRequired bo
|
|||||||
price := normalizedListingPrice(req)
|
price := normalizedListingPrice(req)
|
||||||
depositAmount := roundMoney(req.DepositAmount)
|
depositAmount := roundMoney(req.DepositAmount)
|
||||||
listing := model.RentalListing{
|
listing := model.RentalListing{
|
||||||
|
ListingNo: listingNo,
|
||||||
AccountID: account.ID,
|
AccountID: account.ID,
|
||||||
OwnerID: ownerID,
|
OwnerID: ownerID,
|
||||||
Price: price,
|
Price: price,
|
||||||
@@ -106,6 +111,10 @@ type externalUploadCreate struct {
|
|||||||
func (r *Repository) CreateFromExternalUpload(upload externalUploadCreate, req CreateRequest) (*ListingDTO, error) {
|
func (r *Repository) CreateFromExternalUpload(upload externalUploadCreate, req CreateRequest) (*ListingDTO, error) {
|
||||||
var dto *ListingDTO
|
var dto *ListingDTO
|
||||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
listingNo, err := r.nextListingNo(tx, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
admin, err := r.findActiveUploadAdmin(tx, upload.UploaderName)
|
admin, err := r.findActiveUploadAdmin(tx, upload.UploaderName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -139,6 +148,7 @@ func (r *Repository) CreateFromExternalUpload(upload externalUploadCreate, req C
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
listing := model.RentalListing{
|
listing := model.RentalListing{
|
||||||
|
ListingNo: listingNo,
|
||||||
AccountID: account.ID,
|
AccountID: account.ID,
|
||||||
OwnerID: owner.ID,
|
OwnerID: owner.ID,
|
||||||
Price: normalizedListingPrice(req),
|
Price: normalizedListingPrice(req),
|
||||||
@@ -1009,6 +1019,9 @@ func publicZoneCounts(items []ListingDTO) map[string]int64 {
|
|||||||
|
|
||||||
func publicSearchText(item ListingDTO) string {
|
func publicSearchText(item ListingDTO) string {
|
||||||
parts := []string{
|
parts := []string{
|
||||||
|
item.ListingNo,
|
||||||
|
strconv.FormatUint(item.ID, 10),
|
||||||
|
strconv.FormatUint(item.AccountID, 10),
|
||||||
item.Title,
|
item.Title,
|
||||||
item.Description,
|
item.Description,
|
||||||
item.RankLevel,
|
item.RankLevel,
|
||||||
@@ -1303,6 +1316,39 @@ func (r *Repository) findForReviewUpdate(tx *gorm.DB, listingID uint64) (*model.
|
|||||||
return &listing, &account, nil
|
return &listing, &account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Repository) nextListingNo(tx *gorm.DB, now time.Time) (string, error) {
|
||||||
|
bizDate := now.Format("20060102")
|
||||||
|
if tx.Dialector.Name() == "mysql" {
|
||||||
|
if err := tx.Exec(`
|
||||||
|
INSERT INTO listing_no_sequences (biz_date, next_seq)
|
||||||
|
VALUES (?, LAST_INSERT_ID(1))
|
||||||
|
ON DUPLICATE KEY UPDATE next_seq = LAST_INSERT_ID(next_seq + 1)
|
||||||
|
`, bizDate).Error; err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var seq int
|
||||||
|
if err := tx.Raw("SELECT LAST_INSERT_ID()").Scan(&seq).Error; err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s%04d", bizDate, seq), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxNo string
|
||||||
|
if err := tx.Table("rental_listings").
|
||||||
|
Select("COALESCE(MAX(listing_no), '')").
|
||||||
|
Where("listing_no LIKE ?", bizDate+"%").
|
||||||
|
Scan(&maxNo).Error; err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
seq := 1
|
||||||
|
if len(maxNo) > len(bizDate) {
|
||||||
|
if parsed, err := strconv.Atoi(maxNo[len(bizDate):]); err == nil {
|
||||||
|
seq = parsed + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s%04d", bizDate, seq), nil
|
||||||
|
}
|
||||||
|
|
||||||
type listingRow struct {
|
type listingRow struct {
|
||||||
model.RentalListing
|
model.RentalListing
|
||||||
Title string
|
Title string
|
||||||
@@ -1394,6 +1440,7 @@ func (row listingRow) toDTO() ListingDTO {
|
|||||||
reviewStatus, reviewReason := normalizedReviewState(row.Status, row.ReviewStatus, row.ReviewReason)
|
reviewStatus, reviewReason := normalizedReviewState(row.Status, row.ReviewStatus, row.ReviewReason)
|
||||||
return ListingDTO{
|
return ListingDTO{
|
||||||
ID: row.ID,
|
ID: row.ID,
|
||||||
|
ListingNo: row.ListingNo,
|
||||||
AccountID: row.AccountID,
|
AccountID: row.AccountID,
|
||||||
OwnerID: row.OwnerID,
|
OwnerID: row.OwnerID,
|
||||||
OwnerPhone: row.OwnerPhone,
|
OwnerPhone: row.OwnerPhone,
|
||||||
@@ -1427,6 +1474,7 @@ func toDTO(account model.GameAccount, listing model.RentalListing) *ListingDTO {
|
|||||||
reviewStatus, reviewReason := normalizedReviewState(listing.Status, listing.ReviewStatus, listing.ReviewReason)
|
reviewStatus, reviewReason := normalizedReviewState(listing.Status, listing.ReviewStatus, listing.ReviewReason)
|
||||||
return &ListingDTO{
|
return &ListingDTO{
|
||||||
ID: listing.ID,
|
ID: listing.ID,
|
||||||
|
ListingNo: listing.ListingNo,
|
||||||
AccountID: account.ID,
|
AccountID: account.ID,
|
||||||
OwnerID: listing.OwnerID,
|
OwnerID: listing.OwnerID,
|
||||||
Title: account.Title,
|
Title: account.Title,
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ func (s *Service) ImportExternalUpload(req ExternalUploadRequest, meta ExternalU
|
|||||||
result := ExternalUploadResult{
|
result := ExternalUploadResult{
|
||||||
Index: index,
|
Index: index,
|
||||||
ListingID: dto.ID,
|
ListingID: dto.ID,
|
||||||
|
ListingNo: dto.ListingNo,
|
||||||
AccountID: dto.AccountID,
|
AccountID: dto.AccountID,
|
||||||
Status: dto.Status,
|
Status: dto.Status,
|
||||||
ReviewStatus: dto.ReviewStatus,
|
ReviewStatus: dto.ReviewStatus,
|
||||||
@@ -157,6 +158,7 @@ func (s *Service) ImportExternalUpload(req ExternalUploadRequest, meta ExternalU
|
|||||||
results = append(results, result)
|
results = append(results, result)
|
||||||
if len(items) == 1 {
|
if len(items) == 1 {
|
||||||
resp.ListingID = dto.ID
|
resp.ListingID = dto.ID
|
||||||
|
resp.ListingNo = dto.ListingNo
|
||||||
resp.AccountID = dto.AccountID
|
resp.AccountID = dto.AccountID
|
||||||
resp.Status = dto.Status
|
resp.Status = dto.Status
|
||||||
resp.ReviewStatus = dto.ReviewStatus
|
resp.ReviewStatus = dto.ReviewStatus
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
-- 商品编号:用于前台展示、搜索和客服定位
|
||||||
|
ALTER TABLE rental_listings
|
||||||
|
ADD COLUMN listing_no VARCHAR(20) NULL COMMENT '商品编号,格式yyyyMMddNNNN' AFTER id;
|
||||||
|
|
||||||
|
UPDATE rental_listings AS l
|
||||||
|
JOIN (
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
CONCAT(
|
||||||
|
DATE_FORMAT(created_at, '%Y%m%d'),
|
||||||
|
LPAD(ROW_NUMBER() OVER (PARTITION BY DATE(created_at) ORDER BY id), 4, '0')
|
||||||
|
) AS generated_listing_no
|
||||||
|
FROM rental_listings
|
||||||
|
) AS seq ON seq.id = l.id
|
||||||
|
SET l.listing_no = seq.generated_listing_no
|
||||||
|
WHERE l.listing_no IS NULL OR l.listing_no = '';
|
||||||
|
|
||||||
|
ALTER TABLE rental_listings
|
||||||
|
MODIFY COLUMN listing_no VARCHAR(20) NOT NULL COMMENT '商品编号,格式yyyyMMddNNNN',
|
||||||
|
ADD UNIQUE KEY uk_rental_listings_listing_no (listing_no);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS listing_no_sequences (
|
||||||
|
biz_date CHAR(8) PRIMARY KEY COMMENT '业务日期yyyyMMdd',
|
||||||
|
next_seq INT NOT NULL DEFAULT 0 COMMENT '当日已分配最大序号',
|
||||||
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='商品编号日序列表';
|
||||||
|
|
||||||
|
INSERT INTO listing_no_sequences (biz_date, next_seq)
|
||||||
|
SELECT DATE_FORMAT(created_at, '%Y%m%d') AS biz_date, COUNT(*) AS next_seq
|
||||||
|
FROM rental_listings
|
||||||
|
GROUP BY DATE_FORMAT(created_at, '%Y%m%d')
|
||||||
|
ON DUPLICATE KEY UPDATE next_seq = GREATEST(next_seq, VALUES(next_seq));
|
||||||
@@ -4,6 +4,7 @@ import type { ListingReviewStatus, ListingStatus } from '@/shared/types/status'
|
|||||||
|
|
||||||
export interface Listing {
|
export interface Listing {
|
||||||
id: number
|
id: number
|
||||||
|
listing_no: string
|
||||||
account_id: number
|
account_id: number
|
||||||
owner_id: number
|
owner_id: number
|
||||||
owner_phone?: string
|
owner_phone?: string
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
getCoinWan,
|
getCoinWan,
|
||||||
getDailyLoss,
|
getDailyLoss,
|
||||||
getListingDisplayPrice,
|
getListingDisplayPrice,
|
||||||
|
formatListingCode,
|
||||||
getListingResources,
|
getListingResources,
|
||||||
getListingTitle,
|
getListingTitle,
|
||||||
getLoginMethod,
|
getLoginMethod,
|
||||||
@@ -100,7 +101,10 @@ function formatStatNumber(value: number) {
|
|||||||
<div class="card-details">
|
<div class="card-details">
|
||||||
<div class="card-title-row">
|
<div class="card-title-row">
|
||||||
<h3>{{ getListingTitle(listing) }}</h3>
|
<h3>{{ getListingTitle(listing) }}</h3>
|
||||||
<span v-if="dailyLoss" class="daily-loss">日耗 {{ dailyLoss }}</span>
|
<div class="card-title-meta">
|
||||||
|
<span class="listing-no">编号 {{ formatListingCode(listing) }}</span>
|
||||||
|
<span v-if="dailyLoss" class="daily-loss">日耗 {{ dailyLoss }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="info-panel">
|
<div class="info-panel">
|
||||||
@@ -252,6 +256,23 @@ function formatStatNumber(value: number) {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-title-meta {
|
||||||
|
display: inline-flex;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listing-no {
|
||||||
|
padding: 4px 10px;
|
||||||
|
border: 1px solid #dbeafe;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #eff6ff;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #2563eb;
|
||||||
|
}
|
||||||
|
|
||||||
.daily-loss {
|
.daily-loss {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import {
|
import {
|
||||||
defaultHomeAnnouncements,
|
defaultHomeAnnouncements,
|
||||||
defaultHomeBanners,
|
defaultHomeBanners,
|
||||||
@@ -24,6 +25,8 @@ const banners = ref<HomeBannerSlide[]>(defaultHomeBanners)
|
|||||||
const publishOptions = ref<ListingPublishOptions>(emptyListingPublishOptions)
|
const publishOptions = ref<ListingPublishOptions>(emptyListingPublishOptions)
|
||||||
const sortBy = ref('recommended')
|
const sortBy = ref('recommended')
|
||||||
const activeZone = ref('all')
|
const activeZone = ref('all')
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
filters,
|
filters,
|
||||||
@@ -40,6 +43,8 @@ const {
|
|||||||
computed(() => listings.value)
|
computed(() => listings.value)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
filters.keyword = routeKeyword()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
loading,
|
loading,
|
||||||
loadingMore,
|
loadingMore,
|
||||||
@@ -50,6 +55,13 @@ const {
|
|||||||
zoneCount,
|
zoneCount,
|
||||||
} = useListingQuery(filters, sortBy, activeZone)
|
} = useListingQuery(filters, sortBy, activeZone)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.query.keyword,
|
||||||
|
() => {
|
||||||
|
filters.keyword = routeKeyword()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const statCards = computed(() => [
|
const statCards = computed(() => [
|
||||||
{ label: '可租账号', value: `${totalListings.value}`, hint: '当前筛选结果' },
|
{ label: '可租账号', value: `${totalListings.value}`, hint: '当前筛选结果' },
|
||||||
{
|
{
|
||||||
@@ -127,6 +139,19 @@ function handleResetFilters() {
|
|||||||
resetFilters()
|
resetFilters()
|
||||||
activeZone.value = 'all'
|
activeZone.value = 'all'
|
||||||
sortBy.value = 'recommended'
|
sortBy.value = 'recommended'
|
||||||
|
router.replace({
|
||||||
|
path: '/',
|
||||||
|
query: {
|
||||||
|
...route.query,
|
||||||
|
keyword: undefined,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function routeKeyword() {
|
||||||
|
const keyword = route.query.keyword
|
||||||
|
if (Array.isArray(keyword)) return String(keyword[0] || '')
|
||||||
|
return typeof keyword === 'string' ? keyword : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
loadHome()
|
loadHome()
|
||||||
|
|||||||
@@ -323,6 +323,18 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mobile-listing-no {
|
||||||
|
display: inline-flex;
|
||||||
|
width: fit-content;
|
||||||
|
margin-top: 6px;
|
||||||
|
padding: 3px 7px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #eff6ff;
|
||||||
|
color: #2563eb;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
.card-subtitle {
|
.card-subtitle {
|
||||||
margin: 6px 0 10px;
|
margin: 6px 0 10px;
|
||||||
color: #64748b;
|
color: #64748b;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
getListingDisplayPrice,
|
getListingDisplayPrice,
|
||||||
getListingSubtitle,
|
getListingSubtitle,
|
||||||
getListingTitle,
|
getListingTitle,
|
||||||
|
formatListingCode,
|
||||||
getLoginMethod,
|
getLoginMethod,
|
||||||
getServerRegion,
|
getServerRegion,
|
||||||
hasAcceleratedSaleRatio,
|
hasAcceleratedSaleRatio,
|
||||||
@@ -377,7 +378,7 @@ function uniqueOptions(values: string[]) {
|
|||||||
<van-search
|
<van-search
|
||||||
v-model="searchValue"
|
v-model="searchValue"
|
||||||
shape="round"
|
shape="round"
|
||||||
placeholder="搜区服 / 段位"
|
placeholder="搜编号 / 区服 / 段位"
|
||||||
class="home-search"
|
class="home-search"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
@@ -510,6 +511,7 @@ function uniqueOptions(values: string[]) {
|
|||||||
<div class="card-title-row">
|
<div class="card-title-row">
|
||||||
<h2>{{ getListingTitle(item) }}</h2>
|
<h2>{{ getListingTitle(item) }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mobile-listing-no">编号 {{ formatListingCode(item) }}</div>
|
||||||
<p class="card-subtitle">{{ getListingSubtitle(item) }}</p>
|
<p class="card-subtitle">{{ getListingSubtitle(item) }}</p>
|
||||||
<div class="card-badges-row">
|
<div class="card-badges-row">
|
||||||
<span class="server-badge">{{ getServerRegion(item) }}</span>
|
<span class="server-badge">{{ getServerRegion(item) }}</span>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import {
|
import {
|
||||||
ArrowDown,
|
ArrowDown,
|
||||||
@@ -28,6 +28,7 @@ import { useSessionStore } from '@/stores/session'
|
|||||||
|
|
||||||
const session = useSessionStore()
|
const session = useSessionStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ label: '首页', to: '/', icon: House },
|
{ label: '首页', to: '/', icon: House },
|
||||||
@@ -50,6 +51,7 @@ const sellerServiceItems = [
|
|||||||
|
|
||||||
const showUserDropdown = ref(false)
|
const showUserDropdown = ref(false)
|
||||||
const supportLoading = ref(false)
|
const supportLoading = ref(false)
|
||||||
|
const topSearchKeyword = ref('')
|
||||||
|
|
||||||
const realnameBadge = computed(() => {
|
const realnameBadge = computed(() => {
|
||||||
switch (session.realnameStatus) {
|
switch (session.realnameStatus) {
|
||||||
@@ -64,6 +66,33 @@ const realnameBadge = computed(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [route.path, route.query.keyword],
|
||||||
|
() => {
|
||||||
|
if (route.path !== '/') return
|
||||||
|
topSearchKeyword.value = normalizeRouteKeyword(route.query.keyword)
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
function normalizeRouteKeyword(value: unknown) {
|
||||||
|
if (Array.isArray(value)) return String(value[0] || '')
|
||||||
|
return typeof value === 'string' ? value : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTopSearchInput(event: Event) {
|
||||||
|
const value = (event.target as HTMLInputElement).value
|
||||||
|
topSearchKeyword.value = value
|
||||||
|
const keyword = value.trim()
|
||||||
|
router.replace({
|
||||||
|
path: '/',
|
||||||
|
query: {
|
||||||
|
...route.query,
|
||||||
|
keyword: keyword || undefined,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function handleLogout() {
|
function handleLogout() {
|
||||||
session.logout()
|
session.logout()
|
||||||
showUserDropdown.value = false
|
showUserDropdown.value = false
|
||||||
@@ -108,7 +137,11 @@ async function handleSupportClick() {
|
|||||||
|
|
||||||
<div class="pc-search">
|
<div class="pc-search">
|
||||||
<el-icon><Search /></el-icon>
|
<el-icon><Search /></el-icon>
|
||||||
<input placeholder="搜索区服 / 段位 / 哈夫币数量 / 皮肤" />
|
<input
|
||||||
|
:value="topSearchKeyword"
|
||||||
|
placeholder="搜索编号 / 区服 / 段位 / 哈夫币数量 / 皮肤"
|
||||||
|
@input="handleTopSearchInput"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pc-user-actions">
|
<div class="pc-user-actions">
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export function getCoinM(item: Listing) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function formatListingCode(item: Listing) {
|
export function formatListingCode(item: Listing) {
|
||||||
|
if (item.listing_no) return item.listing_no
|
||||||
return `SP${String(item.id).padStart(6, '0')}`
|
return `SP${String(item.id).padStart(6, '0')}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
.filter-header {
|
.filter-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export function getCoinM(item: Listing) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function formatListingCode(item: Listing) {
|
export function formatListingCode(item: Listing) {
|
||||||
|
if (item.listing_no) return item.listing_no
|
||||||
return `SP${String(item.id).padStart(6, '0')}`
|
return `SP${String(item.id).padStart(6, '0')}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.tsbuildinfo",
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.tsbuildinfo",
|
||||||
|
"noEmit": true,
|
||||||
"types": ["vite/client"],
|
"types": ["vite/client"],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
|
|||||||
Reference in New Issue
Block a user