feat: 实现商品详情页、用户信息页面和搜索功能
refactor: 重构商品列表和主页布局 style: 优化UI组件样式和交互效果 docs: 更新类型定义和路由配置 fix: 修复购物车商品重复问题 perf: 提升页面加载性能和响应速度 test: 添加商品详情页和搜索功能的测试用例 build: 更新依赖项以支持新功能 chore: 清理无用代码和文件
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<Herde></Herde>
|
||||
<RouterView />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { RouterView } from "vue-router";
|
||||
|
||||
import Herde from "@/Views/Herde.vue";
|
||||
import './Style/App.css'
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
@@ -3,6 +3,12 @@ import { createRouter, createWebHistory } from 'vue-router'
|
||||
// 导入组件
|
||||
import Home from '../Views/Home.vue'
|
||||
import Login from '../Views/Login.vue'
|
||||
import Search from '../Views/Search.vue'
|
||||
import ProductDetail from '../Views/product/productdetil.vue'
|
||||
import Cart from '../Views/Cart.vue'
|
||||
import Order from '../Views/Order.vue'
|
||||
|
||||
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@@ -10,11 +16,31 @@ const routes = [
|
||||
name: 'home',
|
||||
component: Home
|
||||
},
|
||||
{
|
||||
path: '/search',
|
||||
name: 'search',
|
||||
component: Search
|
||||
},
|
||||
{
|
||||
path: '/product',
|
||||
name: 'productDetail',
|
||||
component: ProductDetail
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: Login
|
||||
},
|
||||
{
|
||||
path: '/cart',
|
||||
name: 'cart',
|
||||
component: Cart
|
||||
},
|
||||
{
|
||||
path: '/order',
|
||||
name: 'order',
|
||||
component: Order
|
||||
},
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
|
||||
13
src/Style/App.css
Normal file
13
src/Style/App.css
Normal file
@@ -0,0 +1,13 @@
|
||||
/* 全局样式 */
|
||||
body {
|
||||
height: 100%;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
@@ -7,6 +7,45 @@ export type ProductDetail = {
|
||||
price: string
|
||||
img: string
|
||||
}
|
||||
// 店铺详情类型
|
||||
export type ShopDetail = {
|
||||
id: string
|
||||
name: string
|
||||
img: string
|
||||
}
|
||||
// 购物车类型
|
||||
export type Cart = {
|
||||
id: string
|
||||
name: string
|
||||
price: string
|
||||
img: string
|
||||
count: number
|
||||
}
|
||||
|
||||
// 商品类型
|
||||
export type Product = {
|
||||
id: string
|
||||
name: string
|
||||
price: string
|
||||
img: string
|
||||
model: string
|
||||
}
|
||||
|
||||
// 订单详情类型
|
||||
export type OrderDetail = {
|
||||
id: string
|
||||
name: string
|
||||
price: string
|
||||
img: string
|
||||
count: number
|
||||
}
|
||||
// 注册类型
|
||||
export type Register = {
|
||||
username: string
|
||||
password: string
|
||||
confirmPassword: string
|
||||
}
|
||||
// 登录类型
|
||||
export type Login = {
|
||||
username: string
|
||||
password: string
|
||||
|
||||
1377
src/Views/Cart.vue
Normal file
1377
src/Views/Cart.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,10 @@
|
||||
<template>
|
||||
<div id="home">
|
||||
<Header></Header>
|
||||
<Main></Main>
|
||||
<Footer></Footer>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Header from "./herde.vue";
|
||||
import Main from "./main.vue";
|
||||
import Footer from "./footer.vue";
|
||||
import Main from "@/Views/Main.vue";
|
||||
import Footer from "@/Views/Footer.vue";
|
||||
</script>
|
||||
<style scoped>
|
||||
#home {
|
||||
height: 100%;
|
||||
padding: 0 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
2
src/Views/Myttw.vue
Normal file
2
src/Views/Myttw.vue
Normal file
@@ -0,0 +1,2 @@
|
||||
<!-- 我的ttw -->
|
||||
<!-- 页面布局 左边 导航栏有二级菜单 可以跳转很多页面 列如订单页面 购物车等 -->
|
||||
18
src/Views/Order.vue
Normal file
18
src/Views/Order.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<!-- 订单页面 -->
|
||||
<template>
|
||||
<div id="order">
|
||||
<h1>订单</h1>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Button, Col, Row, Carousel } from 'ant-design-vue';
|
||||
const router = useRouter()
|
||||
</script>
|
||||
<style scoped>
|
||||
#order {
|
||||
width: 100%;
|
||||
padding: 0 20px;
|
||||
}
|
||||
</style>
|
||||
46
src/Views/Search.vue
Normal file
46
src/Views/Search.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<!-- 搜索结果页面 -->
|
||||
<template>
|
||||
<div id="search-container">
|
||||
<productList :productList="productLists" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import productList from './product/productList.vue'
|
||||
import Herde from "@/Views/Herde.vue";
|
||||
import router from '@/Route/route';
|
||||
|
||||
const productLists = ref([
|
||||
{
|
||||
id: '1',
|
||||
name: '商品1',
|
||||
price: '100',
|
||||
img: 'https://example.com/product1.jpg',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: '商品2',
|
||||
price: '200',
|
||||
img: 'https://example.com/product2.jpg',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: '商品3',
|
||||
price: '300',
|
||||
img: 'https://example.com/product3.jpg',
|
||||
}
|
||||
]);
|
||||
// 监听路由参数变化
|
||||
watch(() => router.currentRoute.value.query.keyword, (newValue) => {
|
||||
if (newValue) {
|
||||
// 根据关键词搜索商品
|
||||
//data(newValue)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
#search-container {
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
</style>
|
||||
19
src/Views/Shop.vue
Normal file
19
src/Views/Shop.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<!-- 店铺详情 -->
|
||||
<template>
|
||||
<div id="shop-detail">
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { type PropType } from 'vue'
|
||||
import type { ShopDetail } from '@/Util/Type'
|
||||
import { useGlobalStore } from '@/Util/globalStore'
|
||||
import router from '@/Route/route'
|
||||
// 接收父类传递的店铺详情
|
||||
const props = defineProps({
|
||||
shopDetail: {
|
||||
type: Object as PropType<ShopDetail>,
|
||||
default: () => {},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
29
src/Views/User/Userinfo.vue
Normal file
29
src/Views/User/Userinfo.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<!-- 用户信息 -->
|
||||
<!-- 布局 从上到下 user信息:用户头像 用户名 -->
|
||||
<template>
|
||||
<div class="user-info">
|
||||
<div class="user-info-top">
|
||||
<div class="user-avatar">
|
||||
<!-- <img :src="user.avatar" alt="用户头像"> -->
|
||||
</div>
|
||||
<div class="user-name">
|
||||
<div>用户名:</div>
|
||||
<div>关注店铺 收货地址</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-info-main">
|
||||
<div>
|
||||
购物信息 :购物车 待收货 代发货 待付款
|
||||
</div>
|
||||
<div>
|
||||
订单信息 :已完成 待评价 待付款 待发货
|
||||
</div>
|
||||
<div>
|
||||
优惠 :红包 优惠卷 淘币
|
||||
</div>
|
||||
<div>
|
||||
足迹信息 : 已买完 收藏夹 购买过的店 足迹信息
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -42,12 +42,23 @@ const productLists = ref([
|
||||
price: '400',
|
||||
img: '',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: '商品4',
|
||||
price: '400',
|
||||
img: '',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: '商品4',
|
||||
price: '400',
|
||||
img: '',
|
||||
},
|
||||
])
|
||||
</script>
|
||||
<style scoped>
|
||||
#footer {
|
||||
width: 100%;
|
||||
background-color: #f5f5f5;
|
||||
padding: 0 20px;
|
||||
}
|
||||
h1 {
|
||||
|
||||
@@ -2,26 +2,88 @@
|
||||
<div id="header">
|
||||
<div id="header-profile">
|
||||
<div id="header-profile-left">
|
||||
<!-- 登录 注册 -->
|
||||
<Button type="primary" @click="router.push('/login')">登录</Button>
|
||||
<Button type="primary" @click="router.push('/register')">注册</Button>
|
||||
<!-- 用户信息悬浮菜单 -->
|
||||
<Dropdown :menu="{ items: userMenu }" trigger="hover">
|
||||
<div class="user-info-container">
|
||||
<Avatar size="large" src="https://api.dicebear.com/7.x/avataaars/svg?seed=user123" />
|
||||
<span class="user-name">用户名</span>
|
||||
<span class="user-arrow">▼</span>
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div id="header-profile-right">
|
||||
<!-- 购物车 个人中心 -->
|
||||
<!-- 右侧功能菜单 -->
|
||||
<Dropdown :menu="{ items: rightMenu }" trigger="hover">
|
||||
<Button type="text">更多</Button>
|
||||
</Dropdown>
|
||||
<a href="/">首页</a>
|
||||
<Button type="primary" @click="router.push('/cart')">购物车</Button>
|
||||
<Button type="primary" @click="router.push('/user')">个人中心</Button>
|
||||
<Button type="primary" @click="router.push('/order')">订单</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Row id="header-nav-row">
|
||||
<Col :span="4">
|
||||
<Row id="header-nav-row" v-if="booleanSearch">
|
||||
<Col class="header-nav-Logo" :span="4">
|
||||
<a href="/">TaoTaoWang</a>
|
||||
</Col>
|
||||
<Col :span="16">
|
||||
<a-input-search v-model:value="value" placeholder="请输入" style="width: 200px"
|
||||
@search="onSearch" />
|
||||
<Col class="header-nav-search" :span="16">
|
||||
<div class="search-input-container" :class="{ 'search-focused': showHistory }">
|
||||
<div class="search-wrapper">
|
||||
<!-- 搜索类型选择 -->
|
||||
<div class="search-type-selector">
|
||||
<ul>
|
||||
<li class="search-type-item active" @click="setSearchType('宝贝')">宝贝</li>
|
||||
<li class="search-type-item" @click="setSearchType('店铺')">店铺</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 搜索输入区域 -->
|
||||
<div class="search-input-wrapper">
|
||||
<div class="search-input-prefix">
|
||||
<i class="iconfont icon-sousuo prefix-icon"></i>
|
||||
</div>
|
||||
<Input
|
||||
v-model:value="searchValue"
|
||||
placeholder="请输入搜索关键词"
|
||||
style="width: 100%"
|
||||
@search="onSearch"
|
||||
@focus="onInputFocus"
|
||||
@blur="onInputBlur"
|
||||
class="custom-search-input"
|
||||
/>
|
||||
<div class="search-input-suffix" v-if="searchValue">
|
||||
<i class="iconfont icon-guanbi" @click="clearSearch"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜索按钮 -->
|
||||
<div class="search-button-container" @onclick="onSearch">
|
||||
<span class="search-button-text">搜索</span>
|
||||
<div class="search-button-icon">
|
||||
<i class="iconfont icon-sousuo"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-history-container" v-if="showHistory">
|
||||
<div class="search-history-header">
|
||||
<span class="history-title">搜索历史</span>
|
||||
<a class="clear-history" @click="clearHistory" href="javascript:void(0)">
|
||||
清空历史
|
||||
</a>
|
||||
</div>
|
||||
<div class="search-history-tags" v-if="historyList.length > 0">
|
||||
<Tag v-for="(item, index) in historyList" :key="index" class="history-tag" @click="onClickHistory(item)">
|
||||
{{ item }}
|
||||
</Tag>
|
||||
</div>
|
||||
<div class="no-history" v-else>
|
||||
暂无搜索历史
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col :span="4">
|
||||
<a href="/product">商品列表</a>
|
||||
<Col class="header-nav-product" :span="4">
|
||||
<a href="#">随便放点东西显得对称</a>
|
||||
<!-- 其他页面的时候修改布局 -->
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
@@ -30,31 +92,157 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Button, Col, Row ,Space } from 'ant-design-vue';
|
||||
import { Button, Col, Row, Space, Dropdown, Avatar, Menu, Divider, Tag } from 'ant-design-vue';
|
||||
const router = useRouter()
|
||||
// 定义搜索框绑定的变量
|
||||
const value = ref('')
|
||||
const searchValue = ref('')
|
||||
// 控制历史记录显示状态
|
||||
const showHistory = ref(false)
|
||||
// 标志:是否正在清空历史记录
|
||||
const isClearingHistory = ref(false)
|
||||
// 标志:是否在搜索界面
|
||||
const booleanSearch = ref(true)
|
||||
// 路由事件
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (to.name === 'search' && to.query.keyword) {
|
||||
searchValue.value = to.query.keyword as string
|
||||
}
|
||||
console.log(to.name)
|
||||
// 如果在商品页面 隐藏搜索框
|
||||
if (to.name === 'productDetail') {
|
||||
booleanSearch.value = false
|
||||
} else {
|
||||
booleanSearch.value = true
|
||||
}
|
||||
next()
|
||||
})
|
||||
// 定义搜索框的搜索事件
|
||||
const onSearch = (value: string) => {
|
||||
console.log('搜索关键词:', value)
|
||||
// 跳转到搜索结果页面
|
||||
if (value.trim()) {
|
||||
// 添加到历史记录
|
||||
if (!historyList.value.includes(value)) {
|
||||
historyList.value.unshift(value)
|
||||
// 限制历史记录数量
|
||||
if (historyList.value.length > 10) {
|
||||
historyList.value.pop()
|
||||
}
|
||||
}
|
||||
// 判断是否在搜索界面 如果不在 则跳转到搜索界面 如果在搜索界面 则更新查询参数
|
||||
if (router.currentRoute.value.name !== 'search') {
|
||||
router.push({ name: 'search', query: { keyword: value } })
|
||||
} else {
|
||||
router.push({ name: 'search', query: { keyword: value } })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索历史记录
|
||||
const historyList = ref(['手机', '电脑', '耳机', '键盘', '鼠标'])
|
||||
|
||||
// 清空历史记录
|
||||
const clearHistory = () => {
|
||||
isClearingHistory.value = true
|
||||
historyList.value = []
|
||||
// 300ms后重置标志,确保onInputBlur的200ms延迟执行完毕
|
||||
setTimeout(() => {
|
||||
isClearingHistory.value = false
|
||||
}, 300)
|
||||
}
|
||||
|
||||
// 点击历史记录
|
||||
const onClickHistory = (item: string) => {
|
||||
searchValue.value = item
|
||||
onSearch(item)
|
||||
}
|
||||
|
||||
// 搜索框获得焦点
|
||||
const onInputFocus = () => {
|
||||
showHistory.value = true
|
||||
}
|
||||
|
||||
// 搜索框失去焦点
|
||||
const onInputBlur = () => {
|
||||
// 使用setTimeout确保点击历史记录的事件能先执行
|
||||
setTimeout(() => {
|
||||
// 如果正在清空历史记录,则不隐藏历史记录容器
|
||||
if (!isClearingHistory.value) {
|
||||
showHistory.value = false
|
||||
}
|
||||
}, 200)
|
||||
}
|
||||
|
||||
// 设置搜索类型
|
||||
const setSearchType = (type: string) => {
|
||||
const typeItems = document.querySelectorAll('.search-type-item')
|
||||
typeItems.forEach(item => {
|
||||
item.classList.remove('active')
|
||||
})
|
||||
// 添加active类到点击的元素
|
||||
// event?.currentTarget?.classList.add('active')
|
||||
console.log('搜索类型:', type)
|
||||
}
|
||||
|
||||
// 清除搜索输入
|
||||
const clearSearch = () => {
|
||||
searchValue.value = ''
|
||||
console.log('已清除搜索输入')
|
||||
}
|
||||
|
||||
// 左侧用户菜单
|
||||
const userMenu = [
|
||||
{
|
||||
key: '1',
|
||||
label: '个人中心'
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: '设置'
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
label: '退出登录',
|
||||
danger: true
|
||||
}
|
||||
]
|
||||
|
||||
// 右侧功能菜单
|
||||
const rightMenu = [
|
||||
{
|
||||
key: '1',
|
||||
label: '浏览记录'
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: '收藏夹'
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
label: '帮助中心'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
<style scoped>
|
||||
#header {
|
||||
width: 100%;
|
||||
background-color: #f5f5f5;
|
||||
padding: 0 20px;
|
||||
background-color: #ffffff;
|
||||
padding: 10px 20px;
|
||||
/* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); */
|
||||
position: relative;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #42b983;
|
||||
#header-nav-row {
|
||||
height: 60px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#header-profile {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
#header-profile-left {
|
||||
@@ -63,17 +251,512 @@ h1 {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#header-profile-left>button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#header-profile-right {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
#header-profile-right>button {
|
||||
margin-left: 10px;
|
||||
/* 用户信息容器样式 */
|
||||
.user-info-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.user-info-container:hover {
|
||||
background-color: #f0f2f5;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.user-arrow {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.user-info-container:hover .user-arrow {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* Logo样式 */
|
||||
.header-nav-Logo a {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #1890ff;
|
||||
text-decoration: none;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.header-nav-Logo a:hover {
|
||||
color: #40a9ff;
|
||||
}
|
||||
|
||||
/* 搜索框样式 */
|
||||
.header-nav-search {
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 700px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.search-input-container {
|
||||
width: 100%;
|
||||
display: block;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
.search-input-container.search-focused {
|
||||
/* box-shadow: 0 4px 16px rgba(255, 80, 0, 0.2); */
|
||||
}
|
||||
|
||||
/* 搜索框整体布局 */
|
||||
.search-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #ffffff;
|
||||
border: 2px solid #e8e8e8;
|
||||
border-radius: 28px;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.search-input-container.search-focused .search-wrapper {
|
||||
border-color: #ff5000;
|
||||
box-shadow: 0 4px 20px rgba(255, 80, 0, 0.2);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* 搜索类型选择器 */
|
||||
.search-type-selector {
|
||||
padding: 3.5px 16px;
|
||||
border-right: 1px solid #e8e8e8;
|
||||
background-color: #fafafa;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
border-radius: 28px 0 0 28px;
|
||||
}
|
||||
|
||||
.search-type-selector:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.search-type-selector ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.search-type-item {
|
||||
padding: 14px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
position: relative;
|
||||
user-select: none;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.search-type-item:hover {
|
||||
color: #ff5000;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.search-type-item.active {
|
||||
color: #ff5000;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.search-type-item.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: #ff5000;
|
||||
border-radius: 1px;
|
||||
animation: slideIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
width: 0;
|
||||
left: 50%;
|
||||
}
|
||||
to {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 搜索输入区域 */
|
||||
.search-input-wrapper {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-input-prefix {
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #999;
|
||||
font-size: 18px;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.search-input-container.search-focused .search-input-prefix {
|
||||
color: #ff5000;
|
||||
transform: translateY(-50%) scale(1.1);
|
||||
}
|
||||
|
||||
.custom-search-input {
|
||||
font-size: 16px;
|
||||
min-height: 52px;
|
||||
height: 52px;
|
||||
padding: 0 50px 0 48px;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.5px;
|
||||
box-sizing: border-box; /* 关键! */
|
||||
outline: none; /* 避免默认焦点轮廓 */
|
||||
}
|
||||
|
||||
/* 可选:焦点状态 */
|
||||
.custom-search-input:focus {
|
||||
background-color: rgba(255, 255, 255, 0.03);
|
||||
}
|
||||
|
||||
.custom-search-input::placeholder {
|
||||
color: #ccc;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.custom-search-input:focus {
|
||||
border-color: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.search-input-container.search-focused .custom-search-input::placeholder {
|
||||
color: #ffccb3;
|
||||
}
|
||||
|
||||
.search-input-suffix {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #999;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
background-color: #f5f5f5;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
animation: fadeIn 0.3s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-50%) scale(0.8);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(-50%) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.search-input-suffix:hover {
|
||||
color: #ff5000;
|
||||
background-color: #fff0e6;
|
||||
transform: translateY(-50%) scale(1.1);
|
||||
}
|
||||
|
||||
.search-input-container.search-focused .search-input-suffix {
|
||||
color: #ff5000;
|
||||
}
|
||||
|
||||
/* 搜索按钮 */
|
||||
.search-button-container {
|
||||
margin-right: 1px;
|
||||
padding: 15.5px;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, #ff5000, #ff8c00);
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 0 28px 28px 0;
|
||||
}
|
||||
|
||||
|
||||
.search-button-container:active {
|
||||
transform: translateX(1px) translateY(0);
|
||||
box-shadow: 0 2px 8px rgba(255, 80, 0, 0.3);
|
||||
}
|
||||
|
||||
.search-button-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
||||
transform: rotate(45deg);
|
||||
animation: shine 2.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* @keyframes shine {
|
||||
0% {
|
||||
transform: rotate(45deg) translateX(-100%) translateY(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
100% {
|
||||
transform: rotate(45deg) translateX(100%) translateY(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
} */
|
||||
|
||||
.search-button-text {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
letter-spacing: 1px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.search-button-icon {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
font-size: 16px;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.search-button-container:hover .search-button-icon {
|
||||
transform: scale(1.1) rotate(15deg);
|
||||
}
|
||||
|
||||
.search-button-container:hover .search-button-text {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 768px) {
|
||||
.search-type-selector {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.search-type-selector ul {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.search-type-item {
|
||||
padding: 12px 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.search-input-wrapper {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.custom-search-input {
|
||||
font-size: 14px !important;
|
||||
height: 48px !important;
|
||||
padding: 0 40px 0 40px !important;
|
||||
}
|
||||
|
||||
.search-input-prefix {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.search-button-container {
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.search-button-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.search-button-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.search-button-container:hover .search-button-icon {
|
||||
transform: scale(1.05) rotate(10deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.search-type-selector {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.search-type-selector ul {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.search-type-item {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.search-input-wrapper {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.custom-search-input {
|
||||
font-size: 13px !important;
|
||||
padding: 0 35px 0 35px !important;
|
||||
}
|
||||
|
||||
.search-input-prefix {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.search-button-container {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.search-button-text {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.search-button-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 搜索历史样式 */
|
||||
.search-history-container {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
margin-top: 4px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
z-index: 100;
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.search-history-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.history-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.clear-history {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
text-decoration: none;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.clear-history:hover {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.search-history-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.history-tag {
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 14px;
|
||||
padding: 4px 12px;
|
||||
background-color: #f5f5f5;
|
||||
border-color: #e8e8e8;
|
||||
}
|
||||
|
||||
.history-tag:hover {
|
||||
background-color: #e6f7ff;
|
||||
border-color: #91d5ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.no-history {
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 商品列表链接样式 */
|
||||
.header-nav-product a {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.header-nav-product a:hover {
|
||||
color: #1890ff;
|
||||
background-color: #e6f7ff;
|
||||
}
|
||||
</style>
|
||||
@@ -1,45 +1,54 @@
|
||||
<template>
|
||||
<div id="main">
|
||||
<div id="main-header">
|
||||
<Button type="primary" @click="router.push('/')">按钮1</Button>
|
||||
<Button type="primary" @click="router.push('/list')">按钮2</Button>
|
||||
<Button type="primary" @click="router.push('/cart')">按钮3</Button>
|
||||
<Button type="primary" @click="router.push('/history')">按钮4</Button>
|
||||
<Button type="primary" @click="router.push('/user')">按钮5</Button>
|
||||
<Button type="primary" @click="router.push('/login')">按钮6</Button>
|
||||
<Button type="primary" @click="router.push('/register')">按钮7</Button>
|
||||
</div>
|
||||
<Row id="main-content">
|
||||
<Col class="main-content-col-top" :span="6">
|
||||
<h2>商品列表</h2>
|
||||
<Row id="main-content" :wrap="false">
|
||||
<Col class="main-content-col-top" :span="4">
|
||||
<div>
|
||||
<ul>
|
||||
<li>
|
||||
<!-- 分类项 -->
|
||||
<div class="category-item">
|
||||
<i class="iconfont icon-zhineng"></i>
|
||||
<a href="http://localhost:5173/search?keyword=电脑" >电脑</a>
|
||||
<span>/</span>
|
||||
<a href="http://localhost:5173/search?keyword=配件" >配件</a>
|
||||
<span>/</span>
|
||||
<a href="http://localhost:5173/search?keyword=办公" >办公</a>
|
||||
<span>/</span>
|
||||
<a href="http://localhost:5173/search?keyword=文具" >文具</a>
|
||||
</div>
|
||||
<!-- 分类详情 -->
|
||||
<div v-if="true" class="category-detail">
|
||||
<ul>
|
||||
<li class="category-detail-item">
|
||||
<a href="http://localhost:5173/search?keyword=电脑整机">电脑整机</a>
|
||||
<span >></span>
|
||||
<a href="http://localhost:5173/search?keyword=笔记本" >笔记本</a>
|
||||
<a href="http://localhost:5173/search?keyword=台式机" >台式机</a>
|
||||
<a href="http://localhost:5173/search?keyword=服务器" >服务器</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Col>
|
||||
<Col class="main-content-col-center" :span="12">
|
||||
<Row id="main-content-ad">
|
||||
<Col :span="12">
|
||||
<h2>轮动广告</h2>
|
||||
<Col class="main-content-col-center" :span="15">
|
||||
<!-- 轮动广告 -->
|
||||
<div id="carousel-container">
|
||||
<Carousel autoplay :dots="true" effect="fade" :autoplay-speed="3000">
|
||||
<div v-for="ad in adList" :key="ad.id" class="carousel-item">
|
||||
<a :href="ad.link" target="_blank" rel="noopener noreferrer">
|
||||
<img :src="ad.image" :alt="ad.title" class="carousel-image" />
|
||||
<div class="carousel-title">{{ ad.title }}</div>
|
||||
</a>
|
||||
</div>
|
||||
</Carousel>
|
||||
</div>
|
||||
</Col>
|
||||
<Col :span="12">
|
||||
<h2>百亿补贴</h2>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row id="main-content-hot-goods">
|
||||
<!-- 热门商品展示 -->
|
||||
<Col :span="6">
|
||||
<h2>热门商品1</h2>
|
||||
</Col>
|
||||
<Col :span="6">
|
||||
<h2>热门商品2</h2>
|
||||
</Col>
|
||||
<Col :span="6">
|
||||
<h2>热门商品3</h2>
|
||||
</Col>
|
||||
<Col :span="6">
|
||||
<h2>热门商品4</h2>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col class="main-content-col-bottom" :span="6">
|
||||
<h2>个人信息</h2>
|
||||
<Col class="main-content-col-bottom" :span="4">
|
||||
<UserInfo />
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
@@ -48,22 +57,382 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Button, Col, Row } from 'ant-design-vue';
|
||||
import { Button, Col, Row, Carousel } from 'ant-design-vue';
|
||||
import UserInfo from '@/Views/User/Userinfo.vue'
|
||||
const router = useRouter()
|
||||
|
||||
// 按钮数据数组
|
||||
const buttonList = [
|
||||
{ id: 1, text: '按钮1', route: '/' },
|
||||
{ id: 2, text: '按钮2', route: '/list' },
|
||||
{ id: 3, text: '按钮3', route: '/cart' },
|
||||
{ id: 4, text: '按钮4', route: '/history' },
|
||||
{ id: 5, text: '按钮5', route: '/user' },
|
||||
{ id: 6, text: '按钮6', route: '/login' },
|
||||
{ id: 7, text: '按钮7', route: '/register' }
|
||||
]
|
||||
|
||||
// 广告数据
|
||||
const adList = [
|
||||
{ id: 1, title: '广告1', image: 'https://picsum.photos/id/237/800/300', link: '#' },
|
||||
{ id: 2, title: '广告2', image: 'https://picsum.photos/id/238/800/300', link: '#' },
|
||||
{ id: 3, title: '广告3', image: 'https://picsum.photos/id/239/800/300', link: '#' }
|
||||
]
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
#main {
|
||||
width: 100%;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #42b983;
|
||||
}
|
||||
|
||||
#main-header {
|
||||
/* 根据中心对齐 分布按钮 */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
padding: 8px 0;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
padding: 16px 0;
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#main-header .ant-btn {
|
||||
width: 120px;
|
||||
height: 45px;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
#main-header .ant-btn-primary {
|
||||
background-color: #1890ff;
|
||||
border-color: #1890ff;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
#main-header .ant-btn-primary:hover {
|
||||
background-color: #40a9ff;
|
||||
border-color: #40a9ff;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
|
||||
}
|
||||
/* 商品分类样式 */
|
||||
.main-content-col-top {
|
||||
padding: 20px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
.main-content-col-top:hover {
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
|
||||
@keyframes shine {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
20% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
.main-content-col-top ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.main-content-col-top li {
|
||||
margin-bottom: 12px;
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* 分类详情样式 */
|
||||
.main-content-col-top li:hover {
|
||||
background-color: #fff5f5;
|
||||
transform: translateX(8px);
|
||||
box-shadow: 0 2px 8px rgba(255, 80, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 分类项样式 */
|
||||
.category-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
.category-item:hover {
|
||||
background-color: rgba(255, 80, 0, 0.05);
|
||||
}
|
||||
|
||||
/* 分类详情样式 */
|
||||
.category-detail {
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: -60px;
|
||||
width: 600px;
|
||||
height: 400px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
|
||||
padding: 20px;
|
||||
margin-left: 10px;
|
||||
z-index: 9999;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: translateX(-20px);
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
border: 2px solid #f0f0f0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/* 悬停显示分类详情 */
|
||||
.main-content-col-top li:hover .category-detail {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.category-detail ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.category-detail-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.category-detail-item:hover {
|
||||
background-color: #f9f9f9;
|
||||
padding-left: 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.category-detail-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.category-detail-item a {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.category-detail-item a:hover {
|
||||
color: #ff5000;
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
.category-detail-item a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 1px;
|
||||
background-color: #ff5000;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.category-detail-item a:hover::after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.category-detail-item span {
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.main-content-col-top .iconfont {
|
||||
font-size: 22px;
|
||||
color: #ff5000;
|
||||
flex-shrink: 0;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
.main-content-col-top li:hover .iconfont {
|
||||
transform: scale(1.1) rotate(5deg);
|
||||
color: #ff5000;
|
||||
}
|
||||
|
||||
.main-content-col-top a {
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.main-content-col-top a:hover {
|
||||
color: #ff5000;
|
||||
background-color: rgba(255, 80, 0, 0.1);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.main-content-col-top span {
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.main-content-col-top li:hover span {
|
||||
color: #ff5000;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 主内容区域样式 */
|
||||
#main-content {
|
||||
margin-top: 20px;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.main-content-col-center{
|
||||
background-color: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.main-content-col-center h2{
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
background-color: #fafafa;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#main-content-ad,
|
||||
#main-content-hot-goods {
|
||||
padding: 0 20px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
#main-content-ad .ant-col,
|
||||
#main-content-hot-goods .ant-col {
|
||||
background-color: #fafafa;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
#main-content-ad .ant-col:hover,
|
||||
#main-content-hot-goods .ant-col:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
/* 轮播广告样式 */
|
||||
#carousel-container {
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.carousel-item {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.carousel-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.carousel-item:hover .carousel-image {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.carousel-title {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
|
||||
color: #ffffff;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.ant-carousel .slick-dots {
|
||||
bottom: 20px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.ant-carousel .slick-dots li button {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 50%;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.ant-carousel .slick-dots li.slick-active button {
|
||||
background-color: #ffffff;
|
||||
width: 20px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* 个人信息样式 */
|
||||
.main-content-col-bottom {
|
||||
padding: 20px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.main-content-col-bottom h2 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
background-color: #fafafa;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -1,21 +1,23 @@
|
||||
<!-- 根据父类传递的商品列表渲染商品列表 -->
|
||||
<template>
|
||||
<div id="product-list">
|
||||
<div id="product-list-header" v-for="product in productList" :key="product.id">
|
||||
<div class="product-item" v-for="product in productList" :key="product.id">
|
||||
<!-- 这个商品的id是加密 -->
|
||||
<a href="http://localhost:5173/product/{{ product.id }}" target="_blank">
|
||||
<div id="product-list-header-img">
|
||||
<img class="product-list-header-img" :src="product.img || '/0.png'" alt="商品图片">
|
||||
<div class="product-list-header-img-mask">
|
||||
<div class="product-img-container" @click="handleClick(product)">
|
||||
<img class="product-img" :src="product.img || '/0.png'" alt="商品图片">
|
||||
<div class="product-img-mask">
|
||||
<div class="product-img-actions">
|
||||
<span class="action-btn">查看详情</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="product-list-header-content">
|
||||
<h2>{{ product.name }}</h2>
|
||||
</div>
|
||||
<div id="product-list-header-price">
|
||||
<h2>{{ product.price }}</h2>
|
||||
<div class="product-content">
|
||||
<h3 class="product-name">{{ product.name }}</h3>
|
||||
<div class="product-price">
|
||||
<span class="price-symbol">¥</span>
|
||||
<span class="price-value">{{ product.price }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -23,6 +25,7 @@
|
||||
import { type PropType } from 'vue'
|
||||
import type { ProductDetail } from '@/Util/Type'
|
||||
import { useGlobalStore } from '@/Util/globalStore'
|
||||
import router from '@/Route/route'
|
||||
// 接收父类传递的商品列表
|
||||
const props = defineProps({
|
||||
productList: {
|
||||
@@ -30,7 +33,13 @@ const props = defineProps({
|
||||
default: () => [],
|
||||
},
|
||||
})
|
||||
|
||||
// 点击商品跳转详情页
|
||||
const handleClick = (product: ProductDetail) => {
|
||||
// id 加密
|
||||
const encryptedId = btoa(product.id)
|
||||
// 跳转详情页 (新开页面)
|
||||
window.open(`/product?id=${encryptedId}`, '_blank')
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
/* 自定义css */
|
||||
@@ -39,47 +48,242 @@ const props = defineProps({
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #42b983;
|
||||
/* 商品项 */
|
||||
.product-item {
|
||||
flex: 0 0 calc(16.666666% - 16.666666px);
|
||||
max-width: calc(16.666666% - 16.666666px);
|
||||
background-color: #ffffff;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 商品 */
|
||||
#product-list-header {
|
||||
max-width: 16.666666666666%;
|
||||
height: initial;
|
||||
margin: 8px;
|
||||
.product-item:hover {
|
||||
transform: translateY(-6px);
|
||||
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
#product-list-header-img {
|
||||
.product-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, #ff5000, #ff8c00);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.product-item:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 商品链接 */
|
||||
.product-item a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 商品图片容器 */
|
||||
.product-img-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
padding-top: 100%; /* 1:1 宽高比 */
|
||||
overflow: hidden;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 16px 16px 0 0;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
.product-list-header-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 8px;
|
||||
.product-item:hover .product-img-container {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
/* 鼠标图片悬浮亮度变低 */
|
||||
.product-list-header-img-mask {
|
||||
.product-img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
background-color: transparent;
|
||||
transition: all 0.3s ease;
|
||||
z-index: 999999;
|
||||
object-fit: cover;
|
||||
transition: transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
#product-list-header-img:hover .product-list-header-img-mask {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
.product-item:hover .product-img {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* 图片遮罩层 */
|
||||
.product-img-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
border-radius: 16px 16px 0 0;
|
||||
}
|
||||
|
||||
.product-item:hover .product-img-mask {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 图片悬停操作按钮 */
|
||||
.product-img-actions {
|
||||
transform: translateY(20px);
|
||||
opacity: 0;
|
||||
transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
.product-item:hover .product-img-actions {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
background-color: #ffffff;
|
||||
color: #ff5000;
|
||||
padding: 10px 20px;
|
||||
border-radius: 25px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
box-shadow: 0 4px 12px rgba(255, 80, 0, 0.2);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
background-color: #ff5000;
|
||||
color: #ffffff;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(255, 80, 0, 0.4);
|
||||
}
|
||||
|
||||
.action-btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
||||
transform: rotate(45deg);
|
||||
animation: shine 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes shine {
|
||||
0% {
|
||||
transform: translateX(-100%) rotate(45deg);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%) rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 商品内容 */
|
||||
.product-content {
|
||||
padding: 20px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.product-item:hover .product-content {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.product-name {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin: 0 0 12px 0;
|
||||
line-height: 1.4;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.product-item:hover .product-name {
|
||||
color: #ff5000;
|
||||
}
|
||||
|
||||
/* 商品价格 */
|
||||
.product-price {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 6px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.price-symbol {
|
||||
font-size: 14px;
|
||||
color: #ff5000;
|
||||
font-weight: 500;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.product-item:hover .price-symbol {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.price-value {
|
||||
font-size: 20px;
|
||||
color: #ff5000;
|
||||
font-weight: 700;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.product-item:hover .price-value {
|
||||
transform: scale(1.05);
|
||||
text-shadow: 0 1px 2px rgba(255, 80, 0, 0.3);
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 1200px) {
|
||||
.product-item {
|
||||
flex: 0 0 calc(20% - 16px);
|
||||
max-width: calc(20% - 16px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.product-item {
|
||||
flex: 0 0 calc(25% - 15px);
|
||||
max-width: calc(25% - 15px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.product-item {
|
||||
flex: 0 0 calc(33.333333% - 13.333333px);
|
||||
max-width: calc(33.333333% - 13.333333px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.product-item {
|
||||
flex: 0 0 calc(50% - 10px);
|
||||
max-width: calc(50% - 10px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,27 +1,809 @@
|
||||
<!-- 商品详情页面 -->
|
||||
<template>
|
||||
<template>
|
||||
<div id="product-detail">
|
||||
<van-row id="product-detail-header">
|
||||
<van-col :span="24">
|
||||
<h2>商品详情</h2>
|
||||
</van-col>
|
||||
</van-row>
|
||||
<div class="product-detail-container">
|
||||
<div class="left-panel">
|
||||
<div class="shop-info">
|
||||
<!-- 店铺信息 -->
|
||||
<div>
|
||||
<!-- 店铺头像 -->
|
||||
<Avatar size="large" src="https://api.dicebear.com/7.x/avataaars/svg?seed=user123" />
|
||||
</div>
|
||||
<!-- 店铺名称 店铺评分 -->
|
||||
<div class="shop-info-item">
|
||||
<div class="shop-info-label">店铺名称
|
||||
<div class="shop-info-detail">
|
||||
<div class="shop-info-detail-item">
|
||||
<div class="shop-info-detail-label">店铺详情</div>
|
||||
<div class="shop-info-detail-value">店铺详情内容</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shop-info-value">店铺评分</div>
|
||||
</div>
|
||||
<!-- 按钮 客服 进店 -->
|
||||
<div class="shop-btn-group">
|
||||
<a href="#" class="btn btn-primary"><i class="iconfont icon-kefu"></i><span>客服</span></a>
|
||||
<a href="#" class="btn btn-primary"><i class="iconfont icon-xiangyou"></i><span>进店</span></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gallery-container">
|
||||
<div class="thumbnails">
|
||||
<div v-for="(img, index) in images" :key="index" class="thumbnail-item"
|
||||
:class="{ active: currentIndex === index }" @click="selectImage(index)">
|
||||
<img :src="img" :alt="'缩略图' + (index + 1)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="main-image">
|
||||
<a-image class="main-image-container" :src="currentImage" alt="商品主图" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-container">
|
||||
<div class="tab-header">
|
||||
<a-anchor direction="horizontal" :items="tabs" class="tab-item" />
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
<!-- 评论 -->
|
||||
<div id="reviews">
|
||||
<h2>
|
||||
评论
|
||||
<a href="#reviews"></a>
|
||||
</h2>
|
||||
<div class="reviews-content">
|
||||
<!-- 评论内容 -->
|
||||
<h3>用户评论</h3>
|
||||
<!-- 更多评论... -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- 文图详情 -->
|
||||
<div id="description">
|
||||
<h2>
|
||||
文图详情
|
||||
<a href="#description"></a>
|
||||
</h2>
|
||||
</div>
|
||||
<!-- 参数信息 -->
|
||||
<div id="specs">
|
||||
<h2>
|
||||
参数信息
|
||||
<a href="#specs"></a>
|
||||
</h2>
|
||||
</div>
|
||||
<!-- 本店推荐 -->
|
||||
<div id="recommendations">
|
||||
<h2>
|
||||
本店推荐
|
||||
<a href="#recommendations"></a>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-panel">
|
||||
<div class="product-info">
|
||||
<h1 class="product-title">BenQ 明基投影仪 商务办公会议培训用 1080P高清智能投影机</h1>
|
||||
<div class="price-section">
|
||||
<div class="price-row">
|
||||
<span class="price-label">券后价</span>
|
||||
<span class="price-value">{{ selectedModelInfo?.price }}</span>
|
||||
<span class="original-price">{{ selectedModelInfo?.originalPrice }}</span>
|
||||
</div>
|
||||
<div class="tags">
|
||||
<span class="tag">官方正品</span>
|
||||
<span class="tag">全国包邮</span>
|
||||
<span class="tag">7天无理由退换</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="model-selection">
|
||||
<div class="selection-container">
|
||||
<h3 class="selection-title">选择型号</h3>
|
||||
<div class="view-switch" @click="toggleView">
|
||||
<p>{{ isSmallView ? '切换为大视图' : '切换为小视图' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 图(小)文从左到右 -->
|
||||
<div v-if="isSmallView" class="model-options-small">
|
||||
<div v-for="model in models" :key="model.id" class="model-option"
|
||||
:class="{ active: selectedModel === model.id }" @click="selectModel(model.id)">
|
||||
<div class="model-small-item">
|
||||
<img :src="model.img" alt="{{ model.name }}">
|
||||
<span> {{ model.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 图(大)文 图上文下-->
|
||||
<div v-if="!isSmallView" class="model-options-big">
|
||||
<div v-for="model in models" :key="model.id" class="model-option"
|
||||
:class="{ active: selectedModel === model.id }" @click="selectModel(model.id)">
|
||||
<!-- 图片 -->
|
||||
<div class="model-image">
|
||||
<img :src="model.img" alt="{{ model.name }}">
|
||||
</div>
|
||||
<div class="model-name">{{ model.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-section">
|
||||
<div class="action-buttons">
|
||||
<div @click="addToCart" class="btn-cart">加入购物车</div>
|
||||
<div @click="buyProduct" class="btn-buy">领券购买</div>
|
||||
</div>
|
||||
<div class="btn-collect" @click="collectProduct">
|
||||
<i class="iconfont icon-xiangyou"></i>
|
||||
收藏
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
// 根据路由获取商品id
|
||||
const productId = ref(router.currentRoute.value.params.productId)
|
||||
// 在根据service获取商品详情
|
||||
|
||||
const currentIndex = ref(0)
|
||||
const images = ref([
|
||||
'/0.png',
|
||||
'/0.png',
|
||||
'/0.png',
|
||||
'/0.png',
|
||||
'/0.png'
|
||||
])
|
||||
|
||||
const currentImage = computed(() => images.value[currentIndex.value])
|
||||
|
||||
const selectImage = (index: number) => {
|
||||
currentIndex.value = index
|
||||
}
|
||||
|
||||
const selectedModel = ref('x500')
|
||||
const models = ref([
|
||||
{ id: 'x500', name: 'X500', price: '999', originalPrice: '1999', img: '/0.png' },
|
||||
{ id: 'w600', name: 'W600', price: '1299', originalPrice: '2499', img: '/0.png' },
|
||||
{ id: 'e800', name: 'E800', price: '1599', originalPrice: '3299', img: '/0.png' }
|
||||
])
|
||||
|
||||
const selectModel = (id: string) => {
|
||||
selectedModel.value = id
|
||||
}
|
||||
const selectedModelInfo = computed(() => models.value.find(model => model.id === selectedModel.value))
|
||||
|
||||
const activeTab = ref('reviews')
|
||||
const tabs = ref([
|
||||
{
|
||||
key: 'reviews',
|
||||
href: '#reviews',
|
||||
title: '评论',
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
href: '#description',
|
||||
title: '文图详情',
|
||||
},
|
||||
{
|
||||
key: 'specs',
|
||||
href: '#specs',
|
||||
title: '参数信息',
|
||||
},
|
||||
{
|
||||
key: 'recommendations',
|
||||
href: '#recommendations',
|
||||
title: '本店推荐',
|
||||
}
|
||||
])
|
||||
|
||||
const isSmallView = ref(true)
|
||||
|
||||
const toggleView = () => {
|
||||
isSmallView.value = !isSmallView.value
|
||||
}
|
||||
|
||||
const collectProduct = () => {
|
||||
console.log('收藏商品')
|
||||
}
|
||||
|
||||
const addToCart = () => {
|
||||
console.log('加入购物车')
|
||||
}
|
||||
|
||||
const buyProduct = () => {
|
||||
console.log('领券购买')
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
#product-detail {
|
||||
width: 100%;
|
||||
padding: 0 20px;
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding: 20px;
|
||||
}
|
||||
h2 {
|
||||
color: #42b983;
|
||||
|
||||
.product-detail-container {
|
||||
display: flex;
|
||||
border-radius: 16px;
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #fafafa 100%);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.left-panel {
|
||||
width: 50%;
|
||||
padding: 25px;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #fafafa 100%);
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
margin-bottom: 25px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
|
||||
.shop-info-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.shop-info-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.shop-info-detail {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #fafafa 100%);
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
||||
z-index: 100;
|
||||
min-width: 200px;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: translateY(-10px);
|
||||
transition: all 0.3s ease;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.shop-info-item:hover .shop-info-detail {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.shop-info-value {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
.shop-btn-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.shop-btn-group .btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.shop-btn-group .btn-primary {
|
||||
background: linear-gradient(135deg, #ff5000 0%, #ff6b00 100%);
|
||||
color: #ffffff;
|
||||
box-shadow: 0 2px 8px rgba(255, 80, 0, 0.2);
|
||||
}
|
||||
|
||||
.shop-btn-group .btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(255, 80, 0, 0.3);
|
||||
}
|
||||
|
||||
.shop-btn-group .btn span {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.gallery-container {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.main-image {
|
||||
flex: 1;
|
||||
aspect-ratio: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.thumbnails {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
overflow-y: auto;
|
||||
max-height: 400px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.thumbnail-item {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.thumbnail-item:hover {
|
||||
border-color: #ff5000;
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 2px 8px rgba(255, 80, 0, 0.2);
|
||||
}
|
||||
|
||||
.thumbnail-item.active {
|
||||
border-color: #ff5000;
|
||||
box-shadow: 0 4px 12px rgba(255, 80, 0, 0.3);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.thumbnail-item img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
flex: 1;
|
||||
padding: 35px;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #fafafa 100%);
|
||||
}
|
||||
|
||||
.product-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.product-title {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
line-height: 1.5;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.price-section {
|
||||
background: linear-gradient(135deg, #fff5f5 0%, #ffe8e8 100%);
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #ffcccc;
|
||||
box-shadow: 0 2px 8px rgba(255, 80, 0, 0.08);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.price-section:hover {
|
||||
box-shadow: 0 4px 12px rgba(255, 80, 0, 0.15);
|
||||
}
|
||||
|
||||
.price-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.price-label {
|
||||
font-size: 14px;
|
||||
color: #ff5000;
|
||||
}
|
||||
|
||||
.price-value {
|
||||
font-size: 32px;
|
||||
color: #ff5000;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.original-price {
|
||||
font-size: 16px;
|
||||
color: #999;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 12px;
|
||||
color: #ff5000;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #fff5f5 100%);
|
||||
border: 1px solid #ff5000;
|
||||
padding: 6px 14px;
|
||||
border-radius: 20px;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 6px rgba(255, 80, 0, 0.08);
|
||||
}
|
||||
|
||||
.tag:hover {
|
||||
background: linear-gradient(135deg, #ff5000 0%, #ff6b00 100%);
|
||||
color: #ffffff;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(255, 80, 0, 0.25);
|
||||
}
|
||||
|
||||
.model-selection {
|
||||
border-top: 1px solid #f0f0f0;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.selection-title {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
margin: 0 0 15px 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.selection-container {
|
||||
display: flex;
|
||||
}
|
||||
/* 切换视图 */
|
||||
.view-switch {
|
||||
/* 靠右对齐 */
|
||||
margin-left: auto;
|
||||
margin-right: 10px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.view-switch:hover {
|
||||
color: #ff5000;
|
||||
}
|
||||
.model-options-small, .model-options-big {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.model-option {
|
||||
flex: 0 0 120px;
|
||||
border: 2px solid #f0f0f0;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-align: center;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #fafafa 100%);
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.model-option:hover {
|
||||
border-color: #ff5000;
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 4px 12px rgba(255, 80, 0, 0.15);
|
||||
}
|
||||
|
||||
.model-option.active {
|
||||
border-color: #ff5000;
|
||||
background: linear-gradient(135deg, #fff5f5 0%, #ffe8e8 100%);
|
||||
box-shadow: 0 4px 16px rgba(255, 80, 0, 0.25);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
.model-image {
|
||||
border-radius: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.model-image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
.model-name {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.model-small-item {
|
||||
text-align: left;
|
||||
border-radius: 8px;
|
||||
flex: 0 0 120px;
|
||||
transition: all 0.3s ease;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #fafafa 100%);
|
||||
}
|
||||
.model-small-item img {
|
||||
margin: 5px;
|
||||
height: 30px;
|
||||
align-items: left;
|
||||
}
|
||||
|
||||
.model-price {
|
||||
font-size: 18px;
|
||||
color: #ff5000;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.action-section {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
bottom: 30px;
|
||||
right: 30px;
|
||||
z-index: 1000;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #fafafa 100%);
|
||||
padding: 18px;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||||
border: 1px solid #f0f0f0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.btn-cart,
|
||||
.btn-buy {
|
||||
text-align: center;
|
||||
width: 200px;
|
||||
line-height: 48px;
|
||||
flex: 1;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 24px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.btn-cart {
|
||||
background: linear-gradient(135deg, #ffffff 0%, #fff5f5 100%);
|
||||
color: #ff5000;
|
||||
border: 2px solid #ff5000;
|
||||
}
|
||||
|
||||
.btn-cart:hover {
|
||||
background: linear-gradient(135deg, #ff5000 0%, #ff6b00 100%);
|
||||
color: #ffffff;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(255, 80, 0, 0.3);
|
||||
}
|
||||
|
||||
.btn-buy {
|
||||
background: linear-gradient(135deg, #ff5000 0%, #ff6b00 100%);
|
||||
color: #ffffff;
|
||||
box-shadow: 0 4px 12px rgba(255, 80, 0, 0.3);
|
||||
}
|
||||
|
||||
.btn-buy:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(255, 80, 0, 0.4);
|
||||
}
|
||||
|
||||
.btn-collect {
|
||||
flex: 1;
|
||||
line-height: 48px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #fff5f5 100%);
|
||||
color: #ff5000;
|
||||
margin-left: 10px;
|
||||
border: 2px solid #ff5000;
|
||||
border-radius: 24px;
|
||||
padding: 0 20px;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.btn-collect:hover {
|
||||
background: linear-gradient(135deg, #ff5000 0%, #ff6b00 100%);
|
||||
color: #ffffff;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(255, 80, 0, 0.3);
|
||||
}
|
||||
|
||||
.auxiliary-actions {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.aux-item {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.aux-item:hover {
|
||||
color: #ff5000;
|
||||
}
|
||||
|
||||
.tab-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tab-header {
|
||||
display: flex;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
padding: 18px 35px;
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tab-item:hover {
|
||||
color: #ff5000;
|
||||
background-color: #fff5f5;
|
||||
}
|
||||
|
||||
.tab-item.active {
|
||||
color: #ff5000;
|
||||
font-weight: 600;
|
||||
background-color: #fff5f5;
|
||||
}
|
||||
|
||||
.tab-item.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, #ff5000 0%, #ff6b00 100%);
|
||||
box-shadow: 0 2px 8px rgba(255, 80, 0, 0.3);
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
padding: 30px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.tab-panel {
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.review-item {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.review-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.review-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.reviewer {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.review-rating {
|
||||
font-size: 16px;
|
||||
color: #ffc107;
|
||||
}
|
||||
|
||||
.review-text {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.params-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.params-table td {
|
||||
padding: 15px;
|
||||
border: 1px solid #f0f0f0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.params-table td:first-child {
|
||||
background-color: #fafafa;
|
||||
color: #666;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.params-table td:last-child {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.thumbnails::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.thumbnails::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
.thumbnails::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.thumbnails::-webkit-scrollbar-thumb:hover {
|
||||
background: #999;
|
||||
}
|
||||
|
||||
.left-panel::-webkit-scrollbar,
|
||||
.right-panel::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.left-panel::-webkit-scrollbar-track,
|
||||
.right-panel::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
.left-panel::-webkit-scrollbar-thumb,
|
||||
.right-panel::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.left-panel::-webkit-scrollbar-thumb:hover,
|
||||
.right-panel::-webkit-scrollbar-thumb:hover {
|
||||
background: #999;
|
||||
}
|
||||
</style>
|
||||
226
src/Views/product/productmodal.vue
Normal file
226
src/Views/product/productmodal.vue
Normal file
@@ -0,0 +1,226 @@
|
||||
<!-- 根据数组渲染产品详情弹窗 -->
|
||||
<template>
|
||||
<a-modal v-model:open="showModel" @ok="handleOk">
|
||||
<div class="product-modal">
|
||||
<div class="product-modal-container">
|
||||
<div class="product-modal-right">
|
||||
<div class="product-modal-img">
|
||||
<img :src="currentProduct.img" alt="产品图片" class="product-modal-image">
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-modal-left">
|
||||
<div class="product-modal-top">
|
||||
<div class="product-modal-price">
|
||||
<span class="price-label">价格:</span>
|
||||
<span class="price-value">¥{{ currentProduct.price }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-modal-bottom">
|
||||
<h3 class="model-title">选择型号</h3>
|
||||
<div class="model-list">
|
||||
<div v-for="item in currentItems" :key="item.id" class="model-item"
|
||||
:class="{ active: currentProduct.id === item.id }" @click="selectProduct(item)">
|
||||
<div class="model-img">
|
||||
<img :src="item.img" alt="型号图片" class="model-image">
|
||||
</div>
|
||||
<div class="model-name">{{ item.model }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import type { Product } from '@/Util/Type';
|
||||
|
||||
const props = defineProps({
|
||||
currentItems: {
|
||||
type: Array as () => Product[],
|
||||
default: () => [],
|
||||
},
|
||||
showModel: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['selectProduct', 'update:showModel'])
|
||||
// 监听 showModel 变化 用于更新弹窗的显示状态
|
||||
watch(() => props.showModel, (newVal) => {
|
||||
showModel.value = newVal
|
||||
})
|
||||
const showModel = ref(false)
|
||||
const currentProduct = ref<Product>(props.currentItems[0] || {} as Product)
|
||||
// 选择型号 并触发事件 用于更新当前选中的型号
|
||||
const selectProduct = (item: Product) => {
|
||||
currentProduct.value = item
|
||||
}
|
||||
// 点击确认按钮 触发事件 用于更新父组件的 selectedModel
|
||||
const handleOk = () => {
|
||||
emit('selectProduct', currentProduct.value)
|
||||
showModel.value = false
|
||||
emit('update:showModel', false)
|
||||
}
|
||||
// 监听弹窗关闭事件
|
||||
watch(() => showModel.value, (newVal) => {
|
||||
emit('update:showModel', newVal)
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.product-modal {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.product-modal-container {
|
||||
display: flex;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.product-modal-right {
|
||||
flex: 1;
|
||||
padding: 10 15px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.product-modal-img {
|
||||
width: 100%;
|
||||
max-width: 200px;
|
||||
max-height: 200px;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.product-modal-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.product-modal-image:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.product-modal-left {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.product-modal-top {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.product-modal-title {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
margin: 0 0 20px 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.product-modal-price {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.price-label {
|
||||
font-size: 18px;
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.price-value {
|
||||
font-size: 32px;
|
||||
color: #ff5000;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.product-modal-bottom {
|
||||
flex: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 20px;
|
||||
overflow-y: auto;
|
||||
|
||||
}
|
||||
|
||||
.model-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
|
||||
.model-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.model-item {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
padding: 5px 12px;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #fafafa 100%);
|
||||
border: 2px solid #f0f0f0;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.model-item:hover {
|
||||
border: 2px solid #ff5000;
|
||||
}
|
||||
|
||||
.model-item.active {
|
||||
border-color: #ff5000;
|
||||
background: linear-gradient(135deg, #fff5f5 0%, #ffffff 100%);
|
||||
box-shadow: 0 4px 12px rgba(255, 80, 0, 0.2);
|
||||
}
|
||||
|
||||
.model-img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.model-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.model-name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.model-price {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #ff5000;
|
||||
}
|
||||
|
||||
.model-item.active .model-name {
|
||||
color: #ff5000;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user