初始项目

This commit is contained in:
liupopo
2023-02-11 12:55:02 +08:00
parent 1748bda84a
commit 0b89e36064
3363 changed files with 506201 additions and 1 deletions

View File

@@ -0,0 +1,103 @@
<template>
<input :type="inputType"
readonly
:class="classStyle"
@click="btnClick($event)"
:disabled="classStyle==='disabled-btn'"
:value="text">
</template>
<script>
export default {
props: {
text: {
type: [String, Number],
default: '一颗小按钮'
},
inputType: {
type: [String],
default: 'button'
},
classStyle: {
type: String,
default: 'default-btn'
}
},
methods: {
btnClick (event) {
this.$emit('btnClick', event)
}
}
}
</script>
<style lang="scss" rel="stylesheet/scss" scoped>
@import "../assets/style/theme";
%style {
width: 100px;
height: 30px;
line-height: 28px;
vertical-align: middle;
}
@mixin color($a,$b,$c) {
border: 1px solid $a;
border-radius: 4px;
font-size: 12px;
color: $b;
background-color: $c;
}
input {
display: inline-block;
cursor: pointer;
text-align: center;
/*> span {*/
/*user-select: none;*/
/*display: inline-block;*/
/*width: 100%;*/
/*height: 100%;*/
/*}*/
}
/*灰色的按钮*/
.gray-btn {
border: 1px solid #d5d5d5;
color: #646464;
}
// 默认按钮
.default-btn {
@include color(#e1e1e1, #646464, #f9f9f9);
@extend %style;
background-image: -webkit-linear-gradient(top, $cf, #f9f9f9);
background-image: linear-gradient(180deg, $cf, #f9f9f9);
&:hover {
background-color: #eee;
background-image: -webkit-linear-gradient(top, #f5f5f5, #eee);
background-image: linear-gradient(180deg, #f5f5f5, #eee);
}
}
// 主色按钮
.main-btn {
@include color(#5c81e3, #fff, #678ee7);
background-image: -webkit-linear-gradient(top, #678ee7, #5078df);
background-image: linear-gradient(180deg, #678ee7, #5078df);
@extend %style;
&:hover {
background-color: #6c8cd4;
background-image: -webkit-linear-gradient(top, #6c8cd4, #4769c2);
background-image: linear-gradient(180deg, #6c8cd4, #4769c2);
}
}
// 禁用
.disabled-btn {
cursor: not-allowed;
@include color(#afafaf, $cf, #a9a9a9);
@extend %style;
background-image: -webkit-linear-gradient(top, #b8b8b8, #a9a9a9);
background-image: linear-gradient(180deg, #b8b8b8, #a9a9a9);
}
</style>

View File

@@ -0,0 +1,207 @@
<template>
<!--数量-->
<div class="item-cols-num clearfix">
<div class="select">
<span class="down"
@click.stop.prevent="down()"
:class="{'down-disabled':Num<=1}">-
</span>
<span class="num">
<input type="text"
:class="{show:show}"
v-model="Num>=limit?limit:Num"
@blur="blur()"
maxlength="2">
<ul ref="ul">
<li v-for="i in numList" :key="i">{{i}}</li>
</ul>
</span>
<span class="up" :class="{'up-disabled':Num>=limit}"
@click.stop.prevent="up()">+</span>
</div>
</div>
</template>
<script>
export default {
props: {
num: {
type: [Number],
default: 1
},
id: {
type: [Number, String]
},
checked: {
type: [String, Boolean]
},
limit: {
type: Number,
default: 10
}
},
computed: {},
data () {
return {
show: true,
flag: true,
Num: this.num,
numList: []
}
},
methods: {
up () {
if (this.flag && this.Num < this.limit) {
this.ani('up')
}
return false
},
down () {
if (this.flag && this.Num > 1) {
this.ani('down')
}
return false
},
blur () {
this.Num = this.Num > this.limit ? Number(this.limit) : Number(this.Num)
this.$emit('edit-num', this.Num, this.id, this.checked)
},
ani (opera) {
this.flag = false
let n = this.Num
this.numList = [n - 1, n, n + 1]
let ul = this.$refs.ul
let ulStyle = ul.style
this.show = false
ulStyle.zIndex = '99'
ulStyle.transition = 'all .2s ease-out'
if (opera === 'up') {
this.Num++
ulStyle.transform = 'translateY(-54px)'
} else {
this.Num--
ulStyle.transform = `translateY(-18px)`
}
ul.addEventListener('transitionend', () => {
this.show = true
this.domInt(ulStyle)
this.flag = true
})
ul.addEventListener('webkitAnimationEnd', () => {
this.show = true
this.domInt(ulStyle)
this.flag = true
})
this.$emit('edit-num', this.Num, this.id, this.checked)
},
domInt (domStyle) {
domStyle.zIndex = '1'
domStyle.transition = 'all 0s'
domStyle.transform = 'translateY(-36px)' // 回到原位
}
}
}
</script>
<style lang="scss" scoped>
.select {
input {
z-index: 10;
width: 36px;
height: 18px;
background-color: #fff;
border: none;
text-align: center;
line-height: 18px;
font-size: 14px;
padding: 0;
color: #666;
visibility: hidden;
position: relative;
border: none;
&.show {
visibility: visible;
}
}
ul {
padding: 0;
line-height: 18px;
font-size: 14px;
display: inline-block;
position: absolute;
left: 0;
list-style: none;
width: 36px;
font-family: system-ui;
z-index: 1;
transform: translateY(-36px);
}
.up.up-disabled, .up.up-disabled:hover {
background-position: 0 -240px !important;
cursor: not-allowed !important;
}
}
/*数量*/
.item-cols-num {
display: inline-block;
}
.select {
height: 40px;
padding-top: 4px;
input {
width: 100%;
text-align: center;
}
.down {
background-position: 0 -60px;
}
.down.down-disabled:hover {
background-position: 0 -300px;
cursor: not-allowed;
}
.down, .up {
background: url(../../static/images/cart-updown_8303731e15@2x.jpg) no-repeat;
overflow: hidden;
float: left;
width: 34px;
height: 37px;
background-size: 100% auto;
line-height: 40px;
text-indent: -9999em;
cursor: pointer;
user-select: none;
}
.num {
position: relative;
overflow: hidden;
text-align: center;
float: left;
width: 36px;
height: 18px;
margin: 7px 0 0;
border: none;
border-radius: 3px;
line-height: 18px;
text-align: center;
font-size: 14px;
}
.up {
margin: 0;
background-position: 0 0;
&:hover {
background-position: 0 -120px;
}
}
.down {
background-position: 0 -60px;
&:hover {
background-position: 0 -180px;
}
}
}
.down.down-disabled {
background-position: 0 -300px;
cursor: not-allowed;
}
</style>

View File

@@ -0,0 +1,74 @@
<template>
<span :endTime="endTime" :callback="callback" :endText="endText">
<slot>
{{content}}
</slot>
</span>
</template>
<script>
export default {
data () {
return {
content: ''
}
},
props: {
endTime: {
type: String,
default: ''
},
endText: {
type: String,
default: '已结束'
},
callback: {
type: Function,
default: null
}
},
methods: {
countdowm (timestamp) {
let self = this
let timer = setInterval(function () {
let nowTime = new Date()
let t = timestamp - nowTime.getTime()
if (t > 0) {
let day = Math.floor(t / 86400000)
let hour = Math.floor((t / 3600000) % 24)
let min = Math.floor((t / 60000) % 60)
let sec = Math.floor((t / 1000) % 60)
hour = hour < 10 ? '0' + hour : hour
min = min < 10 ? '0' + min : min
sec = sec < 10 ? '0' + sec : sec
let format = ''
if (day > 0) {
format = `${day}${hour} 小时 ${min}${sec}`
}
if (day <= 0 && hour > 0) {
format = `${hour} 小时 ${min}${sec}`
}
if (day <= 0 && hour <= 0) {
format = `${min}${sec}`
}
self.content = format
} else {
clearInterval(timer)
self.content = self.endText
self._callback()
}
}, 1000)
},
_callback () {
if (this.callback && this.callback instanceof Function) {
this.callback(...this)
}
}
},
mounted () {
this.countdowm(this.endTime)
}
}
</script>
<style lang='scss' rel='stylesheet/scss' scoped>
</style>

View File

@@ -0,0 +1,142 @@
<template>
<div class="good-item">
<div style="">
<div class="good-img">
<a @click="openProduct(msg.id)">
<img v-lazy="msg.pic" :alt="msg.name" :key="msg.pic">
</a>
</div>
<h6 class="good-title" v-html="msg.name">{{msg.name}}</h6>
<h3 class="sub-title ellipsis">{{msg.subTitle}}</h3>
<div class="good-price pr">
<div class="ds pa">
<a @click="openProduct(msg.id)">
<y-button text="查看详情" style="margin: 0 5px"></y-button>
</a>
<y-button text="加入购物车"
style="margin: 0 5px"
@btnClick="addCart(msg.id,msg.price,msg.name,msg.pic)"
classStyle="main-btn"
></y-button>
</div>
<p><span style="font-size:14px"></span>{{Number(msg.price).toFixed(2)}}</p>
</div>
</div>
</div>
</template>
<script>
import YButton from '/components/YButton'
import { addCart } from '/api/goods.js'
import { mapMutations, mapState } from 'vuex'
import { getStore } from '/utils/storage'
export default {
props: {
msg: {
salePrice: 0
}
},
data () {
return {}
},
methods: {
...mapMutations(['ADD_CART', 'ADD_ANIMATION', 'SHOW_CART']),
goodsDetails (id) {
this.$router.push({path: 'goodsDetails/id=' + id})
},
openProduct (id) {
window.open('//' + window.location.host + '/#/goodsDetails?id=' + id)
},
addCart (id, price, name, img) {
if (!this.showMoveImg) { // 动画是否在运动
if (this.login) { // 登录了 直接存在用户名下
addCart({userId: getStore('userId'), id: id, productNum: 1}).then(res => {
// 并不重新请求数据
this.ADD_CART({id: id, salePrice: price, productName: name, productImg: img})
})
} else { // 未登录 vuex
this.ADD_CART({id: id, salePrice: price, productName: name, productImg: img})
}
// 加入购物车动画
var dom = event.target
// 获取点击的坐标
let elLeft = dom.getBoundingClientRect().left + (dom.offsetWidth / 2)
let elTop = dom.getBoundingClientRect().top + (dom.offsetHeight / 2)
// 需要触发
this.ADD_ANIMATION({moveShow: true, elLeft: elLeft, elTop: elTop, img: img})
if (!this.showCart) {
this.SHOW_CART({showCart: true})
}
}
}
},
computed: {
...mapState(['login', 'showMoveImg', 'showCart'])
},
mounted () {
},
components: {
YButton
}
}
</script>
<style lang="scss" rel="stylesheet/scss" scoped>
@import "../assets/style/mixin";
@import "../assets/style/theme";
.good-item {
background: #fff;
width: 25%;
transition: all .5s;
height: 430px;
&:hover {
transform: translateY(-3px);
box-shadow: 1px 1px 20px #999;
.good-price p {
display: none;
}
.ds {
display: flex;
}
}
.ds {
@extend %block-center;
width: 100%;
display: none;
}
.good-img {
img {
margin: 50px auto 10px;
@include wh(206px);
display: block;
}
}
.good-price {
margin: 15px 0;
height: 30px;
text-align: center;
line-height: 30px;
color: #d44d44;
font-family: Arial;
font-size: 18px;
font-weight: 700;
}
.good-title {
line-height: 1.2;
font-size: 16px;
color: #424242;
margin: 0 auto;
padding: 0 14px;
text-align: center;
overflow: hidden;
}
h3 {
text-align: center;
line-height: 1.2;
font-size: 12px;
color: #d0d0d0;
padding: 10px;
}
}
</style>

View File

@@ -0,0 +1,111 @@
<template>
<div class="popup" v-if="open">
<div class="mask">
<div class="content">
<div class="topbar">
<div class="dialog-tit">
<h4>{{title}}</h4>
</div>
<span class="close" @click="close">
<svg t="1501234940517" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3014" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20"><path d="M941.576 184.248l-101.824-101.824L512 410.176 184.248 82.424 82.424 184.248 410.168 512l-327.744 327.752 101.824 101.824L512 613.824l327.752 327.752 101.824-101.824L613.832 512z" fill="#cdcdcd" p-id="3015"></path></svg>
</span>
</div>
<div class="s-content">
<slot name="content"></slot>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
open: {
type: Boolean,
default: false
},
title: {
type: String,
default: '管理收货地址'
}
},
methods: {
close () {
this.$emit('close')
}
}
}
</script>
<style lang="scss" scoped>
.popup {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 1000;
text-align: center;
transition: opacity .2s ease-in;
.mask {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .5);
}
.content {
width: 30%;
background: #fff;
border-radius: 10px;
}
.topbar {
position: relative;
z-index: 10;
overflow: hidden;
width: 100%;
height: 60px;
background: #F5F5F5;
background: linear-gradient(#FFF,#F5F5F5);
border-bottom: 1px solid #DCDCDC;
border-radius: 10px 10px 0 0;
box-shadow: 2px 0 5px rgba(0,0,0,.1);
}
.dialog-tit {
height: 60px;
padding: 0 15px;
line-height: 60px;
h4 {
text-align: center;
font-size: 18px;
font-weight: 400;
color: #666;
}
}
.s-content{
padding: 30px 15px;
}
.close{
position: absolute;
right: 10px;
top: 50%;
transform:translateY(-50%);
width: 20px;
height: 20px;
&:hover{
svg{
transition: all 1s;
transform: rotate(360deg);
transform-origin:50% 50%;
}
path{
fill: #8a8a8a;
}
}
}
}
</style>

View File

@@ -0,0 +1,63 @@
<template>
<div class="item" id="product.spu.id">
<div class="img-box">
<img :src="product.spu.sku_info[0].ali_image" alt="">
</div>
<div class="info">
<h6 class="ellipsis">{{product.spu.sku_info[0].title}}</h6>
<p>{{product.spu.sku_info[0].sub_title}}</p>
</div>
<p class="price">
<i>¥</i>
<span>{{product.spu.price}}</span>
</p>
<ul class="dot-list">
<li></li>
</ul>
</div>
</template>
<script>
export default {
props: [
'product'
]
}
</script>
<style scoped lang="scss">
@import "../assets/style/mixin";
.item {
position: relative;
height: 429px;
text-align: center;
img {
display: block;
width: 206px;
height: 206px;
}
.img-box {
@extend %block-center
}
.info {
width: 100%;
padding: 0 10px;
h6 {
overflow: hidden;
font-size: 16px;
line-height: 1.2;
white-space: nowrap;
text-overflow: ellipsis;
color: #424242;
}
p {
overflow: hidden;
padding-top: 7px;
font-size: 12px;
line-height: 1.2;
white-space: nowrap;
text-overflow: ellipsis;
color: #b2b2b2;
}
}
}
</style>

View File

@@ -0,0 +1,58 @@
<template>
<div class="gray-box">
<div class="title">
<h2>{{title}}</h2>
<div>
<slot name="right"></slot>
</div>
</div>
<!--内容-->
<div>
<slot name="content"></slot>
</div>
</div>
</template>
<script>
export default {
props: [
'title'
]
}
</script>
<style lang="scss" rel="stylesheet/scss" scoped>
.gray-box {
position: relative;
margin-bottom: 30px;
overflow: hidden;
background: #fff;
border-radius: 8px;
border: 1px solid #dcdcdc;
border-color: rgba(0, 0, 0, .14);
box-shadow: 0 3px 8px -6px rgba(0, 0, 0, .1);
.title {
padding-left: 30px;
position: relative;
z-index: 10;
height: 60px;
padding: 0 10px 0 24px;
border-bottom: 1px solid #d4d4d4;
border-radius: 8px 8px 0 0;
box-shadow: rgba(0, 0, 0, .06) 0 1px 7px;
background: #f3f3f3;
background: -webkit-linear-gradient(#fbfbfb, #ececec);
background: linear-gradient(#fbfbfb, #ececec);
line-height: 60px;
font-size: 18px;
color: #333;
display: flex;
justify-content: space-between;
align-items: center;
h2 {
font-size: 18px;
font-weight: 400;
color: #626262;
display: inline-block;
}
}
}
</style>