618 lines
16 KiB
Plaintext
618 lines
16 KiB
Plaintext
<template>
|
||
<view class="product-list-page">
|
||
<!-- 1. 鎼滅储琛ㄥ崟 -->
|
||
<view class="search-card">
|
||
<view class="search-row">
|
||
<view class="search-item">
|
||
<text class="label">鍟嗗搧鎼滅储锛?/text>
|
||
<input class="mock-input" placeholder="璇疯緭鍏ュ晢鍝佸悕绉?鍏抽敭瀛?ID" />
|
||
</view>
|
||
<view class="search-item">
|
||
<text class="label">鍟嗗搧绫诲瀷锛?/text>
|
||
<view class="mock-select">
|
||
<text>鍏ㄩ儴</text>
|
||
<text class="arrow">鈻?/text>
|
||
</view>
|
||
</view>
|
||
<view class="search-item">
|
||
<text class="label">鍟嗗搧鍒嗙被锛?/text>
|
||
<view class="mock-select">
|
||
<text>璇烽€夋嫨</text>
|
||
<text class="arrow">鈻?/text>
|
||
</view>
|
||
</view>
|
||
<view class="search-btns">
|
||
<button class="btn-primary">鏌ヨ</button>
|
||
<button class="btn-reset">閲嶇疆</button>
|
||
<view class="expand-control">
|
||
<text class="expand-txt">灞曞紑</text>
|
||
<text class="expand-arrow">鈻?/text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="search-row mt-12">
|
||
<view class="search-item">
|
||
<text class="label">閰嶉€佹柟寮忥細</text>
|
||
<view class="mock-select">
|
||
<text>鍏ㄩ儴</text>
|
||
<text class="arrow">鈻?/text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 2. 鍟嗗搧鐘舵€?Tabs -->
|
||
<view class="status-tabs-wrap">
|
||
<view class="status-tabs">
|
||
<view
|
||
v-for="(tab, index) in statusTabs"
|
||
:key="index"
|
||
class="tab-item"
|
||
:class="{ active: activeStatus === tab.key }"
|
||
@click="activeStatus = tab.key"
|
||
>
|
||
<text>{{ tab.label }}({{ tab.count }})</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 3. 鎿嶄綔鎸夐挳琛?-->
|
||
<view class="action-bar">
|
||
<view class="left-actions">
|
||
<button class="btn-add" @click="goEdit(null)">娣诲姞鍟嗗搧</button>
|
||
<button class="btn-collect">鍟嗗搧閲囬泦</button>
|
||
<view class="btn-dropdown">
|
||
<text>鎵归噺淇敼</text>
|
||
<text class="arrow">鈻?/text>
|
||
</view>
|
||
<view class="btn-dropdown">
|
||
<text>鍟嗗搧杩佺Щ</text>
|
||
<text class="arrow">鈻?/text>
|
||
</view>
|
||
<button class="btn-export">鏁版嵁瀵煎嚭</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 4. 鍟嗗搧鍒楄〃琛ㄦ牸 -->
|
||
<view class="list-card">
|
||
<view class="table-v5">
|
||
<view class="th-row">
|
||
<view class="th col-check"><text>鈻?/text></view>
|
||
<view class="th col-id"><text>鍟嗗搧ID</text></view>
|
||
<view class="th col-img"><text>鍟嗗搧鍥?/text></view>
|
||
<view class="th col-name"><text>鍟嗗搧鍚嶇О</text></view>
|
||
<view class="th col-activity"><text>鍙備笌娲诲姩</text></view>
|
||
<view class="th col-type"><text>鍟嗗搧绫诲瀷</text></view>
|
||
<view class="th col-price"><text>鍟嗗搧鍞环</text></view>
|
||
<view class="th col-sales"><text>閿€閲?/text></view>
|
||
<view class="th col-stock"><text>搴撳瓨</text></view>
|
||
<view class="th col-sort"><text>鎺掑簭</text></view>
|
||
<view class="th col-status"><text>鐘舵€?/text></view>
|
||
<view class="th col-op"><text>鎿嶄綔</text></view>
|
||
</view>
|
||
|
||
<view v-for="(item, index) in productList" :key="index"
|
||
class="tr-row"
|
||
:style="{ zIndex: activeDropdownId === item.id ? 1000 : 1 }"
|
||
>
|
||
<view class="td col-check"><text>鈻?/text></view>
|
||
<view class="td col-id"><text>{{ item.id }}</text></view>
|
||
<view class="td col-img">
|
||
<image class="p-img" :src="item.image" mode="aspectFill" />
|
||
</view>
|
||
<view class="td col-name">
|
||
<text class="p-name-txt">{{ item.name }}</text>
|
||
</view>
|
||
<view class="td col-activity">
|
||
<view class="activity-tags">
|
||
<text v-for="tag in item.activities" :key="tag" class="tag" :class="tag">{{ getActivityName(tag) }}</text>
|
||
<text v-if="item.activities.length === 0">-</text>
|
||
</view>
|
||
</view>
|
||
<view class="td col-type"><text>{{ item.typeName }}</text></view>
|
||
<view class="td col-price"><text>{{ item.price }}</text></view>
|
||
<view class="td col-sales"><text>{{ item.sales }}</text></view>
|
||
<view class="td col-stock"><text>{{ item.stock }}</text></view>
|
||
<view class="td col-sort"><text>{{ item.sort }}</text></view>
|
||
<view class="td col-status">
|
||
<view class="mock-switch" :class="{ on: item.status === 1 }">
|
||
<text class="switch-txt">{{ item.status === 1 ? '涓婃灦' : '涓嬫灦' }}</text>
|
||
<view class="switch-dot"></view>
|
||
</view>
|
||
</view>
|
||
<view class="td col-op op-group">
|
||
<text class="op-link" @click.stop="goEdit(item.id)">缂栬緫</text>
|
||
<text class="op-divider">|</text>
|
||
<view class="more-hover-box"
|
||
@mouseenter="activeDropdownId = item.id"
|
||
@mouseleave="activeDropdownId = null"
|
||
>
|
||
<view class="more-trigger-txt">
|
||
<text class="more-link-text">鏇村</text>
|
||
<text class="more-arrow-icon">鈭?/text>
|
||
</view>
|
||
<view class="dropdown-list-container" v-show="activeDropdownId === item.id">
|
||
<view class="dropdown-triangle"></view>
|
||
<view class="dropdown-menu-body">
|
||
<text class="menu-item" @click.stop="goReviews(item.id)">鏌ョ湅璇勮</text>
|
||
<text class="menu-item" @click.stop="goMemberPrice(item.id)">浼氬憳浠风鐞?/text>
|
||
<text class="menu-item" @click.stop="uni.showToast({title:'浣i噾绠$悊寮€鍙戜腑', icon:'none'})">浣i噾绠$悊</text>
|
||
<text class="menu-item danger-item" @click.stop="moveToRecycle(item.id)">{{ activeStatus === 'recycle' ? '鎭㈠鍟嗗搧' : '绉诲埌鍥炴敹绔? }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 5. 鍒嗛〉 -->
|
||
<view class="pagination-row">
|
||
<text class="total">鍏?{{ total }} 鏉?/text>
|
||
<view class="page-ctrl">
|
||
<text class="page-btn disabled">{"<"}</text>
|
||
<text class="page-num active">1</text>
|
||
<text class="page-num">2</text>
|
||
<text class="page-btn">{">"}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import { ref } from 'vue'
|
||
import { openRoute } from '@/layouts/admin/store/adminNavStore.uts'
|
||
|
||
const total = ref(49)
|
||
const activeStatus = ref('selling')
|
||
const activeDropdownId = ref<number | null>(null)
|
||
|
||
const statusTabs = ref([
|
||
{ key: 'selling', label: '鍑哄敭涓殑鍟嗗搧', count: 49 },
|
||
{ key: 'warehouse', label: '浠撳簱涓殑鍟嗗搧', count: 4 },
|
||
{ key: 'soldout', label: '宸茬粡鍞絼鍟嗗搧', count: 11 },
|
||
{ key: 'alarm', label: '璀︽垝搴撳瓨鍟嗗搧', count: 27 },
|
||
{ key: 'recycle', label: '鍥炴敹绔欑殑鍟嗗搧', count: 176 },
|
||
])
|
||
|
||
const productList = ref([
|
||
{
|
||
id: 963,
|
||
image: 'https://img1.baidu.com/it/u=254065646,3100346083&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
|
||
name: 'UR2024澶忓鏂版濂宠澶嶅彜绾姘涘洿鎰熶竴瀛楄偐鐭T鎭よ~UWG440060',
|
||
activities: ['kj', 'pt'],
|
||
typeName: '鏅€氬晢鍝?,
|
||
price: '0.01',
|
||
sales: 639,
|
||
stock: 1602,
|
||
sort: 9999,
|
||
status: 1
|
||
},
|
||
{
|
||
id: 108,
|
||
image: 'https://img2.baidu.com/it/u=3033501986,2204481084&fm=253&fmt=auto&app=138&f=JPEG?w=569&h=500',
|
||
name: 'FOMIX 铔嬪3妞?杩涘彛澶村眰鐗涚毊姗欒壊鍗曚汉娌欏彂妞匛gg chair璁捐甯堝笀鍗曟鍗曟矙澶村眰鐗涚毊/鍗曟',
|
||
activities: ['pt', 'ms'],
|
||
typeName: '鏅€氬晢鍝?,
|
||
price: '7580.00',
|
||
sales: 14,
|
||
stock: 16638,
|
||
sort: 9999,
|
||
status: 1
|
||
},
|
||
{
|
||
id: 48,
|
||
image: 'https://img0.baidu.com/it/u=1762118431,3101886131&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
|
||
name: '闃胯开杈炬柉瀹樼綉 adidas BBALL CAP COT 鐢峰コ璁粌杩愬姩甯藉瓙FQ5270 浼犲澧ㄦ按钃?浼犲澧ㄦ按钃?鐧?XL',
|
||
activities: ['kj', 'pt', 'ms'],
|
||
typeName: '鏅€氬晢鍝?,
|
||
price: '100.00',
|
||
sales: 841,
|
||
stock: 2318,
|
||
sort: 9998,
|
||
status: 1
|
||
}
|
||
])
|
||
|
||
function getActivityName(tag: string): string {
|
||
if (tag === 'kj') return '鐮嶄环'
|
||
if (tag === 'pt') return '鎷煎洟'
|
||
if (tag === 'ms') return '绉掓潃'
|
||
return tag
|
||
}
|
||
|
||
function goEdit(id: number | null) {
|
||
openRoute('product_edit')
|
||
}
|
||
|
||
function goReviews(id: number) {
|
||
openRoute('product_productReply')
|
||
}
|
||
|
||
function goMemberPrice(id: number) {
|
||
openRoute('product_member_price')
|
||
}
|
||
|
||
function moveToRecycle(id: number) {
|
||
const action = activeStatus.value === 'recycle' ? '鎭㈠' : '绉诲埌鍥炴敹绔?;
|
||
uni.showModal({
|
||
title: '鎻愮ず',
|
||
content: `纭瑕佸皢璇ュ晢鍝?{action}鍚楋紵`,
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.showToast({ title: '鎿嶄綔鎴愬姛', icon: 'success' });
|
||
}
|
||
}
|
||
})
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.product-list-page {
|
||
/* padding removed */
|
||
|
||
|
||
}
|
||
|
||
.search-card {
|
||
background: #fff;
|
||
padding: 24px;
|
||
border-radius: 4px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.search-row {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 24px;
|
||
}
|
||
.mt-12 { margin-top: 12px; }
|
||
|
||
.search-item {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
.label { font-size: 14px; color: #606266; width: 80px; text-align: right; }
|
||
}
|
||
|
||
.mock-input {
|
||
width: 200px;
|
||
height: 32px;
|
||
border: 1px solid #dcdfe6;
|
||
border-radius: 4px;
|
||
padding: 0 12px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.mock-select {
|
||
width: 160px;
|
||
height: 32px;
|
||
border: 1px solid #dcdfe6;
|
||
border-radius: 4px;
|
||
padding: 0 12px;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
font-size: 13px;
|
||
color: #606266;
|
||
.arrow { font-size: 10px; color: #c0c4cc; }
|
||
}
|
||
|
||
.search-btns {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.btn-primary { background: #1890ff; color: #fff; height: 32px; padding: 0 16px; border-radius: 4px; font-size: 13px; border: none; }
|
||
.btn-reset { background: #fff; color: #606266; height: 32px; padding: 0 16px; border-radius: 4px; font-size: 13px; border: 1px solid #dcdfe6; }
|
||
.expand-control { display: flex; flex-direction: row; align-items: center; gap: 4px; cursor: pointer; }
|
||
.expand-txt { font-size: 13px; color: #1890ff; }
|
||
.expand-arrow { font-size: 10px; color: #1890ff; }
|
||
|
||
.status-tabs-wrap {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.status-tabs {
|
||
display: flex;
|
||
flex-direction: row;
|
||
border-bottom: 2px solid #f0f2f5;
|
||
}
|
||
|
||
.tab-item {
|
||
padding: 12px 20px;
|
||
font-size: 14px;
|
||
color: #606266;
|
||
position: relative;
|
||
cursor: pointer;
|
||
&.active {
|
||
color: #1890ff;
|
||
&::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: -2px;
|
||
left: 0;
|
||
right: 0;
|
||
height: 2px;
|
||
background: #1890ff;
|
||
}
|
||
}
|
||
}
|
||
|
||
.action-bar {
|
||
margin-bottom: 16px;
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.left-actions {
|
||
display: flex;
|
||
flex-direction: row;
|
||
gap: 12px;
|
||
align-items: center;
|
||
}
|
||
|
||
.btn-add { background: #1890ff; color: #fff; height: 32px; padding: 0 12px; border-radius: 4px; font-size: 13px; border: none; }
|
||
.btn-collect { background: #13ce66; color: #fff; height: 32px; padding: 0 12px; border-radius: 4px; font-size: 13px; border: none; }
|
||
.btn-export { background: #fff; color: #606266; height: 32px; padding: 0 12px; border-radius: 4px; font-size: 13px; border: 1px solid #dcdfe6; }
|
||
|
||
.btn-dropdown {
|
||
height: 32px;
|
||
padding: 0 12px;
|
||
border: 1px solid #dcdfe6;
|
||
border-radius: 4px;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-size: 13px;
|
||
color: #606266;
|
||
.arrow { font-size: 10px; color: #c4cc; }
|
||
}
|
||
|
||
.list-card {
|
||
background: #fff;
|
||
border-radius: 4px;
|
||
padding-bottom: 20px;
|
||
position: relative;
|
||
overflow: visible;
|
||
}
|
||
|
||
.table-v5 {
|
||
width: 100%;
|
||
overflow: visible;
|
||
}
|
||
|
||
.th-row {
|
||
display: flex;
|
||
flex-direction: row;
|
||
background-color: #f2f2f2;
|
||
}
|
||
|
||
.th {
|
||
padding: 12px 8px;
|
||
font-size: 13px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.tr-row {
|
||
display: flex;
|
||
flex-direction: row;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
position: relative;
|
||
overflow: visible;
|
||
&:hover { background-color: #fafafa; }
|
||
}
|
||
|
||
.td {
|
||
padding: 12px 8px;
|
||
font-size: 13px;
|
||
color: #606266;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.col-check { width: 50px; }
|
||
.col-id { width: 80px; }
|
||
.col-img { width: 80px; }
|
||
.col-name { flex: 1; justify-content: flex-start; text-align: left; }
|
||
.col-activity { width: 140px; }
|
||
.col-type { width: 100px; }
|
||
.col-price { width: 100px; }
|
||
.col-sales { width: 80px; }
|
||
.col-stock { width: 80px; }
|
||
.col-sort { width: 80px; }
|
||
.col-status { width: 100px; }
|
||
.col-op { width: 150px; }
|
||
|
||
.op-group {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.more-hover-box {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 40px;
|
||
padding: 0 10px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.more-trigger-txt {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
pointer-events: none; /* 璁╀簨浠堕€忎紶缁?more-hover-box */
|
||
}
|
||
|
||
.more-link-text {
|
||
font-size: 13px;
|
||
color: #1890ff;
|
||
}
|
||
|
||
.more-arrow-icon {
|
||
font-size: 10px;
|
||
color: #1890ff;
|
||
margin-left: 2px;
|
||
}
|
||
|
||
.dropdown-list-container {
|
||
position: absolute;
|
||
top: 35px;
|
||
right: -10px;
|
||
width: 120px;
|
||
padding-top: 8px; /* 澧炲姞鎰熷簲杩炵画鎬?*/
|
||
z-index: 9999;
|
||
}
|
||
|
||
.dropdown-triangle {
|
||
position: absolute;
|
||
top: 2px;
|
||
right: 25px;
|
||
width: 0;
|
||
height: 0;
|
||
border-left: 6px solid transparent;
|
||
border-right: 6px solid transparent;
|
||
border-bottom: 6px solid #fff;
|
||
}
|
||
|
||
.dropdown-menu-body {
|
||
background: #fff;
|
||
border-radius: 4px;
|
||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding: 6px 0;
|
||
}
|
||
|
||
.menu-item {
|
||
padding: 8px 16px;
|
||
font-size: 14px;
|
||
color: #606266;
|
||
text-align: center;
|
||
line-height: 1.5;
|
||
&:hover {
|
||
background-color: #f5f7fa;
|
||
color: #1890ff;
|
||
}
|
||
}
|
||
|
||
.danger-item {
|
||
&:hover {
|
||
color: #ff4d4f;
|
||
}
|
||
}
|
||
|
||
.p-img { width: 40px; height: 40px; border-radius: 4px; }
|
||
.p-name-txt { font-size: 13px; line-height: 1.4; color: #333; }
|
||
|
||
.activity-tags {
|
||
display: flex;
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
gap: 4px;
|
||
.tag {
|
||
padding: 2px 4px;
|
||
font-size: 11px;
|
||
color: #fff;
|
||
border-radius: 2px;
|
||
&.kj { background: #1890ff; }
|
||
&.pt { background: #52c41a; }
|
||
&.ms { background: #f5222d; }
|
||
}
|
||
}
|
||
|
||
.mock-switch {
|
||
width: 50px;
|
||
height: 20px;
|
||
background: #dbdbdb;
|
||
border-radius: 10px;
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0 4px;
|
||
&.on {
|
||
background: #1890ff;
|
||
.switch-dot { left: 32px; }
|
||
.switch-txt { left: 6px; }
|
||
}
|
||
&:not(.on) {
|
||
.switch-txt { right: 6px; }
|
||
.switch-dot { left: 2px; }
|
||
}
|
||
.switch-txt {
|
||
position: absolute;
|
||
font-size: 10px;
|
||
color: #fff;
|
||
}
|
||
.switch-dot {
|
||
position: absolute;
|
||
width: 16px;
|
||
height: 16px;
|
||
background: #fff;
|
||
border-radius: 50%;
|
||
transition: left 0.2s;
|
||
}
|
||
}
|
||
|
||
.op-link {
|
||
font-size: 13px;
|
||
color: #1890ff;
|
||
cursor: pointer;
|
||
&.danger { color: #ff4d4f; }
|
||
}
|
||
.op-divider { color: #e8e8e8; font-size: 12px; margin: 0 4px; }
|
||
|
||
.more-dropdown {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
.arrow { font-size: 10px; color: #1890ff; margin-left: 2px; }
|
||
}
|
||
|
||
.pagination-row {
|
||
padding: 24px;
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: flex-end;
|
||
align-items: center;
|
||
gap: 16px;
|
||
.total { font-size: 13px; color: #606266; }
|
||
}
|
||
|
||
.page-ctrl {
|
||
display: flex;
|
||
flex-direction: row;
|
||
gap: 8px;
|
||
.page-num, .page-btn {
|
||
width: 32px;
|
||
height: 32px;
|
||
border: 1px solid #dcdfe6;
|
||
border-radius: 4px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 13px;
|
||
color: #606266;
|
||
&.active { background: #1890ff; color: #fff; border-color: #1890ff; }
|
||
&.disabled { color: #c0c4cc; background: #f5f7fa; }
|
||
}
|
||
}
|
||
</style>
|
||
|