购物车

This commit is contained in:
李志鹏
2026-04-23 16:11:13 +08:00
parent 98e2548f01
commit ad81feaf81
4 changed files with 213 additions and 183 deletions

View File

@@ -27,7 +27,6 @@
}) })
onMounted(() => { onMounted(() => {
observer.observe(viewRef.value) observer.observe(viewRef.value)
console.log('onMounted')
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
observer.disconnect() observer.disconnect()

View File

@@ -2,8 +2,9 @@
<div class="shopping-cart"> <div class="shopping-cart">
<div class="content"> <div class="content">
<sc-list @selected-change="(v) => (selectedList = v)" /> <sc-list @selected-change="(v) => (selectedList = v)" />
<!-- <sc-list is-mini is-view title="Order Summary" /> -->
<order-summary :list="selectedList" /> <order-summary :list="selectedList" />
<!-- <sc-list is-mini style="height: 70rem" /> -->
<!-- <sc-list is-mini is-view title="Order Summary" style="height: 70rem" /> -->
</div> </div>
<my-footer /> <my-footer />
</div> </div>

View File

@@ -0,0 +1,164 @@
<template>
<div class="sc-item">
<slot name="checkbox" />
<img :src="info.url" />
<div class="content">
<div class="title">{{ info.title }}</div>
<div class="brand">
<span class="icon"><svg-icon name="order-shop" size="24" /></span>
<span class="text">{{ info.brand }}</span>
</div>
<div class="tags" v-if="showTags">
<span v-for="tag in info.tags" :key="tag" class="tag">{{ tag }}</span>
</div>
<div class="size" v-if="showSize">
<div class="icon"><svg-icon name="order-file" size="18" /></div>
<div class="text">
<span>{{ FormatBytes(info.fileSize) }}</span>
<span v-if="showSizeDate"
>&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;{{
FormatDate(info.date, 'SM D, YYYY, h:mm A')
}}</span
>
</div>
</div>
</div>
<div class="right">
<div class="amount">${{ info.amount }}<span> HKD</span></div>
<div class="remove" v-if="showRemove" @click="onRemove">
<span class="icon"><svg-icon name="order-delete" size="18" /></span>
<span class="text">Remove</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, onMounted } from 'vue'
import { FormatBytes, FormatDate } from '@/utils/tools'
const emit = defineEmits(['remove'])
const props = defineProps({
showTags: { type: Boolean, default: true },
showSize: { type: Boolean, default: true },
showSizeDate: { type: Boolean, default: true },
showRemove: { type: Boolean, default: true },
info: { type: Object, default: () => {} }
})
const onRemove = () => {
emit('remove', props.info.id)
}
</script>
<style lang="less" scoped>
.sc-item {
border-bottom: 0.1rem solid #c4c4c4;
padding: var(--sc-item-padding, 2.4rem 0);
display: flex;
align-items: center;
> img {
width: var(--sc-item-img-width, 14.8rem);
height: var(--sc-item-img-height, 18.8rem);
object-fit: contain;
background-color: #f6f6f6;
}
> .content {
flex: 1;
margin: var(--sc-item-content-margin, 0 4rem);
align-self: var(--sc-item-content-align-self, auto);
> * {
margin-bottom: var(--sc-item-margin-bottom, 1.6rem);
&:last-child {
margin-bottom: 0;
}
}
> .title {
font-family: KaiseiOpti-Bold;
font-size: var(--sc-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-item-brand-font-size, 1.6rem);
text-decoration: underline;
color: #232323;
}
}
> .tags {
display: flex;
flex-wrap: wrap;
gap: 0.8rem;
> .tag {
min-width: var(--sc-item-tag-min-width, 8.8rem);
height: var(--sc-item-tag-height, 2.4rem);
line-height: var(--sc-item-tag-height, 2.4rem);
border-radius: var(--sc-item-tag-radius, 2.4rem);
font-size: var(--sc-item-tag-font-size, 1.4rem);
padding: var(--sc-item-tag-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: var(--sc-item-right-align-self, end);
display: var(--sc-item-right-display, '');
flex-direction: var(--sc-item-right-flex-direction, '');
justify-content: var(--sc-item-right-justify-content, '');
align-items: var(--sc-item-right-align-items, '');
height: var(--sc-item-right-height, auto);
margin-top: var(--sc-item-right-margin-top, 0);
> .amount {
font-family: KaiseiOpti-Bold;
font-size: var(--sc-item-amount-font-size, 2.2rem);
color: #232323;
> span {
font-size: var(--sc-item-currency-font-size, 1.4rem);
color: #585858;
vertical-align: bottom;
}
}
> .remove {
margin-top: var(--sc-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;
}
}
}
}
</style>

View File

@@ -43,38 +43,20 @@
<div class="tip">Discover new fashion assets and add them to your cart.</div> <div class="tip">Discover new fashion assets and add them to your cart.</div>
<button custom v-if="!isMini" @click="handleExploreClick">EXPLORE DIGITAL ITEMS</button> <button custom v-if="!isMini" @click="handleExploreClick">EXPLORE DIGITAL ITEMS</button>
</div> </div>
<div class="item" v-for="v in list" :key="v.id"> <sc-item
<el-checkbox v-model="v.checked" v-if="!isMini" @change="handleSelectedChange" /> v-for="v in list"
<img :src="v.url" /> :key="v.id"
<div class="content"> :info="v"
<div class="title">{{ v.title }}</div> :show-tags="!isMini || isView"
<div class="brand"> :show-size="!isMini"
<span class="icon"><svg-icon name="order-shop" size="24" /></span> :show-size-date="!isMini"
<span class="text">{{ v.brand }}</span> :show-remove="!isView"
</div> @remove="handleRemoveClick"
<div class="tags" v-if="!isMini || isView"> >
<span v-for="tag in v.tags" :key="tag" class="tag">{{ tag }}</span> <template #checkbox>
</div> <el-checkbox v-model="v.checked" v-if="!isMini" @change="handleSelectedChange" />
<div class="size" v-if="!isView"> </template>
<div class="icon"><svg-icon name="order-file" size="18" /></div> </sc-item>
<div class="text">
<span>{{ FormatBytes(v.fileSize) }}</span>
<span v-if="!isMini"
>&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;{{
FormatDate(v.date, 'SM D, YYYY, h:mm A')
}}</span
>
</div>
</div>
</div>
<div class="right">
<div class="amount">${{ v.amount }}<span> HKD</span></div>
<div class="remove" v-if="!isView">
<span class="icon"><svg-icon name="order-delete" size="18" /></span>
<span class="text">Remove</span>
</div>
</div>
</div>
</div> </div>
<div class="footer" v-if="isMini"> <div class="footer" v-if="isMini">
<div class="total size" v-show="list.length > 0"> <div class="total size" v-show="list.length > 0">
@@ -97,6 +79,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, onMounted } from 'vue' import { computed, ref, onMounted } from 'vue'
import { FormatBytes, FormatDate } from '@/utils/tools' import { FormatBytes, FormatDate } from '@/utils/tools'
import scItem from './sc-item.vue'
const emit = defineEmits(['close', 'selected-change']) const emit = defineEmits(['close', 'selected-change'])
const props = defineProps({ const props = defineProps({
title: { type: String, default: '' }, title: { type: String, default: '' },
@@ -202,6 +185,10 @@
onMounted(() => { onMounted(() => {
handleSelectedChange() handleSelectedChange()
}) })
const handleRemoveClick = (id: number) => {
list.value = list.value.filter((v) => v.id !== id)
handleSelectedChange()
}
const handleExploreClick = () => { const handleExploreClick = () => {
console.log('探索') console.log('探索')
} }
@@ -216,62 +203,45 @@
overflow: hidden; overflow: hidden;
--sc-list-title-font-size: 2rem; --sc-list-title-font-size: 2rem;
--sc-list-title-padding-bottom: 3rem; --sc-list-title-padding-bottom: 3rem;
--sc-list-list-item-padding: 2rem 0; --sc-item-padding: 2rem 0;
--sc-list-list-item-img-width: 10.4rem; --sc-item-img-width: 10.4rem;
--sc-list-list-item-img-height: 13.2rem; --sc-item-img-height: 13.2rem;
--sc-list-list-item-content-margin: 0 2rem; --sc-item-content-margin: 0 2rem;
--sc-list-list-item-margin-bottom: 0.8rem; --sc-item-margin-bottom: 0.8rem;
--sc-list-list-item-title-font-size: 1.6rem; --sc-item-title-font-size: 1.6rem;
--sc-list-list-item-brand-font-size: 1.4rem; --sc-item-brand-font-size: 1.4rem;
--sc-list-list-item-amount-font-size: 1.8rem; --sc-item-amount-font-size: 1.8rem;
--sc-list-list-item-currency-font-size: 1.6rem; --sc-item-currency-font-size: 1.6rem;
--sc-item-currency-font-size: 1.6rem;
--sc-item-content-align-self: baseline;
--sc-item-right-display: flex;
--sc-item-right-flex-direction: column;
--sc-item-right-justify-content: space-between;
--sc-item-right-align-items: flex-end;
--sc-item-right-height: var(--sc-item-img-height);
> .list { > .list {
flex: 1; flex: 1;
min-height: 30rem; min-height: 30rem;
overflow-y: auto; 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);
}
}
} }
} }
&.mini.view { &.mini.view {
--sc-list-title-padding-bottom: 2.4rem; --sc-list-title-padding-bottom: 2.4rem;
--sc-list-list-item-img-width: 9.6rem; --sc-item-img-width: 9.6rem;
--sc-list-list-item-img-height: 12.2rem; --sc-item-img-height: 12.2rem;
--sc-list-list-item-padding: 1.6rem; --sc-item-padding: 1.6rem;
--sc-list-list-item-margin-bottom: 0.8rem; --sc-item-margin-bottom: 0.8rem;
--sc-list-list-item-title-font-size: 2rem; --sc-item-title-font-size: 2rem;
--sc-item-right-margin-top: 1.2rem;
--sc-item-content-margin: 1.2rem 2rem 0;
--sc-item-tag-min-width: 0;
--sc-item-tag-height: 2rem;
--sc-item-tag-radius: 2rem;
--sc-item-tag-font-size: 1.2rem;
--sc-item-tag-padding: 0 0.8rem;
> .header { > .header {
border: none; border: none;
} }
> .list {
> .item {
> .content {
margin-top: 1.2rem;
> .tags {
> .tag {
min-width: 0;
height: 2rem;
line-height: 2rem;
font-size: 1.2rem;
padding: 0 0.8rem;
}
}
}
> .right {
margin-top: 1.2rem;
}
}
}
> .footer { > .footer {
margin-top: 4.1rem; margin-top: 4.1rem;
> .total { > .total {
@@ -405,110 +375,6 @@
margin-top: 4.2rem; margin-top: 4.2rem;
} }
} }
> .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 { > .footer {
margin-top: 3rem; margin-top: 3rem;