购物车

This commit is contained in:
李志鹏
2026-04-23 11:48:22 +08:00
parent 32a801b24f
commit b0ee5a0783
11 changed files with 689 additions and 37 deletions

View File

@@ -95,7 +95,7 @@
{
icon: 'cart_0',
active_icon: 'cart_1',
path: '/cart'
path: '/shoppingCart'
},
{
icon: 'user_0',
@@ -132,7 +132,7 @@
}
</script>
<style lang="less">
<style lang="less" scoped>
#main-header {
height: var(--header-height);
display: flex;

View File

@@ -0,0 +1,45 @@
<template>
<div class="shopping-cart">
<div class="content">
<sc-list />
<sc-list is-mini style="flex: 0.6; height: var(--app-view-height)" />
<!-- <order-summary /> -->
</div>
<my-footer />
</div>
</template>
<script setup lang="ts">
import { computed, onMounted, defineComponent, onActivated } from 'vue'
import orderSummary from './order-summary.vue'
import scList from './sc-list.vue'
import myFooter from '@/components/Footer.vue'
</script>
<style lang="less" scoped>
.shopping-cart {
width: 100%;
height: 100%;
overflow: hidden;
overflow-y: auto;
--content-top: 4.8rem;
> .content {
max-width: 126rem;
padding-top: var(--content-top);
margin: 0 auto;
min-height: var(--app-view-height);
display: flex;
align-items: flex-start;
> .sc-list {
flex: 1;
margin-right: 7.5rem;
--sc-list-header-top: var(--content-top);
}
> .order-summary {
position: sticky;
top: var(--content-top);
max-height: var(--app-view-height);
}
}
}
</style>

View File

@@ -0,0 +1,160 @@
<template>
<div class="order-summary">
<div class="title">Order Summary</div>
<div class="count">
<span class="label">Selected</span>
<span class="value">3</span>
</div>
<div class="hr"></div>
<div class="brands-header">
<span class="icon"><svg-icon name="order-shop" size="18" /></span>
<span class="text">Brands</span>
</div>
<div class="brands-item" v-for="v in brandsList" :key="v.name">
<span class="label">{{ v.name }}</span>
<span class="value"
><span>{{ v.count }}</span
>item</span
>
</div>
<br />
<div class="total-file-size">
<span class="label">
<span class="icon"><svg-icon name="order-file" size="18" /></span>
<span class="text">Total File Size</span>
</span>
<span class="value">36 <span>mb</span></span>
</div>
<div class="hr"></div>
<br />
<div class="total">
<span class="label">Total</span>
<span class="value"><span>$45.00</span> HKD</span>
</div>
<div class="hr"></div>
<button class="checkout-btn" custom="black">CHECKOUT SELECTED</button>
<div class="tip">Digital assets. Creator retains copyright.</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
const brandsList = ref([
{
name: 'Roaming Clouds',
count: 1
},
{
name: 'Off Grid Apparel',
count: 1
},
{
name: 'Ivory Muse Studio',
count: 1
}
])
</script>
<style lang="less" scoped>
.order-summary {
width: 39.8rem;
padding: 3rem 2rem 3rem 2.4rem;
height: auto;
background-color: #f6f6f6;
> .title {
font-family: KaiseiOpti-Bold;
font-size: 2.4rem;
line-height: 2.3rem;
color: #232323;
margin-bottom: 1.8rem;
}
> div {
display: flex;
justify-content: space-between;
align-items: center;
}
> .count {
color: #232323;
> .label {
font-size: 1.6rem;
}
> .value {
font-size: 1.4rem;
}
}
> .hr {
margin: 1.2rem 0;
width: 100%;
height: 0;
border-top: 0.1rem solid #c4c4c4;
}
> .brands-header {
justify-content: flex-start;
margin-bottom: 1rem;
> .icon {
width: 2.4rem;
height: 2.4rem;
margin-right: 0.4rem;
}
> .text {
font-size: 1.4rem;
color: #232323;
}
}
> .brands-item {
margin-bottom: 0.8rem;
padding-left: 1rem;
font-size: 1.2rem;
> .label {
text-decoration: underline;
color: #585858;
}
> .value {
color: #808080;
&:deep(span) {
font-size: 1.4rem;
color: #585858;
margin-right: 0.8rem;
}
}
}
> .total-file-size {
> .label {
display: flex;
align-items: center;
> .icon {
width: 2.4rem;
height: 2.4rem;
margin-right: 0.4rem;
}
}
> .value {
> span {
color: #808080;
}
}
}
> .total {
> .value {
color: #585858;
> span {
font-size: 2.2rem;
color: #232323;
}
}
}
> .checkout-btn {
width: 100%;
margin-top: 3rem;
}
> .tip {
margin-top: 1rem;
font-family: KaiseiOpti-Regular;
font-size: 1.2rem;
justify-content: center;
color: #808080;
}
}
</style>

View File

@@ -0,0 +1,441 @@
<template>
<!-- 购物车列表 -->
<div class="sc-list" :class="{ mini: isMini }">
<div class="header">
<div class="title">
<span class="text">Shopping Cart</span>
<span class="close" v-if="isMini" @click="onClose"
><svg-icon name="close" size="13"
/></span>
</div>
<div class="options" v-if="!isMini">
<div class="left">
<el-checkbox v-model="check" :indeterminate="true" />
<span class="count">3 Selected</span>
<div class="hr"></div>
<div class="btn">Select All</div>
<div class="btn">Deselect All</div>
</div>
<div class="right">
<el-select v-model="sortBy" placeholder="Sort By" :teleported="false">
<template #label="{ label }">
<span class="header-label">Sort By</span>
<span class="header-value">{{ label }}</span>
</template>
<el-option
v-for="item in sortByOptions"
:key="item.label"
:value="item.value"
:label="item.label"
/>
</el-select>
</div>
</div>
</div>
<div class="list">
<div class="item" v-for="v in list" :key="v.id">
<el-checkbox v-model="v.checked" v-if="!isMini" />
<img :src="v.url" />
<div class="content">
<div class="title">{{ v.title }}</div>
<div class="brand">
<span class="icon"><svg-icon name="order-shop" size="24" /></span>
<span class="text">{{ v.brand }}</span>
</div>
<div class="tags" v-if="!isMini">
<span v-for="tag in v.tags" :key="tag" class="tag">{{ tag }}</span>
</div>
<div class="size">
<div class="icon"><svg-icon name="order-file" size="18" /></div>
<div class="text">
{{ v.fileSize }}kb{{
isMini ? '' : ` &nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;${v.date}`
}}
</div>
</div>
</div>
<div class="right">
<div class="amount">${{ v.amount }}<span> HKD</span></div>
<div class="remove">
<span class="icon"><svg-icon name="order-delete" size="18" /></span>
<span class="text">Remove</span>
</div>
</div>
</div>
</div>
<div class="footer" v-if="isMini">
<div class="total">
<span class="label">Total</span>
<span class="value">$45.00<span> HKD</span></span>
</div>
<button custom="black">CHECKOUT</button>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
const emit = defineEmits(['close'])
const props = defineProps({
isMini: {
type: Boolean,
default: false
}
})
const check = ref(true)
const sortBy = ref('')
const sortByOptions = ref([
{
label: 'Default',
value: 'Default'
},
{
label: 'Selected First',
value: 'Selected First'
},
{
label: 'Date Added',
value: 'Date Added'
}
])
const list = ref([
{
id: 1,
url: 'http://118.31.39.42:3000/falls/shopping-cart-1.png',
title: 'North Outfit Set',
brand: 'Roaming Clouds',
fileSize: 1024, // kb
date: 'Feb 16, 2026, 11:34 PM',
amount: 49.99,
tags: ['female', 'skirt', 'blouse', 'outwear'],
checked: true
},
{
id: 2,
url: 'http://118.31.39.42:3000/falls/shopping-cart-2.png',
title: 'Weekend Drift Co-ord',
brand: 'Urban Line Edit',
fileSize: 100, // kb
date: 'Feb 16, 2026, 11:34 PM',
amount: 9.99,
tags: ['female', 'skirt', 'blouse', 'outwear'],
checked: false
}
])
const onClose = () => {
emit('close')
}
</script>
<style lang="less" scoped>
.sc-list {
display: flex;
flex-direction: column;
&.mini {
height: 100%;
overflow: hidden;
--sc-list-title-font-size: 2rem;
--sc-list-list-item-padding: 2rem 0;
--sc-list-list-item-img-width: 10.4rem;
--sc-list-list-item-img-height: 13.2rem;
--sc-list-list-item-content-margin: 0 2rem;
--sc-list-list-item-margin-bottom: 0.8rem;
--sc-list-list-item-title-font-size: 1.6rem;
--sc-list-list-item-brand-font-size: 1.4rem;
--sc-list-list-item-amount-font-size: 1.8rem;
--sc-list-list-item-currency-font-size: 1.6rem;
> .list {
flex: 1;
min-height: 30rem;
overflow-y: auto;
> .item {
> .content {
align-self: baseline;
}
> .right {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: flex-end;
height: var(--sc-list-list-item-img-height);
}
}
}
}
&:not(.mini) {
> .header {
position: sticky;
top: var(--sc-list-header-top, 0);
background-color: #fff;
z-index: 999;
&:after {
content: '';
position: absolute;
top: 1px;
left: 0;
width: 100%;
height: calc(var(--sc-list-header-top, 0) + 2px);
transform: translateY(-100%);
background-color: #fff;
}
}
}
> .header {
border-bottom: 0.1rem solid #c4c4c4;
padding-bottom: 3rem;
> .title {
display: flex;
justify-content: space-between;
align-items: center;
> .text {
font-family: KaiseiOpti-Bold;
font-size: var(--sc-list-title-font-size, 4rem);
color: #121212;
}
> .close {
width: 2.4rem;
height: 2.4rem;
cursor: pointer;
}
}
> .options {
margin-top: 1rem;
display: flex;
justify-content: space-between;
> .left {
display: flex;
align-items: center;
> .count {
font-family: KaiseiOpti-Regular;
font-size: 1.8rem;
color: #232323;
}
> .hr {
width: 0;
height: 100%;
border-left: 0.1rem solid #c4c4c4;
margin: 0 2rem;
}
> .btn {
margin-right: 1.2rem;
font-family: KaiseiOpti-Regular;
font-size: 1.8rem;
color: #979797;
text-decoration: underline;
user-select: none;
cursor: pointer;
}
}
}
}
> .list {
// height: 1000px;
> .item {
border-bottom: 0.1rem solid #c4c4c4;
padding: var(--sc-list-list-item-padding, 2.4rem 0);
display: flex;
align-items: center;
> img {
width: var(--sc-list-list-item-img-width, 14.8rem);
height: var(--sc-list-list-item-img-height, 18.8rem);
object-fit: contain;
background-color: #f6f6f6;
}
> .content {
flex: 1;
margin: var(--sc-list-list-item-content-margin, 0 4rem);
> * {
margin-bottom: var(--sc-list-list-item-margin-bottom, 1.6rem);
&:last-child {
margin-bottom: 0;
}
}
> .title {
font-family: KaiseiOpti-Bold;
font-size: var(--sc-list-list-item-title-font-size, 2.4rem);
color: #232323;
}
> .brand {
display: flex;
align-items: center;
> .icon {
width: 2.4rem;
height: 2.4rem;
margin-right: 1rem;
}
> .text {
font-size: var(--sc-list-list-item-brand-font-size, 1.6rem);
text-decoration: underline;
color: #232323;
}
}
> .tags {
display: flex;
flex-wrap: wrap;
gap: 0.8rem;
> .tag {
min-width: 8.8rem;
height: 2.4rem;
line-height: 2.4rem;
border-radius: 2.4rem;
font-size: 1.4rem;
padding: 0 1rem;
text-align: center;
color: #8f8f8f;
background-color: #eee;
}
}
> .size {
display: flex;
align-items: center;
> .icon {
width: 2.4rem;
height: 2.4rem;
margin-right: 1rem;
color: #808080;
}
> .text {
font-family: KaiseiOpti-Regular;
font-size: 1.4rem;
color: #808080;
}
}
}
> .right {
align-self: end;
> .amount {
font-family: KaiseiOpti-Bold;
font-size: var(--sc-list-list-item-amount-font-size, 2.2rem);
color: #232323;
> span {
font-size: var(--sc-list-list-item-currency-font-size, 1.4rem);
color: #585858;
vertical-align: bottom;
}
}
> .remove {
margin-top: var(--sc-list-list-item-remove-margin-top, 9rem);
display: flex;
align-items: center;
justify-content: center;
user-select: none;
cursor: pointer;
> .icon {
width: 2rem;
height: 2rem;
margin-right: 0.4rem;
}
> .text {
font-family: KaiseiOpti-Regular;
font-size: 1.4rem;
color: #808080;
}
}
}
}
}
> .footer {
margin-top: 3rem;
> .total {
display: flex;
justify-content: space-between;
align-items: center;
> .label {
font-family: KaiseiOpti-Bold;
font-size: 1.6rem;
color: #121212;
}
> .value {
font-family: KaiseiOpti-Bold;
font-size: 2rem;
color: #232323;
> span {
font-family: KaiseiOpti-Medium;
font-size: 1.6rem;
color: #585858;
vertical-align: bottom;
}
}
}
> button {
width: 100%;
height: 4.6rem;
--button-font-size: 1.4rem;
margin-top: 2rem;
}
}
}
.sc-list:deep(.el-checkbox) {
margin-right: 3rem;
height: auto;
--el-checkbox-font-size: 1.4rem;
--el-checkbox-input-width: 2.4rem;
--el-checkbox-input-height: 2.4rem;
--el-checkbox-checked-bg-color: #000;
--el-checkbox-checked-input-border-color: #000;
--el-checkbox-input-border: 0.1rem solid #232323;
--el-checkbox-bg-color: #fff;
--el-checkbox-border-radius: 0;
.el-checkbox__input.is-indeterminate .el-checkbox__inner:before {
height: 0.2rem;
width: 65%;
top: 50%;
left: 50%;
transform: scale(1) translate(-50%, -50%);
border-radius: 0.1rem;
}
.el-checkbox__inner:after {
width: 0.6rem;
height: 1.3rem;
border-width: 0.2rem;
}
}
.sc-list:deep(.el-select) {
width: 15rem;
--el-border-radius-base: 0;
--el-select-input-color: rgba(0, 0, 0, 0.5);
--el-select-input-font-size: 1rem;
.el-select__wrapper {
font-size: 1.07rem;
padding: 0 0.7rem;
line-height: 1;
min-height: 0;
height: 2.2rem;
.header-label {
font-family: KaiseiOpti-Regular;
color: rgba(0, 0, 0, 0.5);
margin-right: 0.6rem;
}
.header-value {
font-family: KaiseiOpti-Bold;
color: #232323;
}
}
.el-select__popper {
--el-popper-border-radius: 0;
border: 0.1rem solid #d0d0d0;
.el-select-dropdown__list {
padding: 0;
> .el-select-dropdown__item {
margin-bottom: 0.89rem;
color: #232323;
font-size: 1.069rem;
height: 2.68rem;
line-height: 2.68rem;
padding: 0 1.4rem;
&:last-child {
margin-bottom: 0;
}
&.is-selected {
font-family: KaiseiOpti-Bold;
background-color: #f4f4f4;
}
}
}
}
}
</style>