chore:整理
This commit is contained in:
1 parent
af0949c009
commit
6f92eedbbf
622 files changed
+59288
-10104
No files matched your search
@@ -1,7 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
|
||||
onLaunch(() => {});
|
||||
onShow(() => {});
|
||||
onHide(() => {});
|
||||
</script>
|
||||
<style></style>
|
||||
Vendored
-8
@@ -1,8 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module "*.vue" {
|
||||
import { DefineComponent } from "vue";
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
import { createSSRApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import "./style/index.scss";
|
||||
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App);
|
||||
return {
|
||||
app,
|
||||
};
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
{
|
||||
"name" : "",
|
||||
"appid" : "",
|
||||
"description" : "",
|
||||
"versionName" : "1.0.0",
|
||||
"versionCode" : "100",
|
||||
"transformPx" : false,
|
||||
/* 5+App特有相关 */
|
||||
"app-plus" : {
|
||||
"usingComponents" : true,
|
||||
"nvueStyleCompiler" : "uni-app",
|
||||
"compilerVersion" : 3,
|
||||
"splashscreen" : {
|
||||
"alwaysShowBeforeRender" : true,
|
||||
"waiting" : true,
|
||||
"autoclose" : true,
|
||||
"delay" : 0
|
||||
},
|
||||
/* 模块配置 */
|
||||
"modules" : {},
|
||||
/* 应用发布信息 */
|
||||
"distribute" : {
|
||||
/* android打包配置 */
|
||||
"android" : {
|
||||
"permissions" : [
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
]
|
||||
},
|
||||
/* ios打包配置 */
|
||||
"ios" : {},
|
||||
/* SDK配置 */
|
||||
"sdkConfigs" : {}
|
||||
}
|
||||
},
|
||||
/* 快应用特有相关 */
|
||||
"quickapp" : {},
|
||||
/* 小程序特有相关 */
|
||||
"mp-weixin" : {
|
||||
"appid" : "",
|
||||
"setting" : {
|
||||
"urlCheck" : false
|
||||
},
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-alipay" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-baidu" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-toutiao" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"uniStatistics": {
|
||||
"enable": false
|
||||
},
|
||||
"vueVersion" : "3"
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
{
|
||||
"easycom": {
|
||||
"autoscan": true,
|
||||
"custom": {
|
||||
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
|
||||
}
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/home/home",
|
||||
"style": {
|
||||
"navigationBarTitleText": "首页",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/moments/moments",
|
||||
"style": {
|
||||
"navigationBarTitleText": "动态",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/messages/messages",
|
||||
"style": {
|
||||
"navigationBarTitleText": "消息",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/profile/profile",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
],
|
||||
"subPackages": [
|
||||
{
|
||||
"root": "pages/home",
|
||||
"pages": [
|
||||
{
|
||||
"path": "address-change",
|
||||
"style": {
|
||||
"navigationBarTitleText": "城市",
|
||||
"navigationStyle": "default",
|
||||
"navigationBarBackgroundColor": "#222222",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "详情",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarTitleText": "交友聊天",
|
||||
"navigationBarBackgroundColor": "#F8F8F8",
|
||||
"backgroundColor": "#F8F8F8"
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#fff",
|
||||
"selectedColor": "#ffcd32",
|
||||
"backgroundColor": "#222222",
|
||||
"borderStyle": "black",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/home/home",
|
||||
"text": "首页",
|
||||
"iconPath": "static/tabbar/home.png",
|
||||
"selectedIconPath": "static/tabbar/home-active.png"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/moments/moments",
|
||||
"text": "动态",
|
||||
"iconPath": "static/tabbar/moments.png",
|
||||
"selectedIconPath": "static/tabbar/moments-active.png"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/messages/messages",
|
||||
"text": "消息",
|
||||
"iconPath": "static/tabbar/messages.png",
|
||||
"selectedIconPath": "static/tabbar/messages-active.png"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/profile/profile",
|
||||
"text": "我的",
|
||||
"iconPath": "static/tabbar/profile.png",
|
||||
"selectedIconPath": "static/tabbar/profile-active.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<template>1</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -1,255 +0,0 @@
|
||||
<template>
|
||||
<uni-nav-bar dark :fixed="true" :border="false" :height="50" background-color="transparent" left-width="360rpx" right-width="500rpx">
|
||||
<template v-slot:left>
|
||||
<uni-icons type="back" size="30" color="#ffcd32" @click="back"></uni-icons>
|
||||
<view class="nav-user-info" :style="navUserInfoStyle">
|
||||
<image class="nav-avatar" src="" mode="scaleToFill" />
|
||||
<text class="nav-name">稍后发非麻烦咖啡</text>
|
||||
</view>
|
||||
</template>
|
||||
<template v-slot:right>
|
||||
<view class="nav-bar-right">
|
||||
<view class="status">365.56km · 在线</view>
|
||||
<uni-icons type="more-filled" size="30" color="#ffcd32"></uni-icons>
|
||||
</view>
|
||||
</template>
|
||||
</uni-nav-bar>
|
||||
|
||||
<scroll-view
|
||||
class="page"
|
||||
scroll-y
|
||||
refresher-enabled
|
||||
refresher-background="#222"
|
||||
:refresher-triggered="refresherTriggered"
|
||||
:refresher-threshold="100"
|
||||
@refresherrefresh="onRefresherRefresh"
|
||||
@refresherabort="onRefresherAbort"
|
||||
@scrolltolower="onScrollToLower"
|
||||
@scroll="onScroll"
|
||||
>
|
||||
<view class="image" style="background-image: url(http://dummyimage.com/100x100/ac0ee3)">
|
||||
<view class="info">
|
||||
<image class="avatar" src="" />
|
||||
<view class="name">{{ "啊发发" }}</view>
|
||||
<view class="verification">真人认证</view>
|
||||
<view class="tags">
|
||||
<text class="tag">女·23岁</text>
|
||||
<text class="tag">170cm</text>
|
||||
</view>
|
||||
<view class="introduction">{{ "发哈夫哈佛哈维合法i凤凰网你" }}</view>
|
||||
</view>
|
||||
<view class="filter" :style="filterStyle"></view>
|
||||
</view>
|
||||
<view class="album">
|
||||
<view class="title">相册</view>
|
||||
<image class="photo" v-for="(item, index) in 5" :key="index" src="http://dummyimage.com/100x100/ac0ee3" />
|
||||
</view>
|
||||
<view class="evaluate">
|
||||
<view class="title">评价1+</view>
|
||||
<view class="rate">
|
||||
<view class="rate-item">
|
||||
<text>身材</text>
|
||||
<uni-rate :value="2" :touchable="false" :readonly="true" size="18" />
|
||||
</view>
|
||||
<view class="rate-item">
|
||||
<text>颜值</text>
|
||||
<uni-rate :value="2" :touchable="false" :readonly="true" size="18" />
|
||||
</view>
|
||||
<view class="rate-item">
|
||||
<text>耐心</text>
|
||||
<uni-rate :value="2" :touchable="false" :readonly="true" size="18" />
|
||||
</view>
|
||||
<view class="rate-item">
|
||||
<text>有趣</text>
|
||||
<uni-rate :value="2" :touchable="false" :readonly="true" size="18" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<moment v-for="(item, index) in listData">{{ index }}</moment>
|
||||
<uni-load-more :status="loadMoreStatus" :content-text="loadMoreContent" @clickLoadMore="onClickLoadMore" />
|
||||
<view style="height: 100rpx"></view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from "vue";
|
||||
import moment from "@/sheep/components/moment/moment.vue";
|
||||
import makeFriendApi from "@/sheep/api/make-friend";
|
||||
import useScroll from "@/sheep/hooks/use-scroll";
|
||||
|
||||
const {
|
||||
listData,
|
||||
refresherTriggered,
|
||||
loadMoreContent,
|
||||
loadMoreStatus,
|
||||
onRefresherRefresh,
|
||||
onRefresherAbort,
|
||||
onScrollToLower,
|
||||
onClickLoadMore,
|
||||
} = useScroll(makeFriendApi.getMakeFriendList);
|
||||
|
||||
const scrollTop = ref(0);
|
||||
|
||||
const filterStyle = computed(() => {
|
||||
let blur = 0;
|
||||
const scrollY = scrollTop.value;
|
||||
if (scrollY >= 0) {
|
||||
blur = Math.min(1, scrollY / 300) * 10;
|
||||
}
|
||||
return {
|
||||
backdropFilter: `blur(${blur}px)`,
|
||||
};
|
||||
});
|
||||
const navUserInfoStyle = computed(() => {
|
||||
let opacity = 1;
|
||||
const scrollY = scrollTop.value;
|
||||
if (scrollY >= 0) {
|
||||
opacity = Math.min(1, scrollY / 300 < 0.4 ? 0 : scrollY / 300);
|
||||
}
|
||||
return {
|
||||
opacity,
|
||||
};
|
||||
});
|
||||
|
||||
function back() {
|
||||
uni.navigateBack();
|
||||
}
|
||||
|
||||
function onScroll(e: any) {
|
||||
scrollTop.value = e.detail.scrollTop;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.nav-user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.nav-avatar {
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
background-color: #333;
|
||||
border-radius: 50%;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
.nav-name {
|
||||
width: 140rpx;
|
||||
@include no-wrap;
|
||||
}
|
||||
}
|
||||
.nav-bar-right {
|
||||
margin-right: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.status {
|
||||
font-size: $font-size-small;
|
||||
padding: 10rpx 16rpx;
|
||||
background-color: $color-theme-d;
|
||||
border-radius: 36rpx;
|
||||
margin-right: 40rpx;
|
||||
}
|
||||
}
|
||||
.page {
|
||||
height: calc(100vh - 100rpx);
|
||||
background-color: $color-background;
|
||||
overflow: hidden;
|
||||
.image {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
background-repeat: no-repeat;
|
||||
transform-origin: top;
|
||||
background-size: cover;
|
||||
position: relative;
|
||||
.info {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
z-index: 100;
|
||||
.avatar {
|
||||
text-align: center;
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.name {
|
||||
font-size: $font-size-large;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.verification {
|
||||
background-color: $color-theme;
|
||||
font-size: $font-size-small;
|
||||
color: black;
|
||||
padding: 8rpx;
|
||||
border-radius: 6rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
margin-bottom: 40rpx;
|
||||
.tag {
|
||||
background-color: $color-background;
|
||||
font-size: $font-size-small;
|
||||
color: $color-theme;
|
||||
margin-right: 20rpx;
|
||||
padding: 6rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
.introduction {
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
}
|
||||
.filter {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(7, 17, 27, 0.2);
|
||||
}
|
||||
}
|
||||
.album {
|
||||
margin: 40rpx;
|
||||
.title {
|
||||
font-size: $font-size-medium-x;
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
.photo {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
margin: 0 20rpx 20rpx 0;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
.evaluate {
|
||||
margin: 40rpx;
|
||||
.title {
|
||||
font-size: $font-size-medium-x;
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
.rate {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
flex-wrap: wrap;
|
||||
font-size: $font-size-medium;
|
||||
color: $color-theme;
|
||||
.rate-item {
|
||||
gap: 20rpx;
|
||||
background-color: $color-background-d;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12rpx 16rpx;
|
||||
margin-bottom: 10rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,168 +0,0 @@
|
||||
<template>
|
||||
<uni-nav-bar
|
||||
dark
|
||||
:fixed="true"
|
||||
:border="false"
|
||||
:height="50"
|
||||
title=""
|
||||
left-text=""
|
||||
background-color="#222"
|
||||
leftWidth="140rpx"
|
||||
rightWidth="140rpx"
|
||||
>
|
||||
<view class="segmented">
|
||||
<segmented v-model="current" :items="segmentedItems"></segmented>
|
||||
</view>
|
||||
<template v-slot:left>
|
||||
<view class="location" @click="selectAddress">
|
||||
<uni-icons type="location" size="24" color="#ffcd32"></uni-icons>
|
||||
<text>{{}}</text>
|
||||
</view>
|
||||
</template>
|
||||
<template v-slot:right></template>
|
||||
</uni-nav-bar>
|
||||
|
||||
<scroll-view
|
||||
class="user-list"
|
||||
scroll-y
|
||||
refresher-enabled
|
||||
refresher-background="#222"
|
||||
:refresher-triggered="refresherTriggered"
|
||||
:refresher-threshold="100"
|
||||
@refresherrefresh="onRefresherRefresh"
|
||||
@refresherabort="onRefresherAbort"
|
||||
@scrolltolower="onScrollToLower"
|
||||
>
|
||||
<view class="user-card" v-for="user in listData" :key="user.id" @click="detail">
|
||||
<image class="user-image" :src="user.avatar" />
|
||||
<view class="user-info">
|
||||
<view class="user">
|
||||
<text class="user-name">{{ user.name }}</text>
|
||||
<text class="user-status">{{ user.status || "离线" }}</text>
|
||||
</view>
|
||||
<view class="verification">
|
||||
<text class="verify-badge">真人认证</text>
|
||||
</view>
|
||||
<view class="user-details">
|
||||
<text class="tag">{{ user.gender }}·{{ user.age }}岁</text>
|
||||
<text class="tag">{{ user.height }}cm</text>
|
||||
<text class="tag">{{ user.weight }}kg</text>
|
||||
<text class="tag">{{ user.occupation }}</text>
|
||||
<text class="tag">{{ user.distance }}km</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<uni-load-more :status="loadMoreStatus" :content-text="loadMoreContent" @clickLoadMore="onClickLoadMore" />
|
||||
<view style="height: 100rpx"></view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import segmented from "@/sheep/components/segmented/segmented.vue";
|
||||
import makeFriendApi from "@/sheep/api/make-friend";
|
||||
import useScroll from "@/sheep/hooks/use-scroll";
|
||||
|
||||
const {
|
||||
listData,
|
||||
refresherTriggered,
|
||||
loadMoreContent,
|
||||
loadMoreStatus,
|
||||
onRefresherRefresh,
|
||||
onRefresherAbort,
|
||||
onScrollToLower,
|
||||
onClickLoadMore,
|
||||
} = useScroll(makeFriendApi.getMakeFriendList);
|
||||
|
||||
const current = ref(0);
|
||||
const segmentedItems = ["广场", "最近", "新人"];
|
||||
|
||||
const selectAddress = () => {
|
||||
uni.navigateTo({
|
||||
url: "/pages/home/address-change",
|
||||
});
|
||||
};
|
||||
|
||||
const detail = () => {
|
||||
uni.navigateTo({
|
||||
url: "/pages/home/detail",
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.segmented {
|
||||
margin: auto;
|
||||
}
|
||||
.location {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
.user-list {
|
||||
background-color: $color-background;
|
||||
height: calc(100vh - 100rpx - 100rpx);
|
||||
.user-card {
|
||||
height: 300rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
.user-image {
|
||||
width: 200rpx;
|
||||
height: 300rpx;
|
||||
margin-right: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
background-color: #777777;
|
||||
}
|
||||
.user-info {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: start;
|
||||
.user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 16rpx;
|
||||
.user-name {
|
||||
font-size: $font-size-large;
|
||||
font-weight: bold;
|
||||
}
|
||||
.user-status {
|
||||
background-color: $color-theme-d;
|
||||
font-size: $font-size-small;
|
||||
padding: 10rpx 20rpx;
|
||||
border-radius: 36rpx;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
.verification {
|
||||
margin-top: 16rpx;
|
||||
.verify-badge {
|
||||
background-color: $color-theme;
|
||||
font-size: $font-size-small;
|
||||
color: black;
|
||||
padding: 6rpx 12rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
.user-details {
|
||||
margin-top: 26rpx;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20rpx;
|
||||
font-size: $font-size-small;
|
||||
color: $color-text-ll;
|
||||
.tag {
|
||||
background-color: $color-highlight-background;
|
||||
padding: 10rpx 16rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,134 +0,0 @@
|
||||
<template>
|
||||
<view class="messages-page">
|
||||
<view class="header">
|
||||
<view class="message-type">
|
||||
<text class="message">消息</text>
|
||||
<text>缘友</text>
|
||||
</view>
|
||||
<view class="interact">
|
||||
<uni-icons type="chat" size="24" color="#333"></uni-icons>
|
||||
<text class="text">互动消息</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view class="chat-list" scroll-y>
|
||||
<view class="chat-item" v-for="message in 10" :key="message.id">
|
||||
<uni-badge size="small" text="100" absolute="rightTop" :offset="[10, 10]" :custom-style="{ borderColor: 'red' }">
|
||||
<image :src="message.avatar" class="avatar" />
|
||||
</uni-badge>
|
||||
<view class="chat-user">
|
||||
<view class="top">
|
||||
<text class="username">{{ message.username }}</text>
|
||||
<text class="time">{{ message.time }}</text>
|
||||
</view>
|
||||
<view class="bottom">
|
||||
<text class="text">{{ message.lastMessage }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view style="height: 100rpx"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from "vue";
|
||||
|
||||
interface Message {
|
||||
id: number;
|
||||
username: string;
|
||||
avatar: string;
|
||||
lastMessage: string;
|
||||
time: string;
|
||||
unread: number;
|
||||
}
|
||||
|
||||
const messageList = ref<Message[]>();
|
||||
|
||||
const unreadCount = computed(() => {
|
||||
return messageList.value.reduce((total, message) => total + message.unread, 0);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.messages-page {
|
||||
background-color: $color-background;
|
||||
height: calc(100vh - 100rpx);
|
||||
.header {
|
||||
position: relative;
|
||||
height: 100rpx;
|
||||
.message-type {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: $font-size-large;
|
||||
.message {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
.interact {
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: 50rpx;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: $color-theme;
|
||||
border-radius: 40rpx;
|
||||
padding: 4rpx 20rpx;
|
||||
.text {
|
||||
font-size: $font-size-medium;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
.chat-list {
|
||||
height: calc(100vh - 100rpx - 100rpx);
|
||||
.chat-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #333;
|
||||
.avatar {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 25px;
|
||||
margin-right: 15rpx;
|
||||
background-color: #333;
|
||||
}
|
||||
.chat-user {
|
||||
flex: 1;
|
||||
padding: 0 20rpx;
|
||||
overflow: hidden;
|
||||
.top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
.username {
|
||||
font-size: $font-size-medium-x;
|
||||
}
|
||||
.time {
|
||||
font-size: $font-size-small;
|
||||
color: $color-text-l;
|
||||
}
|
||||
}
|
||||
.bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.text {
|
||||
font-size: $font-size-medium;
|
||||
flex: 1;
|
||||
margin-top: 10rpx;
|
||||
@include no-wrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,83 +0,0 @@
|
||||
<template>
|
||||
<view class="header">
|
||||
<segmented class="segmented" v-model="current" :items="segmentedItems"></segmented>
|
||||
<view class="camera">
|
||||
<uni-icons type="camera" size="24" color="#ffcd32"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="moments-list"
|
||||
scroll-y
|
||||
refresher-enabled
|
||||
refresher-background="#222"
|
||||
:refresher-triggered="refresherTriggered"
|
||||
:refresher-threshold="100"
|
||||
@refresherrefresh="onRefresherRefresh"
|
||||
@refresherabort="onRefresherAbort"
|
||||
@scrolltolower="onScrollToLower"
|
||||
>
|
||||
<swiper indicator-dots autoplay circular>
|
||||
<swiper-item>11</swiper-item>
|
||||
<swiper-item>22</swiper-item>
|
||||
<swiper-item>33</swiper-item>
|
||||
</swiper>
|
||||
<moment
|
||||
v-for="item in listData"
|
||||
:avatar="item.avatar"
|
||||
:name="item.name"
|
||||
:verification="item.verification"
|
||||
:content="item.content"
|
||||
:images="item.images"
|
||||
:location="item.location"
|
||||
:likes="item.likes"
|
||||
:comments="item.comments"
|
||||
/>
|
||||
<uni-load-more :status="loadMoreStatus" :content-text="loadMoreContent" @clickLoadMore="onClickLoadMore" />
|
||||
<view style="height: 100rpx"></view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import segmented from "@/sheep/components/segmented/segmented.vue";
|
||||
import moment from "@/sheep/components/moment/moment.vue";
|
||||
import makeFriendApi from "@/sheep/api/make-friend";
|
||||
import useScroll from "@/sheep/hooks/use-scroll";
|
||||
|
||||
const {
|
||||
listData,
|
||||
refresherTriggered,
|
||||
loadMoreContent,
|
||||
loadMoreStatus,
|
||||
onRefresherRefresh,
|
||||
onRefresherAbort,
|
||||
onScrollToLower,
|
||||
onClickLoadMore,
|
||||
} = useScroll(makeFriendApi.getMakeFriendList);
|
||||
|
||||
const current = ref(0);
|
||||
const segmentedItems = ["动态", "附近"];
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.header {
|
||||
position: relative;
|
||||
height: 100rpx;
|
||||
.segmented {
|
||||
position: absolute;
|
||||
left: 30rpx;
|
||||
top: 50rpx;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
.camera {
|
||||
position: absolute;
|
||||
top: 50rpx;
|
||||
transform: translateY(-50%);
|
||||
right: 30rpx;
|
||||
}
|
||||
}
|
||||
.moments-list {
|
||||
height: calc(100vh - 100rpx - 100rpx);
|
||||
}
|
||||
</style>
|
||||
@@ -1,163 +0,0 @@
|
||||
<template>
|
||||
<view class="profile-page">
|
||||
<view class="header">
|
||||
<view class="name">我的</view>
|
||||
<view class="edit-info">
|
||||
<uni-icons type="auth" size="20" color="#333"></uni-icons>
|
||||
<text class="text">编辑资料</text>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view class="content" scroll-y>
|
||||
<view class="user-card">
|
||||
<image :src="userInfo.avatar" class="user-avatar" />
|
||||
<view class="user-info">
|
||||
<text class="username">{{ userInfo.username }}</text>
|
||||
<text class="user-id">ID: {{ userInfo.id }}</text>
|
||||
<text class="tag">{{ "vip" }}</text>
|
||||
</view>
|
||||
<view class="edit-btn">
|
||||
<text class="edit-text">编辑</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="menu-section">
|
||||
<view class="menu-item" v-for="item in menuList" :key="item.id">
|
||||
<view class="menu-icon">{{ item.icon }}</view>
|
||||
<text class="menu-text">{{ item.text }}</text>
|
||||
<uni-icons type="right" size="16" color="#999999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
<view style="height: 100rpx"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
|
||||
interface UserInfo {
|
||||
username: string;
|
||||
id: string;
|
||||
avatar: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
interface MenuItem {
|
||||
id: number;
|
||||
icon: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
const userInfo = ref<UserInfo>({
|
||||
username: "我的昵称",
|
||||
id: "123456789",
|
||||
avatar: "/static/my-avatar.jpg",
|
||||
tags: ["真人认证", "VIP会员"],
|
||||
});
|
||||
|
||||
const menuList = ref<MenuItem[]>([
|
||||
{ id: 1, icon: "1", text: "个人资料" },
|
||||
{ id: 2, icon: "2", text: "会员中心" },
|
||||
{ id: 3, icon: "3", text: "我的礼物" },
|
||||
{ id: 4, icon: "4", text: "我的喜欢" },
|
||||
{ id: 5, icon: "5", text: "我的关注" },
|
||||
{ id: 6, icon: "6", text: "我的相册" },
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.profile-page {
|
||||
background-color: $color-background;
|
||||
height: calc(100vh - 100rpx);
|
||||
.header {
|
||||
position: relative;
|
||||
height: 100rpx;
|
||||
.name {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: $font-size-medium-x;
|
||||
}
|
||||
.edit-info {
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: 50rpx;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: $color-theme;
|
||||
border-radius: 40rpx;
|
||||
padding: 8rpx 20rpx;
|
||||
.text {
|
||||
font-size: $font-size-medium;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
height: calc(100vh - 100rpx - 100rpx);
|
||||
.user-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #333;
|
||||
padding: 0 40rpx 20rpx;
|
||||
.user-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 40px;
|
||||
margin-right: 15px;
|
||||
background-color: #333;
|
||||
}
|
||||
.user-info {
|
||||
flex: 1;
|
||||
.username {
|
||||
display: block;
|
||||
font-size: $font-size-medium-x;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.user-id {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: $color-text-l;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.tag {
|
||||
background-color: $color-theme;
|
||||
color: #000;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
font-size: $font-size-small;
|
||||
}
|
||||
}
|
||||
.edit-btn {
|
||||
padding: 8px 16px;
|
||||
border: 1px solid $color-theme;
|
||||
border-radius: 20px;
|
||||
.edit-text {
|
||||
color: $color-theme;
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
.menu-section {
|
||||
margin: 20px 40px;
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #333;
|
||||
font-size: $font-size-medium;
|
||||
.menu-icon {
|
||||
width: 30px;
|
||||
font-size: 18px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.menu-text {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,43 +0,0 @@
|
||||
// import request from "@/sheep/request";
|
||||
|
||||
const mockUsers = Array.from({ length: 26 }).map((_, index) => ({
|
||||
id: index,
|
||||
name: "山峰" + (index + 1),
|
||||
avatar: "/static/avatar2.jpg",
|
||||
gender: "男",
|
||||
age: 30,
|
||||
height: "185",
|
||||
weight: "75",
|
||||
occupation: "健身教练",
|
||||
distance: "5.2",
|
||||
photoCount: 3,
|
||||
isVip: false,
|
||||
status: "在线",
|
||||
}));
|
||||
|
||||
const makeFriendApi = {
|
||||
// 获得用户收件地址列表
|
||||
// getAddressList: () => {
|
||||
// return request({
|
||||
// url: "/member/address/list",
|
||||
// method: "GET",
|
||||
// });
|
||||
// },
|
||||
|
||||
getMakeFriendList: async (page: number = 1, pageSize: number = 10) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
|
||||
const startIndex = (page - 1) * pageSize;
|
||||
const endIndex = startIndex + pageSize;
|
||||
const newUsers = mockUsers.slice(startIndex, endIndex);
|
||||
|
||||
return {
|
||||
page,
|
||||
pageSize,
|
||||
total: 26,
|
||||
data: newUsers,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export default makeFriendApi;
|
||||
@@ -1,66 +0,0 @@
|
||||
import { baseUrl, apiPath, tenantId } from '@/sheep/config';
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const FileApi = {
|
||||
// 上传文件
|
||||
uploadFile: (file) => {
|
||||
// TODO 芋艿:访问令牌的接入;
|
||||
const token = uni.getStorageSync('token');
|
||||
uni.showLoading({
|
||||
title: '上传中',
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
url: baseUrl + apiPath + '/infra/file/upload',
|
||||
filePath: file,
|
||||
name: 'file',
|
||||
header: {
|
||||
// Accept: 'text/json',
|
||||
Accept: '*/*',
|
||||
'tenant-id': tenantId,
|
||||
// Authorization: 'Bearer test247',
|
||||
},
|
||||
success: (uploadFileRes) => {
|
||||
let result = JSON.parse(uploadFileRes.data);
|
||||
if (result.error === 1) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: result.msg,
|
||||
});
|
||||
} else {
|
||||
return resolve(result);
|
||||
}
|
||||
},
|
||||
fail: (error) => {
|
||||
console.log('上传失败:', error);
|
||||
return resolve(false);
|
||||
},
|
||||
complete: () => {
|
||||
uni.hideLoading();
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// 获取文件预签名地址
|
||||
getFilePresignedUrl: (path) => {
|
||||
return request({
|
||||
url: '/infra/file/presigned-url',
|
||||
method: 'GET',
|
||||
params: {
|
||||
path,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 创建文件
|
||||
createFile: (data) => {
|
||||
return request({
|
||||
url: '/infra/file/create', // 请求的 URL
|
||||
method: 'POST', // 请求方法
|
||||
data: data, // 要发送的数据
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default FileApi;
|
||||
@@ -1,53 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const AddressApi = {
|
||||
// 获得用户收件地址列表
|
||||
getAddressList: () => {
|
||||
return request({
|
||||
url: '/member/address/list',
|
||||
method: 'GET'
|
||||
});
|
||||
},
|
||||
// 创建用户收件地址
|
||||
createAddress: (data) => {
|
||||
return request({
|
||||
url: '/member/address/create',
|
||||
method: 'POST',
|
||||
data,
|
||||
custom: {
|
||||
showSuccess: true,
|
||||
successMsg: '保存成功'
|
||||
},
|
||||
});
|
||||
},
|
||||
// 更新用户收件地址
|
||||
updateAddress: (data) => {
|
||||
return request({
|
||||
url: '/member/address/update',
|
||||
method: 'PUT',
|
||||
data,
|
||||
custom: {
|
||||
showSuccess: true,
|
||||
successMsg: '更新成功'
|
||||
},
|
||||
});
|
||||
},
|
||||
// 获得用户收件地址
|
||||
getAddress: (id) => {
|
||||
return request({
|
||||
url: '/member/address/get',
|
||||
method: 'GET',
|
||||
params: { id }
|
||||
});
|
||||
},
|
||||
// 删除用户收件地址
|
||||
deleteAddress: (id) => {
|
||||
return request({
|
||||
url: '/member/address/delete',
|
||||
method: 'DELETE',
|
||||
params: { id }
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default AddressApi;
|
||||
@@ -1,132 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const AuthUtil = {
|
||||
// 使用手机 + 密码登录
|
||||
login: (data) => {
|
||||
return request({
|
||||
url: '/member/auth/login',
|
||||
method: 'POST',
|
||||
data,
|
||||
custom: {
|
||||
showSuccess: true,
|
||||
loadingMsg: '登录中',
|
||||
successMsg: '登录成功',
|
||||
},
|
||||
});
|
||||
},
|
||||
// 使用手机 + 验证码登录
|
||||
smsLogin: (data) => {
|
||||
return request({
|
||||
url: '/member/auth/sms-login',
|
||||
method: 'POST',
|
||||
data,
|
||||
custom: {
|
||||
showSuccess: true,
|
||||
loadingMsg: '登录中',
|
||||
successMsg: '登录成功',
|
||||
},
|
||||
});
|
||||
},
|
||||
// 发送手机验证码
|
||||
sendSmsCode: (mobile, scene) => {
|
||||
return request({
|
||||
url: '/member/auth/send-sms-code',
|
||||
method: 'POST',
|
||||
data: {
|
||||
mobile,
|
||||
scene,
|
||||
},
|
||||
custom: {
|
||||
loadingMsg: '发送中',
|
||||
showSuccess: true,
|
||||
successMsg: '发送成功',
|
||||
},
|
||||
});
|
||||
},
|
||||
// 登出系统
|
||||
logout: () => {
|
||||
return request({
|
||||
url: '/member/auth/logout',
|
||||
method: 'POST',
|
||||
});
|
||||
},
|
||||
// 刷新令牌
|
||||
refreshToken: (refreshToken) => {
|
||||
return request({
|
||||
url: '/member/auth/refresh-token',
|
||||
method: 'POST',
|
||||
params: {
|
||||
refreshToken
|
||||
},
|
||||
custom: {
|
||||
loading: false, // 不用加载中
|
||||
showError: false, // 不展示错误提示
|
||||
},
|
||||
});
|
||||
},
|
||||
// 社交授权的跳转
|
||||
socialAuthRedirect: (type, redirectUri) => {
|
||||
return request({
|
||||
url: '/member/auth/social-auth-redirect',
|
||||
method: 'GET',
|
||||
params: {
|
||||
type,
|
||||
redirectUri,
|
||||
},
|
||||
custom: {
|
||||
showSuccess: true,
|
||||
loadingMsg: '登陆中',
|
||||
},
|
||||
});
|
||||
},
|
||||
// 社交快捷登录
|
||||
socialLogin: (type, code, state) => {
|
||||
return request({
|
||||
url: '/member/auth/social-login',
|
||||
method: 'POST',
|
||||
data: {
|
||||
type,
|
||||
code,
|
||||
state,
|
||||
},
|
||||
custom: {
|
||||
showSuccess: true,
|
||||
loadingMsg: '登陆中',
|
||||
},
|
||||
});
|
||||
},
|
||||
// 微信小程序的一键登录
|
||||
weixinMiniAppLogin: (phoneCode, loginCode, state) => {
|
||||
return request({
|
||||
url: '/member/auth/weixin-mini-app-login',
|
||||
method: 'POST',
|
||||
data: {
|
||||
phoneCode,
|
||||
loginCode,
|
||||
state
|
||||
},
|
||||
custom: {
|
||||
showSuccess: true,
|
||||
loadingMsg: '登陆中',
|
||||
successMsg: '登录成功',
|
||||
},
|
||||
});
|
||||
},
|
||||
// 创建微信 JS SDK 初始化所需的签名
|
||||
createWeixinMpJsapiSignature: (url) => {
|
||||
return request({
|
||||
url: '/member/auth/create-weixin-jsapi-signature',
|
||||
method: 'POST',
|
||||
params: {
|
||||
url
|
||||
},
|
||||
custom: {
|
||||
showError: false,
|
||||
showLoading: false,
|
||||
},
|
||||
})
|
||||
},
|
||||
//
|
||||
};
|
||||
|
||||
export default AuthUtil;
|
||||
@@ -1,19 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const PointApi = {
|
||||
// 获得用户积分记录分页
|
||||
getPointRecordPage: (params) => {
|
||||
if (params.addStatus === undefined) {
|
||||
delete params.addStatus
|
||||
}
|
||||
const queryString = Object.keys(params)
|
||||
.map((key) => encodeURIComponent(key) + '=' + params[key])
|
||||
.join('&');
|
||||
return request({
|
||||
url: `/member/point/record/page?${queryString}`,
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default PointApi;
|
||||
@@ -1,37 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const SignInApi = {
|
||||
// 获得签到规则列表
|
||||
getSignInConfigList: () => {
|
||||
return request({
|
||||
url: '/member/sign-in/config/list',
|
||||
method: 'GET',
|
||||
});
|
||||
},
|
||||
// 获得个人签到统计
|
||||
getSignInRecordSummary: () => {
|
||||
return request({
|
||||
url: '/member/sign-in/record/get-summary',
|
||||
method: 'GET',
|
||||
});
|
||||
},
|
||||
// 签到
|
||||
createSignInRecord: () => {
|
||||
return request({
|
||||
url: '/member/sign-in/record/create',
|
||||
method: 'POST',
|
||||
});
|
||||
},
|
||||
// 获得签到记录分页
|
||||
getSignRecordPage: (params) => {
|
||||
const queryString = Object.keys(params)
|
||||
.map((key) => encodeURIComponent(key) + '=' + params[key])
|
||||
.join('&');
|
||||
return request({
|
||||
url: `/member/sign-in/record/page?${queryString}`,
|
||||
method: 'GET',
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default SignInApi;
|
||||
@@ -1,76 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const SocialApi = {
|
||||
// 获得社交用户
|
||||
getSocialUser: (type) => {
|
||||
return request({
|
||||
url: '/member/social-user/get',
|
||||
method: 'GET',
|
||||
params: {
|
||||
type
|
||||
},
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 社交绑定
|
||||
socialBind: (type, code, state) => {
|
||||
return request({
|
||||
url: '/member/social-user/bind',
|
||||
method: 'POST',
|
||||
data: {
|
||||
type,
|
||||
code,
|
||||
state
|
||||
},
|
||||
custom: {
|
||||
custom: {
|
||||
showSuccess: true,
|
||||
loadingMsg: '绑定中',
|
||||
successMsg: '绑定成功',
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
// 社交绑定
|
||||
socialUnbind: (type, openid) => {
|
||||
return request({
|
||||
url: '/member/social-user/unbind',
|
||||
method: 'DELETE',
|
||||
data: {
|
||||
type,
|
||||
openid
|
||||
},
|
||||
custom: {
|
||||
showLoading: false,
|
||||
loadingMsg: '解除绑定',
|
||||
successMsg: '解绑成功',
|
||||
},
|
||||
});
|
||||
},
|
||||
// 获取订阅消息模板列表
|
||||
getSubscribeTemplateList: () =>
|
||||
request({
|
||||
url: '/member/social-user/get-subscribe-template-list',
|
||||
method: 'GET',
|
||||
custom: {
|
||||
showError: false,
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
// 获取微信小程序码
|
||||
getWxaQrcode: async (path, query) => {
|
||||
return await request({
|
||||
url: '/member/social-user/wxa-qrcode',
|
||||
method: 'POST',
|
||||
data: {
|
||||
scene: query,
|
||||
path,
|
||||
checkPath: false, // TODO 开发环境暂不检查 path 是否存在
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default SocialApi;
|
||||
@@ -1,85 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const UserApi = {
|
||||
// 获得基本信息
|
||||
getUserInfo: () => {
|
||||
return request({
|
||||
url: '/member/user/get',
|
||||
method: 'GET',
|
||||
custom: {
|
||||
showLoading: false,
|
||||
auth: true,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 修改基本信息
|
||||
updateUser: (data) => {
|
||||
return request({
|
||||
url: '/member/user/update',
|
||||
method: 'PUT',
|
||||
data,
|
||||
custom: {
|
||||
auth: true,
|
||||
showSuccess: true,
|
||||
successMsg: '更新成功'
|
||||
},
|
||||
});
|
||||
},
|
||||
// 修改用户手机
|
||||
updateUserMobile: (data) => {
|
||||
return request({
|
||||
url: '/member/user/update-mobile',
|
||||
method: 'PUT',
|
||||
data,
|
||||
custom: {
|
||||
loadingMsg: '验证中',
|
||||
showSuccess: true,
|
||||
successMsg: '修改成功'
|
||||
},
|
||||
});
|
||||
},
|
||||
// 基于微信小程序的授权码,修改用户手机
|
||||
updateUserMobileByWeixin: (code) => {
|
||||
return request({
|
||||
url: '/member/user/update-mobile-by-weixin',
|
||||
method: 'PUT',
|
||||
data: {
|
||||
code
|
||||
},
|
||||
custom: {
|
||||
showSuccess: true,
|
||||
loadingMsg: '获取中',
|
||||
successMsg: '修改成功'
|
||||
},
|
||||
});
|
||||
},
|
||||
// 修改密码
|
||||
updateUserPassword: (data) => {
|
||||
return request({
|
||||
url: '/member/user/update-password',
|
||||
method: 'PUT',
|
||||
data,
|
||||
custom: {
|
||||
loadingMsg: '验证中',
|
||||
showSuccess: true,
|
||||
successMsg: '修改成功'
|
||||
},
|
||||
});
|
||||
},
|
||||
// 重置密码
|
||||
resetUserPassword: (data) => {
|
||||
return request({
|
||||
url: '/member/user/reset-password',
|
||||
method: 'PUT',
|
||||
data,
|
||||
custom: {
|
||||
loadingMsg: '验证中',
|
||||
showSuccess: true,
|
||||
successMsg: '修改成功'
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default UserApi;
|
||||
@@ -1,21 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
// TODO 芋艿:小程序直播还不支持
|
||||
export default {
|
||||
//小程序直播
|
||||
mplive: {
|
||||
getRoomList: (ids) =>
|
||||
request({
|
||||
url: 'app/mplive/getRoomList',
|
||||
method: 'GET',
|
||||
params: {
|
||||
ids: ids.join(','),
|
||||
}
|
||||
}),
|
||||
getMpLink: () =>
|
||||
request({
|
||||
url: 'app/mplive/getMpLink',
|
||||
method: 'GET'
|
||||
}),
|
||||
},
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
const files = import.meta.glob('./*.js', { eager: true });
|
||||
let api = {};
|
||||
Object.keys(files).forEach((key) => {
|
||||
api = {
|
||||
...api,
|
||||
[key.replace(/(.*\/)*([^.]+).*/gi, '$2')]: files[key].default,
|
||||
};
|
||||
});
|
||||
|
||||
export default api;
|
||||
@@ -1,18 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
export default {
|
||||
// 苹果相关
|
||||
apple: {
|
||||
// 第三方登录
|
||||
login: (data) =>
|
||||
request({
|
||||
url: 'third/apple/login',
|
||||
method: 'POST',
|
||||
data,
|
||||
custom: {
|
||||
showSuccess: true,
|
||||
loadingMsg: '登陆中',
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const PayChannelApi = {
|
||||
// 获得指定应用的开启的支付渠道编码列表
|
||||
getEnableChannelCodeList: (appId) => {
|
||||
return request({
|
||||
url: '/pay/channel/get-enable-code-list',
|
||||
method: 'GET',
|
||||
params: { appId }
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default PayChannelApi;
|
||||
@@ -1,22 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const PayOrderApi = {
|
||||
// 获得支付订单
|
||||
getOrder: (id, sync) => {
|
||||
return request({
|
||||
url: '/pay/order/get',
|
||||
method: 'GET',
|
||||
params: { id, sync },
|
||||
});
|
||||
},
|
||||
// 提交支付订单
|
||||
submitOrder: (data) => {
|
||||
return request({
|
||||
url: '/pay/order/submit',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default PayOrderApi;
|
||||
@@ -1,68 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const PayWalletApi = {
|
||||
// 获取钱包
|
||||
getPayWallet() {
|
||||
return request({
|
||||
url: '/pay/wallet/get',
|
||||
method: 'GET',
|
||||
custom: {
|
||||
showLoading: false,
|
||||
auth: true,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 获得钱包流水分页
|
||||
getWalletTransactionPage: (params) => {
|
||||
const queryString = Object.keys(params)
|
||||
.map((key) => encodeURIComponent(key) + '=' + params[key])
|
||||
.join('&');
|
||||
return request({
|
||||
url: `/pay/wallet-transaction/page?${queryString}`,
|
||||
method: 'GET',
|
||||
});
|
||||
},
|
||||
// 获得钱包流水统计
|
||||
getWalletTransactionSummary: (params) => {
|
||||
const queryString = `createTime=${params.createTime[0]}&createTime=${params.createTime[1]}`;
|
||||
return request({
|
||||
url: `/pay/wallet-transaction/get-summary?${queryString}`,
|
||||
// url: `/pay/wallet-transaction/get-summary`,
|
||||
method: 'GET',
|
||||
// params: params
|
||||
});
|
||||
},
|
||||
// 获得钱包充值套餐列表
|
||||
getWalletRechargePackageList: () => {
|
||||
return request({
|
||||
url: '/pay/wallet-recharge-package/list',
|
||||
method: 'GET',
|
||||
custom: {
|
||||
showError: false,
|
||||
showLoading: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 创建钱包充值记录(发起充值)
|
||||
createWalletRecharge: (data) => {
|
||||
return request({
|
||||
url: '/pay/wallet-recharge/create',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
},
|
||||
// 获得钱包充值记录分页
|
||||
getWalletRechargePage: (params) => {
|
||||
return request({
|
||||
url: '/pay/wallet-recharge/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
custom: {
|
||||
showError: false,
|
||||
showLoading: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default PayWalletApi;
|
||||
@@ -1,21 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const CategoryApi = {
|
||||
// 查询分类列表
|
||||
getCategoryList: () => {
|
||||
return request({
|
||||
url: '/product/category/list',
|
||||
method: 'GET',
|
||||
});
|
||||
},
|
||||
// 查询分类列表,指定编号
|
||||
getCategoryListByIds: (ids) => {
|
||||
return request({
|
||||
url: '/product/category/list-by-ids',
|
||||
method: 'GET',
|
||||
params: { ids },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default CategoryApi;
|
||||
@@ -1,22 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const CommentApi = {
|
||||
// 获得商品评价分页
|
||||
getCommentPage: (spuId, pageNo, pageSize, type) => {
|
||||
return request({
|
||||
url: '/product/comment/page',
|
||||
method: 'GET',
|
||||
params: {
|
||||
spuId,
|
||||
pageNo,
|
||||
pageSize,
|
||||
type,
|
||||
},
|
||||
custom: {
|
||||
showLoading: false,
|
||||
showError: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
export default CommentApi;
|
||||
@@ -1,54 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const FavoriteApi = {
|
||||
// 获得商品收藏分页
|
||||
getFavoritePage: (data) => {
|
||||
return request({
|
||||
url: '/product/favorite/page',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
});
|
||||
},
|
||||
// 检查是否收藏过商品
|
||||
isFavoriteExists: (spuId) => {
|
||||
return request({
|
||||
url: '/product/favorite/exits',
|
||||
method: 'GET',
|
||||
params: {
|
||||
spuId,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 添加商品收藏
|
||||
createFavorite: (spuId) => {
|
||||
return request({
|
||||
url: '/product/favorite/create',
|
||||
method: 'POST',
|
||||
data: {
|
||||
spuId,
|
||||
},
|
||||
custom: {
|
||||
auth: true,
|
||||
showSuccess: true,
|
||||
successMsg: '收藏成功',
|
||||
},
|
||||
});
|
||||
},
|
||||
// 取消商品收藏
|
||||
deleteFavorite: (spuId) => {
|
||||
return request({
|
||||
url: '/product/favorite/delete',
|
||||
method: 'DELETE',
|
||||
data: {
|
||||
spuId,
|
||||
},
|
||||
custom: {
|
||||
auth: true,
|
||||
showSuccess: true,
|
||||
successMsg: '取消成功',
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default FavoriteApi;
|
||||
@@ -1,39 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const SpuHistoryApi = {
|
||||
// 删除商品浏览记录
|
||||
deleteBrowseHistory: (spuIds) => {
|
||||
return request({
|
||||
url: '/product/browse-history/delete',
|
||||
method: 'DELETE',
|
||||
data: { spuIds },
|
||||
custom: {
|
||||
showSuccess: true,
|
||||
successMsg: '删除成功',
|
||||
},
|
||||
});
|
||||
},
|
||||
// 清空商品浏览记录
|
||||
cleanBrowseHistory: () => {
|
||||
return request({
|
||||
url: '/product/browse-history/clean',
|
||||
method: 'DELETE',
|
||||
custom: {
|
||||
showSuccess: true,
|
||||
successMsg: '清空成功',
|
||||
},
|
||||
});
|
||||
},
|
||||
// 获得商品浏览记录分页
|
||||
getBrowseHistoryPage: (data) => {
|
||||
return request({
|
||||
url: '/product/browse-history/page',
|
||||
method: 'GET',
|
||||
data,
|
||||
custom: {
|
||||
showLoading: false
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
export default SpuHistoryApi;
|
||||
@@ -1,53 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const SpuApi = {
|
||||
// 获得商品 SPU 列表
|
||||
getSpuListByIds: (ids) => {
|
||||
return request({
|
||||
url: '/product/spu/list-by-ids',
|
||||
method: 'GET',
|
||||
params: { ids },
|
||||
custom: {
|
||||
showLoading: false,
|
||||
showError: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 获得商品结算信息
|
||||
getSettlementProduct: (spuIds) => {
|
||||
return request({
|
||||
url: '/trade/order/settlement-product',
|
||||
method: 'GET',
|
||||
params: { spuIds },
|
||||
custom: {
|
||||
showLoading: false,
|
||||
showError: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 获得商品 SPU 分页
|
||||
getSpuPage: (params) => {
|
||||
return request({
|
||||
url: '/product/spu/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
showError: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 查询商品
|
||||
getSpuDetail: (id) => {
|
||||
return request({
|
||||
url: '/product/spu/get-detail',
|
||||
method: 'GET',
|
||||
params: { id },
|
||||
custom: {
|
||||
showLoading: false,
|
||||
showError: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
export default SpuApi;
|
||||
@@ -1,16 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const ActivityApi = {
|
||||
// 获得单个商品,进行中的拼团、秒杀、砍价活动信息
|
||||
getActivityListBySpuId: (spuId) => {
|
||||
return request({
|
||||
url: '/promotion/activity/list-by-spu-id',
|
||||
method: 'GET',
|
||||
params: {
|
||||
spuId,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default ActivityApi;
|
||||
@@ -1,12 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
export default {
|
||||
// 获得文章详情
|
||||
getArticle: (id, title) => {
|
||||
return request({
|
||||
url: '/promotion/article/get',
|
||||
method: 'GET',
|
||||
params: { id, title }
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
// 拼团 API
|
||||
const CombinationApi = {
|
||||
// 获得拼团活动分页
|
||||
getCombinationActivityPage: (params) => {
|
||||
return request({
|
||||
url: '/promotion/combination-activity/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
},
|
||||
|
||||
// 获得拼团活动明细
|
||||
getCombinationActivity: (id) => {
|
||||
return request({
|
||||
url: '/promotion/combination-activity/get-detail',
|
||||
method: 'GET',
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 获得拼团活动列表,基于活动编号数组
|
||||
getCombinationActivityListByIds: (ids) => {
|
||||
return request({
|
||||
url: '/promotion/combination-activity/list-by-ids',
|
||||
method: 'GET',
|
||||
params: {
|
||||
ids,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 获得最近 n 条拼团记录(团长发起的)
|
||||
getHeadCombinationRecordList: (activityId, status, count) => {
|
||||
return request({
|
||||
url: '/promotion/combination-record/get-head-list',
|
||||
method: 'GET',
|
||||
params: {
|
||||
activityId,
|
||||
status,
|
||||
count,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 获得我的拼团记录分页
|
||||
getCombinationRecordPage: (params) => {
|
||||
return request({
|
||||
url: '/promotion/combination-record/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
},
|
||||
|
||||
// 获得拼团记录明细
|
||||
getCombinationRecordDetail: (id) => {
|
||||
return request({
|
||||
url: '/promotion/combination-record/get-detail',
|
||||
method: 'GET',
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 获得拼团记录的概要信息
|
||||
getCombinationRecordSummary: () => {
|
||||
return request({
|
||||
url: '/promotion/combination-record/get-summary',
|
||||
method: 'GET',
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default CombinationApi;
|
||||
@@ -1,84 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const CouponApi = {
|
||||
// 获得优惠劵模板列表
|
||||
getCouponTemplateListByIds: (ids) => {
|
||||
return request({
|
||||
url: '/promotion/coupon-template/list-by-ids',
|
||||
method: 'GET',
|
||||
params: { ids },
|
||||
custom: {
|
||||
showLoading: false, // 不展示 Loading,避免领取优惠劵时,不成功提示
|
||||
showError: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 获得优惠劵模版列表
|
||||
getCouponTemplateList: (spuId, productScope, count) => {
|
||||
return request({
|
||||
url: '/promotion/coupon-template/list',
|
||||
method: 'GET',
|
||||
params: { spuId, productScope, count },
|
||||
});
|
||||
},
|
||||
// 获得优惠劵模版分页
|
||||
getCouponTemplatePage: (params) => {
|
||||
return request({
|
||||
url: '/promotion/coupon-template/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
},
|
||||
// 获得优惠劵模版
|
||||
getCouponTemplate: (id) => {
|
||||
return request({
|
||||
url: '/promotion/coupon-template/get',
|
||||
method: 'GET',
|
||||
params: { id },
|
||||
});
|
||||
},
|
||||
// 我的优惠劵列表
|
||||
getCouponPage: (params) => {
|
||||
return request({
|
||||
url: '/promotion/coupon/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
},
|
||||
// 领取优惠券
|
||||
takeCoupon: (templateId) => {
|
||||
return request({
|
||||
url: '/promotion/coupon/take',
|
||||
method: 'POST',
|
||||
data: { templateId },
|
||||
custom: {
|
||||
auth: true,
|
||||
showLoading: true,
|
||||
loadingMsg: '领取中',
|
||||
showSuccess: true,
|
||||
successMsg: '领取成功',
|
||||
},
|
||||
});
|
||||
},
|
||||
// 获得优惠劵
|
||||
getCoupon: (id) => {
|
||||
return request({
|
||||
url: '/promotion/coupon/get',
|
||||
method: 'GET',
|
||||
params: { id },
|
||||
});
|
||||
},
|
||||
// 获得未使用的优惠劵数量
|
||||
getUnusedCouponCount: () => {
|
||||
return request({
|
||||
url: '/promotion/coupon/get-unused-count',
|
||||
method: 'GET',
|
||||
custom: {
|
||||
showLoading: false,
|
||||
auth: true,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default CouponApi;
|
||||
@@ -1,38 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const DiyApi = {
|
||||
getUsedDiyTemplate: () => {
|
||||
return request({
|
||||
url: '/promotion/diy-template/used',
|
||||
method: 'GET',
|
||||
custom: {
|
||||
showError: false,
|
||||
showLoading: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
getDiyTemplate: (id) => {
|
||||
return request({
|
||||
url: '/promotion/diy-template/get',
|
||||
method: 'GET',
|
||||
params: {
|
||||
id
|
||||
},
|
||||
custom: {
|
||||
showError: false,
|
||||
showLoading: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
getDiyPage: (id) => {
|
||||
return request({
|
||||
url: '/promotion/diy-page/get',
|
||||
method: 'GET',
|
||||
params: {
|
||||
id
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default DiyApi;
|
||||
@@ -1,31 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const KeFuApi = {
|
||||
sendKefuMessage: (data) => {
|
||||
return request({
|
||||
url: '/promotion/kefu-message/send',
|
||||
method: 'POST',
|
||||
data,
|
||||
custom: {
|
||||
auth: true,
|
||||
showLoading: true,
|
||||
loadingMsg: '发送中',
|
||||
showSuccess: true,
|
||||
successMsg: '发送成功',
|
||||
},
|
||||
});
|
||||
},
|
||||
getKefuMessageList: (params) => {
|
||||
return request({
|
||||
url: '/promotion/kefu-message/list',
|
||||
method: 'GET',
|
||||
params,
|
||||
custom: {
|
||||
auth: true,
|
||||
showLoading: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default KeFuApi;
|
||||
@@ -1,30 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const PointApi = {
|
||||
// 获得积分商城活动分页
|
||||
getPointActivityPage: (params) => {
|
||||
return request({ url: 'promotion/point-activity/page', method: 'GET', params });
|
||||
},
|
||||
|
||||
// 获得积分商城活动列表,基于活动编号数组
|
||||
getPointActivityListByIds: (ids) => {
|
||||
return request({
|
||||
url: '/promotion/point-activity/list-by-ids',
|
||||
method: 'GET',
|
||||
params: {
|
||||
ids,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 获得积分商城活动明细
|
||||
getPointActivity: (id) => {
|
||||
return request({
|
||||
url: 'promotion/point-activity/get-detail',
|
||||
method: 'GET',
|
||||
params: { id },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default PointApi;
|
||||
@@ -1,14 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const RewardActivityApi = {
|
||||
// 获得满减送活动
|
||||
getRewardActivity: (id) => {
|
||||
return request({
|
||||
url: '/promotion/reward-activity/get',
|
||||
method: 'GET',
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default RewardActivityApi;
|
||||
@@ -1,44 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const SeckillApi = {
|
||||
// 获得秒杀时间段列表
|
||||
getSeckillConfigList: () => {
|
||||
return request({ url: 'promotion/seckill-config/list', method: 'GET' });
|
||||
},
|
||||
|
||||
// 获得当前秒杀活动
|
||||
getNowSeckillActivity: () => {
|
||||
return request({ url: 'promotion/seckill-activity/get-now', method: 'GET' });
|
||||
},
|
||||
|
||||
// 获得秒杀活动分页
|
||||
getSeckillActivityPage: (params) => {
|
||||
return request({ url: 'promotion/seckill-activity/page', method: 'GET', params });
|
||||
},
|
||||
|
||||
// 获得秒杀活动列表,基于活动编号数组
|
||||
getSeckillActivityListByIds: (ids) => {
|
||||
return request({
|
||||
url: '/promotion/seckill-activity/list-by-ids',
|
||||
method: 'GET',
|
||||
params: {
|
||||
ids,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获得秒杀活动明细
|
||||
* @param {number} id 秒杀活动编号
|
||||
* @return {*}
|
||||
*/
|
||||
getSeckillActivity: (id) => {
|
||||
return request({
|
||||
url: 'promotion/seckill-activity/get-detail',
|
||||
method: 'GET',
|
||||
params: { id },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default SeckillApi;
|
||||
@@ -1,13 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const AreaApi = {
|
||||
// 获得地区树
|
||||
getAreaTree: () => {
|
||||
return request({
|
||||
url: '/system/area/tree',
|
||||
method: 'GET'
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default AreaApi;
|
||||
@@ -1,16 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const DictApi = {
|
||||
// 根据字典类型查询字典数据信息
|
||||
getDictDataListByType: (type) => {
|
||||
return request({
|
||||
url: `/system/dict-data/type`,
|
||||
method: 'GET',
|
||||
params: {
|
||||
type,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default DictApi;
|
||||
@@ -1,63 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const AfterSaleApi = {
|
||||
// 获得售后分页
|
||||
getAfterSalePage: (params) => {
|
||||
return request({
|
||||
url: `/trade/after-sale/page`,
|
||||
method: 'GET',
|
||||
params,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 创建售后
|
||||
createAfterSale: (data) => {
|
||||
return request({
|
||||
url: `/trade/after-sale/create`,
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
},
|
||||
// 获得售后
|
||||
getAfterSale: (id) => {
|
||||
return request({
|
||||
url: `/trade/after-sale/get`,
|
||||
method: 'GET',
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 取消售后
|
||||
cancelAfterSale: (id) => {
|
||||
return request({
|
||||
url: `/trade/after-sale/cancel`,
|
||||
method: 'DELETE',
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 获得售后日志列表
|
||||
getAfterSaleLogList: (afterSaleId) => {
|
||||
return request({
|
||||
url: `/trade/after-sale-log/list`,
|
||||
method: 'GET',
|
||||
params: {
|
||||
afterSaleId,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 退回货物
|
||||
deliveryAfterSale: (data) => {
|
||||
return request({
|
||||
url: `/trade/after-sale/delivery`,
|
||||
method: 'PUT',
|
||||
data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default AfterSaleApi;
|
||||
@@ -1,93 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const BrokerageApi = {
|
||||
// 绑定分销用户
|
||||
bindBrokerageUser: (data)=>{
|
||||
return request({
|
||||
url: '/trade/brokerage-user/bind',
|
||||
method: 'PUT',
|
||||
data
|
||||
});
|
||||
},
|
||||
// 获得个人分销信息
|
||||
getBrokerageUser: () => {
|
||||
return request({
|
||||
url: '/trade/brokerage-user/get',
|
||||
method: 'GET'
|
||||
});
|
||||
},
|
||||
// 获得个人分销统计
|
||||
getBrokerageUserSummary: () => {
|
||||
return request({
|
||||
url: '/trade/brokerage-user/get-summary',
|
||||
method: 'GET',
|
||||
});
|
||||
},
|
||||
// 获得分销记录分页
|
||||
getBrokerageRecordPage: params => {
|
||||
if (params.status === undefined) {
|
||||
delete params.status
|
||||
}
|
||||
const queryString = Object.keys(params)
|
||||
.map(key => encodeURIComponent(key) + '=' + params[key])
|
||||
.join('&');
|
||||
return request({
|
||||
url: `/trade/brokerage-record/page?${queryString}`,
|
||||
method: 'GET',
|
||||
});
|
||||
},
|
||||
// 创建分销提现
|
||||
createBrokerageWithdraw: data => {
|
||||
return request({
|
||||
url: '/trade/brokerage-withdraw/create',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
},
|
||||
// 获得商品的分销金额
|
||||
getProductBrokeragePrice: spuId => {
|
||||
return request({
|
||||
url: '/trade/brokerage-record/get-product-brokerage-price',
|
||||
method: 'GET',
|
||||
params: { spuId }
|
||||
});
|
||||
},
|
||||
// 获得分销用户排行(基于佣金)
|
||||
getRankByPrice: params => {
|
||||
const queryString = `times=${params.times[0]}×=${params.times[1]}`;
|
||||
return request({
|
||||
url: `/trade/brokerage-user/get-rank-by-price?${queryString}`,
|
||||
method: 'GET',
|
||||
});
|
||||
},
|
||||
// 获得分销用户排行分页(基于佣金)
|
||||
getBrokerageUserChildSummaryPageByPrice: params => {
|
||||
const queryString = Object.keys(params)
|
||||
.map(key => encodeURIComponent(key) + '=' + params[key])
|
||||
.join('&');
|
||||
return request({
|
||||
url: `/trade/brokerage-user/rank-page-by-price?${queryString}`,
|
||||
method: 'GET',
|
||||
});
|
||||
},
|
||||
// 获得分销用户排行分页(基于用户量)
|
||||
getBrokerageUserRankPageByUserCount: params => {
|
||||
const queryString = Object.keys(params)
|
||||
.map(key => encodeURIComponent(key) + '=' + params[key])
|
||||
.join('&');
|
||||
return request({
|
||||
url: `/trade/brokerage-user/rank-page-by-user-count?${queryString}`,
|
||||
method: 'GET',
|
||||
});
|
||||
},
|
||||
// 获得下级分销统计分页
|
||||
getBrokerageUserChildSummaryPage: params => {
|
||||
return request({
|
||||
url: '/trade/brokerage-user/child-summary-page',
|
||||
method: 'GET',
|
||||
params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default BrokerageApi
|
||||
@@ -1,50 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const CartApi = {
|
||||
addCart: (data) => {
|
||||
return request({
|
||||
url: '/trade/cart/add',
|
||||
method: 'POST',
|
||||
data: data,
|
||||
custom: {
|
||||
showSuccess: true,
|
||||
successMsg: '已添加到购物车~',
|
||||
}
|
||||
});
|
||||
},
|
||||
updateCartCount: (data) => {
|
||||
return request({
|
||||
url: '/trade/cart/update-count',
|
||||
method: 'PUT',
|
||||
data: data
|
||||
});
|
||||
},
|
||||
updateCartSelected: (data) => {
|
||||
return request({
|
||||
url: '/trade/cart/update-selected',
|
||||
method: 'PUT',
|
||||
data: data
|
||||
});
|
||||
},
|
||||
deleteCart: (ids) => {
|
||||
return request({
|
||||
url: '/trade/cart/delete',
|
||||
method: 'DELETE',
|
||||
params: {
|
||||
ids
|
||||
}
|
||||
});
|
||||
},
|
||||
getCartList: () => {
|
||||
return request({
|
||||
url: '/trade/cart/list',
|
||||
method: 'GET',
|
||||
custom: {
|
||||
showLoading: false,
|
||||
auth: true,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default CartApi;
|
||||
@@ -1,16 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const TradeConfigApi = {
|
||||
// 获得交易配置
|
||||
getTradeConfig: () => {
|
||||
return request({
|
||||
url: `/trade/config/get`,
|
||||
method: 'GET',
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default TradeConfigApi;
|
||||
@@ -1,31 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
|
||||
const DeliveryApi = {
|
||||
// 获得快递公司列表
|
||||
getDeliveryExpressList: () => {
|
||||
return request({
|
||||
url: `/trade/delivery/express/list`,
|
||||
method: 'get',
|
||||
});
|
||||
},
|
||||
// 获得自提门店列表
|
||||
getDeliveryPickUpStoreList: (params) => {
|
||||
return request({
|
||||
url: `/trade/delivery/pick-up-store/list`,
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
},
|
||||
// 获得自提门店
|
||||
getDeliveryPickUpStore: (id) => {
|
||||
return request({
|
||||
url: `/trade/delivery/pick-up-store/get`,
|
||||
method: 'GET',
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default DeliveryApi;
|
||||
@@ -1,168 +0,0 @@
|
||||
import request from '@/sheep/request';
|
||||
import { isEmpty } from '@/sheep/helper/utils';
|
||||
|
||||
const OrderApi = {
|
||||
// 计算订单信息
|
||||
settlementOrder: (data) => {
|
||||
const data2 = {
|
||||
...data,
|
||||
};
|
||||
// 移除多余字段
|
||||
if (!(data.couponId > 0)) {
|
||||
delete data2.couponId;
|
||||
}
|
||||
if (!(data.addressId > 0)) {
|
||||
delete data2.addressId;
|
||||
}
|
||||
if (!(data.pickUpStoreId > 0)) {
|
||||
delete data2.pickUpStoreId;
|
||||
}
|
||||
if (isEmpty(data.receiverName)) {
|
||||
delete data2.receiverName;
|
||||
}
|
||||
if (isEmpty(data.receiverMobile)) {
|
||||
delete data2.receiverMobile;
|
||||
}
|
||||
if (!(data.combinationActivityId > 0)) {
|
||||
delete data2.combinationActivityId;
|
||||
}
|
||||
if (!(data.combinationHeadId > 0)) {
|
||||
delete data2.combinationHeadId;
|
||||
}
|
||||
if (!(data.seckillActivityId > 0)) {
|
||||
delete data2.seckillActivityId;
|
||||
}
|
||||
if (!(data.pointActivityId > 0)) {
|
||||
delete data2.pointActivityId;
|
||||
}
|
||||
if (!(data.deliveryType > 0)) {
|
||||
delete data2.deliveryType;
|
||||
}
|
||||
// 解决 SpringMVC 接受 List<Item> 参数的问题
|
||||
delete data2.items;
|
||||
for (let i = 0; i < data.items.length; i++) {
|
||||
data2[encodeURIComponent('items[' + i + '' + '].skuId')] = data.items[i].skuId + '';
|
||||
data2[encodeURIComponent('items[' + i + '' + '].count')] = data.items[i].count + '';
|
||||
if (data.items[i].cartId) {
|
||||
data2[encodeURIComponent('items[' + i + '' + '].cartId')] = data.items[i].cartId + '';
|
||||
}
|
||||
}
|
||||
const queryString = Object.keys(data2)
|
||||
.map((key) => key + '=' + data2[key])
|
||||
.join('&');
|
||||
return request({
|
||||
url: `/trade/order/settlement?${queryString}`,
|
||||
method: 'GET',
|
||||
custom: {
|
||||
showError: true,
|
||||
showLoading: true,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 获得商品结算信息
|
||||
getSettlementProduct: (spuIds) => {
|
||||
return request({
|
||||
url: '/trade/order/settlement-product',
|
||||
method: 'GET',
|
||||
params: { spuIds },
|
||||
custom: {
|
||||
showLoading: false,
|
||||
showError: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 创建订单
|
||||
createOrder: (data) => {
|
||||
return request({
|
||||
url: `/trade/order/create`,
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
},
|
||||
// 获得订单详细:sync 是可选参数
|
||||
getOrderDetail: (id, sync) => {
|
||||
return request({
|
||||
url: `/trade/order/get-detail`,
|
||||
method: 'GET',
|
||||
params: {
|
||||
id,
|
||||
sync,
|
||||
},
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 订单列表
|
||||
getOrderPage: (params) => {
|
||||
return request({
|
||||
url: '/trade/order/page',
|
||||
method: 'GET',
|
||||
params,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 确认收货
|
||||
receiveOrder: (id) => {
|
||||
return request({
|
||||
url: `/trade/order/receive`,
|
||||
method: 'PUT',
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 取消订单
|
||||
cancelOrder: (id) => {
|
||||
return request({
|
||||
url: `/trade/order/cancel`,
|
||||
method: 'DELETE',
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 删除订单
|
||||
deleteOrder: (id) => {
|
||||
return request({
|
||||
url: `/trade/order/delete`,
|
||||
method: 'DELETE',
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 获得交易订单的物流轨迹
|
||||
getOrderExpressTrackList: (id) => {
|
||||
return request({
|
||||
url: `/trade/order/get-express-track-list`,
|
||||
method: 'GET',
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 获得交易订单数量
|
||||
getOrderCount: () => {
|
||||
return request({
|
||||
url: '/trade/order/get-count',
|
||||
method: 'GET',
|
||||
custom: {
|
||||
showLoading: false,
|
||||
auth: true,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 创建单个评论
|
||||
createOrderItemComment: (data) => {
|
||||
return request({
|
||||
url: `/trade/order/item/create-comment`,
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default OrderApi;
|
||||
@@ -1,144 +0,0 @@
|
||||
<template>
|
||||
<view class="moment">
|
||||
<view class="user">
|
||||
<image :src="avatar" class="avatar" />
|
||||
<view class="details">
|
||||
<view>
|
||||
<text class="username">{{ name }}</text>
|
||||
<text v-if="verification" class="verification">实人认证</text>
|
||||
</view>
|
||||
<view class="info">女 · 20岁 | 学生</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="content">
|
||||
<text class="text">{{ content }}</text>
|
||||
<view class="images" v-if="images && images.length > 0">
|
||||
<image v-for="(img, index) in images" :key="index" :src="img" class="moment-image" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="address">
|
||||
<uni-icons type="location" size="16" color="#B5B4BC"></uni-icons>
|
||||
<text class="city">{{ location }}</text>
|
||||
</view>
|
||||
<view class="actions">
|
||||
<view class="action-btn">
|
||||
<uni-icons type="heart" size="24" color="#B5B4BC"></uni-icons>
|
||||
<uni-icons type="heart-filled" size="24" color="#ffcd32"></uni-icons>
|
||||
<text class="text">{{ likes }} 喜欢</text>
|
||||
</view>
|
||||
<view class="action-btn">
|
||||
<uni-icons type="chat" size="24" color="#B5B4BC"></uni-icons>
|
||||
<text class="text">{{ comments }} 打招呼</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<
|
||||
Partial<{
|
||||
avatar: string;
|
||||
name: string;
|
||||
verification: boolean;
|
||||
content: string;
|
||||
images: string[];
|
||||
location: string;
|
||||
likes: number | string;
|
||||
comments: number | string;
|
||||
}>
|
||||
>(),
|
||||
{
|
||||
avatar: "",
|
||||
name: "",
|
||||
verification: false,
|
||||
content: "",
|
||||
images: () => [],
|
||||
location: "",
|
||||
likes: 0,
|
||||
comments: 0,
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.moment {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #333;
|
||||
.user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
.avatar {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 20px;
|
||||
margin-right: 10px;
|
||||
background-color: #333;
|
||||
}
|
||||
.details {
|
||||
flex: 1;
|
||||
.username {
|
||||
font-size: $font-size-medium-x;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
.verification {
|
||||
background-color: $color-theme;
|
||||
font-size: $font-size-small;
|
||||
color: black;
|
||||
padding: 4rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
.info {
|
||||
font-size: $font-size-small;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
flex: 1;
|
||||
padding: 0 20px;
|
||||
margin-bottom: 15px;
|
||||
.text {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.images {
|
||||
margin-top: 20rpx;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
.moment-image {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 8px;
|
||||
background-color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
.address {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
padding: 0 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
.city {
|
||||
font-size: $font-size-small;
|
||||
}
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 60rpx;
|
||||
padding: 0 20rpx;
|
||||
.action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
color: $color-text;
|
||||
font-size: $font-size-medium;
|
||||
.text {
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,196 +0,0 @@
|
||||
<!-- TODO 霖:是不是怎么复用 s-count-down 组件 -->
|
||||
<template>
|
||||
<view class="time" :style="justifyLeft">
|
||||
<text class="" v-if="tipText">{{ tipText }}</text>
|
||||
<text
|
||||
class="styleAll p6"
|
||||
v-if="isDay === true"
|
||||
:style="{ background: bgColor.bgColor, color: bgColor.Color }"
|
||||
>{{ day }}{{ bgColor.isDay ? '天' : '' }}</text
|
||||
>
|
||||
<text
|
||||
class="timeTxt"
|
||||
v-if="dayText"
|
||||
:style="{ width: bgColor.timeTxtwidth, color: bgColor.bgColor }"
|
||||
>{{ dayText }}</text
|
||||
>
|
||||
<text
|
||||
class="styleAll"
|
||||
:class="isCol ? 'timeCol' : ''"
|
||||
:style="{ background: bgColor.bgColor, color: bgColor.Color, width: bgColor.width }"
|
||||
>{{ hour }}</text
|
||||
>
|
||||
<text
|
||||
class="timeTxt"
|
||||
v-if="hourText"
|
||||
:class="isCol ? 'whit' : ''"
|
||||
:style="{ width: bgColor.timeTxtwidth, color: bgColor.bgColor }"
|
||||
>{{ hourText }}</text
|
||||
>
|
||||
<text
|
||||
class="styleAll"
|
||||
:class="isCol ? 'timeCol' : ''"
|
||||
:style="{ background: bgColor.bgColor, color: bgColor.Color, width: bgColor.width }"
|
||||
>{{ minute }}</text
|
||||
>
|
||||
<text
|
||||
class="timeTxt"
|
||||
v-if="minuteText"
|
||||
:class="isCol ? 'whit' : ''"
|
||||
:style="{ width: bgColor.timeTxtwidth, color: bgColor.bgColor }"
|
||||
>{{ minuteText }}</text
|
||||
>
|
||||
<text
|
||||
class="styleAll"
|
||||
:class="isCol ? 'timeCol' : ''"
|
||||
:style="{ background: bgColor.bgColor, color: bgColor.Color, width: bgColor.width }"
|
||||
>{{ second }}</text
|
||||
>
|
||||
<text class="timeTxt" v-if="secondText">{{ secondText }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'countDown',
|
||||
props: {
|
||||
justifyLeft: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
//距离开始提示文字
|
||||
tipText: {
|
||||
type: String,
|
||||
default: '倒计时',
|
||||
},
|
||||
dayText: {
|
||||
type: String,
|
||||
default: '天',
|
||||
},
|
||||
hourText: {
|
||||
type: String,
|
||||
default: '时',
|
||||
},
|
||||
minuteText: {
|
||||
type: String,
|
||||
default: '分',
|
||||
},
|
||||
secondText: {
|
||||
type: String,
|
||||
default: '秒',
|
||||
},
|
||||
datatime: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
isDay: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
isCol: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
bgColor: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
day: '00',
|
||||
hour: '00',
|
||||
minute: '00',
|
||||
second: '00',
|
||||
};
|
||||
},
|
||||
created: function () {
|
||||
this.show_time();
|
||||
},
|
||||
mounted: function () {},
|
||||
methods: {
|
||||
show_time: function () {
|
||||
let that = this;
|
||||
|
||||
function runTime() {
|
||||
//时间函数
|
||||
let intDiff = that.datatime - Date.parse(new Date()) / 1000; //获取数据中的时间戳的时间差;
|
||||
let day = 0,
|
||||
hour = 0,
|
||||
minute = 0,
|
||||
second = 0;
|
||||
if (intDiff > 0) {
|
||||
//转换时间
|
||||
if (that.isDay === true) {
|
||||
day = Math.floor(intDiff / (60 * 60 * 24));
|
||||
} else {
|
||||
day = 0;
|
||||
}
|
||||
hour = Math.floor(intDiff / (60 * 60)) - day * 24;
|
||||
minute = Math.floor(intDiff / 60) - day * 24 * 60 - hour * 60;
|
||||
second = Math.floor(intDiff) - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60;
|
||||
if (hour <= 9) hour = '0' + hour;
|
||||
if (minute <= 9) minute = '0' + minute;
|
||||
if (second <= 9) second = '0' + second;
|
||||
that.day = day;
|
||||
that.hour = hour;
|
||||
that.minute = minute;
|
||||
that.second = second;
|
||||
} else {
|
||||
that.day = '00';
|
||||
that.hour = '00';
|
||||
that.minute = '00';
|
||||
that.second = '00';
|
||||
}
|
||||
}
|
||||
runTime();
|
||||
setInterval(runTime, 1000);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.p6 {
|
||||
padding: 0 8rpx;
|
||||
}
|
||||
.styleAll {
|
||||
/* color: #fff; */
|
||||
font-size: 24rpx;
|
||||
height: 36rpx;
|
||||
line-height: 36rpx;
|
||||
border-radius: 6rpx;
|
||||
text-align: center;
|
||||
/* padding: 0 6rpx; */
|
||||
}
|
||||
.timeTxt {
|
||||
text-align: center;
|
||||
/* width: 16rpx; */
|
||||
height: 36rpx;
|
||||
line-height: 36rpx;
|
||||
display: inline-block;
|
||||
}
|
||||
.whit {
|
||||
color: #fff !important;
|
||||
}
|
||||
.time {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.red {
|
||||
color: #fc4141;
|
||||
margin: 0 4rpx;
|
||||
}
|
||||
|
||||
.timeCol {
|
||||
/* width: 40rpx;
|
||||
height: 40rpx;
|
||||
line-height: 40rpx;
|
||||
text-align:center;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
font-size: 24rpx; */
|
||||
color: #e93323;
|
||||
}
|
||||
</style>
|
||||
@@ -1,256 +0,0 @@
|
||||
<!-- 商品信息:满减送等营销活动的弹窗 -->
|
||||
<template>
|
||||
<su-popup :show="show" type="bottom" round="20" @close="emits('close')" showClose>
|
||||
<view class="model-box">
|
||||
<view class="title ss-m-t-16 ss-m-l-20 ss-flex">优惠</view>
|
||||
<view v-if="state.rewardActivity && state.rewardActivity.id > 0">
|
||||
<view class="titleLi">促销</view>
|
||||
<scroll-view
|
||||
class="model-content"
|
||||
scroll-y
|
||||
:scroll-with-animation="false"
|
||||
:enable-back-to-top="true"
|
||||
>
|
||||
<view
|
||||
class="actBox"
|
||||
v-for="(item, index) in getRewardActivityRuleGroupDescriptions(state.rewardActivity)"
|
||||
:key="index"
|
||||
>
|
||||
<view
|
||||
class="boxCont ss-flex ss-col-top ss-m-b-40"
|
||||
@tap="onGoodsList(state.rewardActivity)"
|
||||
>
|
||||
<view class="model-content-tag ss-flex ss-row-center">{{ item.name }}</view>
|
||||
<view class="model-content-title">
|
||||
<view class="contBu">
|
||||
{{ item.values.join(';') }}
|
||||
</view>
|
||||
<view class="ss-m-b-24 cotBu-txt">
|
||||
{{ sheep.$helper.timeFormat(state.rewardActivity.startTime, 'yyyy.mm.dd') }}
|
||||
-
|
||||
{{ sheep.$helper.timeFormat(state.rewardActivity.endTime, 'yyyy.mm.dd') }}
|
||||
</view>
|
||||
</view>
|
||||
<text class="cicon-forward" />
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view class="titleLi">可领优惠券</view>
|
||||
<scroll-view
|
||||
class="model-content"
|
||||
scroll-y
|
||||
:scroll-with-animation="false"
|
||||
:enable-back-to-top="true"
|
||||
v-if="state.couponInfo.length"
|
||||
>
|
||||
<view class="actBox" v-for="item in state.couponInfo" :key="item.id">
|
||||
<view class="boxCont ss-flex ss-col-top ss-m-b-40">
|
||||
<view class="model-content-tag2">
|
||||
<view class="usePrice"> ¥{{ fen2yuan(item.discountPrice) }} </view>
|
||||
<view class="impose"> 满¥{{ fen2yuan(item.usePrice) }}可用 </view>
|
||||
</view>
|
||||
<view class="model-content-title2">
|
||||
<view class="contBu">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
<view class="ss-m-b-24 cotBu-txt">
|
||||
{{
|
||||
item.validityType == 1
|
||||
? sheep.$helper.timeFormat(item.validStartTime, 'yyyy-mm-dd') +
|
||||
'-' +
|
||||
sheep.$helper.timeFormat(item.validEndTime, 'yyyy-mm-dd')
|
||||
: '领取后' + item.fixedStartTerm + '-' + item.fixedEndTerm + '天可用'
|
||||
}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="coupon" @click.stop="getBuy(item.id)" v-if="item.canTake"> 立即领取 </view>
|
||||
<view class="coupon2" v-else> 已领取 </view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="nullBox" v-else> 暂无可领优惠券 </view>
|
||||
</view>
|
||||
</su-popup>
|
||||
</template>
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { getRewardActivityRuleGroupDescriptions } from '@/sheep/hooks/useGoods';
|
||||
import { computed, reactive, watch, ref } from 'vue';
|
||||
import { fen2yuan } from '@/sheep/hooks/useGoods';
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(['close']);
|
||||
const state = reactive({
|
||||
rewardActivity: computed(() => props.modelValue.rewardActivity),
|
||||
couponInfo: computed(() => props.modelValue.couponInfo),
|
||||
});
|
||||
|
||||
// 领取优惠劵
|
||||
const getBuy = (id) => {
|
||||
emits('get', id);
|
||||
};
|
||||
|
||||
function onGoodsList(e) {
|
||||
sheep.$router.go('/pages/activity/index', {
|
||||
activityId: e.id,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.model-box {
|
||||
height: 60vh;
|
||||
|
||||
.title {
|
||||
justify-content: center;
|
||||
font-size: 36rpx;
|
||||
height: 80rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.model-content {
|
||||
height: fit-content;
|
||||
max-height: 380rpx;
|
||||
padding: 0 20rpx;
|
||||
box-sizing: border-box;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.model-content-tag {
|
||||
// background: rgba(#ff6911, 0.1);
|
||||
font-size: 35rpx;
|
||||
font-weight: 500;
|
||||
color: #ff6911;
|
||||
line-height: 150rpx;
|
||||
width: 200rpx;
|
||||
height: 150rpx;
|
||||
text-align: center;
|
||||
|
||||
// border-radius: 5rpx;
|
||||
}
|
||||
|
||||
.model-content-title {
|
||||
width: 450rpx;
|
||||
height: 150rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cicon-forward {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
// 新增的
|
||||
.titleLi {
|
||||
margin: 10rpx 0 10rpx 20rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.actBox {
|
||||
width: 700rpx;
|
||||
height: 150rpx;
|
||||
background-color: #fff2f2;
|
||||
margin: 10rpx auto;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.boxCont {
|
||||
width: 700rpx;
|
||||
height: 150rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.contBu {
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
overflow: hidden;
|
||||
font-size: 30rpx;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
-o-text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.cotBu-txt {
|
||||
height: 70rpx;
|
||||
line-height: 70rpx;
|
||||
font-size: 25rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.model-content-tag2 {
|
||||
font-size: 35rpx;
|
||||
font-weight: 500;
|
||||
color: #ff6911;
|
||||
width: 200rpx;
|
||||
height: 150rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.usePrice {
|
||||
width: 200rpx;
|
||||
height: 90rpx;
|
||||
line-height: 100rpx;
|
||||
// background-color: red;
|
||||
}
|
||||
|
||||
.impose {
|
||||
width: 200rpx;
|
||||
height: 50rpx;
|
||||
// line-height: 75rpx;
|
||||
font-size: 23rpx;
|
||||
// background-color: gold;
|
||||
}
|
||||
|
||||
.model-content-title2 {
|
||||
width: 330rpx;
|
||||
height: 150rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.coupon {
|
||||
width: 150rpx;
|
||||
height: 50rpx;
|
||||
line-height: 50rpx;
|
||||
background-color: rgb(255, 68, 68);
|
||||
color: white;
|
||||
border-radius: 30rpx;
|
||||
text-align: center;
|
||||
font-size: 25rpx;
|
||||
}
|
||||
|
||||
.coupon2 {
|
||||
width: 150rpx;
|
||||
height: 50rpx;
|
||||
line-height: 50rpx;
|
||||
background-color: rgb(203, 192, 191);
|
||||
color: white;
|
||||
border-radius: 30rpx;
|
||||
text-align: center;
|
||||
font-size: 25rpx;
|
||||
}
|
||||
.nullBox {
|
||||
width: 100%;
|
||||
height: 300rpx;
|
||||
font-size: 25rpx;
|
||||
line-height: 300rpx;
|
||||
text-align: center;
|
||||
color: #999999;
|
||||
}
|
||||
</style>
|
||||
@@ -1,110 +0,0 @@
|
||||
<!-- 地址卡片 -->
|
||||
<template>
|
||||
<view
|
||||
class="address-item ss-flex ss-row-between ss-col-center"
|
||||
:class="[{ 'border-bottom': props.hasBorderBottom }]"
|
||||
>
|
||||
<view class="item-left" v-if="!isEmpty(props.item)">
|
||||
<view class="area-text ss-flex ss-col-center">
|
||||
<uni-tag
|
||||
class="ss-m-r-10"
|
||||
size="small"
|
||||
custom-style="background-color: var(--ui-BG-Main); border-color: var(--ui-BG-Main); color: #fff;"
|
||||
v-if="props.item.defaultStatus"
|
||||
text="默认"
|
||||
/>
|
||||
{{ props.item.areaName }}
|
||||
</view>
|
||||
<view class="address-text">
|
||||
{{ props.item.detailAddress }}
|
||||
</view>
|
||||
<view class="person-text"> {{ props.item.name }} {{ props.item.mobile }} </view>
|
||||
</view>
|
||||
<view v-else>
|
||||
<view class="address-text ss-m-b-10">请选择收货地址</view>
|
||||
</view>
|
||||
<slot>
|
||||
<button class="ss-reset-button edit-btn" @tap.stop="onEdit">
|
||||
<view class="edit-icon ss-flex ss-row-center ss-col-center">
|
||||
<image :src="sheep.$url.static('/static/img/shop/user/address/edit.png')" />
|
||||
</view>
|
||||
</button>
|
||||
</slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 基础组件 - 地址卡片
|
||||
*
|
||||
* @param {String} icon = _icon-edit - icon
|
||||
*
|
||||
* @event {Function()} click - 点击
|
||||
* @event {Function()} actionClick - 点击工具栏
|
||||
*
|
||||
* @slot - 默认插槽
|
||||
*/
|
||||
import sheep from '@/sheep';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
const props = defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
hasBorderBottom: {
|
||||
type: Boolean,
|
||||
defult: true,
|
||||
},
|
||||
});
|
||||
|
||||
const onEdit = () => {
|
||||
sheep.$router.go('/pages/user/address/edit', {
|
||||
id: props.item.id,
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.address-item {
|
||||
padding: 24rpx 30rpx;
|
||||
|
||||
.item-left {
|
||||
width: 600rpx;
|
||||
}
|
||||
|
||||
.area-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: $dark-9;
|
||||
}
|
||||
|
||||
.address-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: 48rpx;
|
||||
}
|
||||
|
||||
.person-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
color: $dark-9;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-btn {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
background: $gray-f;
|
||||
border-radius: 50%;
|
||||
|
||||
.edit-icon {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
}
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,107 +0,0 @@
|
||||
<!-- 账号密码登录 accountLogin -->
|
||||
<template>
|
||||
<view>
|
||||
<!-- 标题栏 -->
|
||||
<view class="head-box ss-m-b-60 ss-flex-col">
|
||||
<view class="ss-flex ss-m-b-20">
|
||||
<view class="head-title-active head-title-line" @tap="showAuthModal('smsLogin')">
|
||||
短信登录
|
||||
</view>
|
||||
<view class="head-title ss-m-r-40 head-title-animation">账号登录</view>
|
||||
</view>
|
||||
<view class="head-subtitle">如果未设置过密码,请点击忘记密码</view>
|
||||
</view>
|
||||
|
||||
<!-- 表单项 -->
|
||||
<uni-forms
|
||||
ref="accountLoginRef"
|
||||
v-model="state.model"
|
||||
:rules="state.rules"
|
||||
validateTrigger="bind"
|
||||
labelWidth="140"
|
||||
labelAlign="center"
|
||||
>
|
||||
<uni-forms-item name="mobile" label="账号">
|
||||
<uni-easyinput placeholder="请输入账号" v-model="state.model.mobile" :inputBorder="false">
|
||||
<template v-slot:right>
|
||||
<button class="ss-reset-button forgot-btn" @tap="showAuthModal('resetPassword')">
|
||||
忘记密码
|
||||
</button>
|
||||
</template>
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
|
||||
<uni-forms-item name="password" label="密码">
|
||||
<uni-easyinput
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
v-model="state.model.password"
|
||||
:inputBorder="false"
|
||||
>
|
||||
<template v-slot:right>
|
||||
<button class="ss-reset-button login-btn-start" @tap="accountLoginSubmit">登录</button>
|
||||
</template>
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, unref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { mobile, password } from '@/sheep/validate/form';
|
||||
import { showAuthModal, closeAuthModal } from '@/sheep/hooks/useModal';
|
||||
import AuthUtil from '@/sheep/api/member/auth';
|
||||
|
||||
const accountLoginRef = ref(null);
|
||||
|
||||
const emits = defineEmits(['onConfirm']);
|
||||
|
||||
const props = defineProps({
|
||||
agreeStatus: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
// 数据
|
||||
const state = reactive({
|
||||
model: {
|
||||
mobile: '', // 账号
|
||||
password: '', // 密码
|
||||
},
|
||||
rules: {
|
||||
mobile,
|
||||
password,
|
||||
},
|
||||
});
|
||||
|
||||
// 账号登录
|
||||
async function accountLoginSubmit() {
|
||||
// 表单验证
|
||||
const validate = await unref(accountLoginRef)
|
||||
.validate()
|
||||
.catch((error) => {
|
||||
console.log('error: ', error);
|
||||
});
|
||||
if (!validate) return;
|
||||
|
||||
// 同意协议
|
||||
if (!props.agreeStatus) {
|
||||
emits('onConfirm', true)
|
||||
sheep.$helper.toast('请勾选同意');
|
||||
return;
|
||||
}
|
||||
|
||||
// 提交数据
|
||||
const { code, data } = await AuthUtil.login(state.model);
|
||||
if (code === 0) {
|
||||
closeAuthModal();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../index.scss';
|
||||
</style>
|
||||
@@ -1,127 +0,0 @@
|
||||
<!-- 绑定/更换手机号 changeMobile -->
|
||||
<template>
|
||||
<view>
|
||||
<!-- 标题栏 -->
|
||||
<view class="head-box ss-m-b-60">
|
||||
<view class="head-title ss-m-b-20">
|
||||
{{ userInfo.mobile ? '更换手机号' : '绑定手机号' }}
|
||||
</view>
|
||||
<view class="head-subtitle">为了您的账号安全,请使用本人手机号码</view>
|
||||
</view>
|
||||
|
||||
<!-- 表单项 -->
|
||||
<uni-forms
|
||||
ref="changeMobileRef"
|
||||
v-model="state.model"
|
||||
:rules="state.rules"
|
||||
validateTrigger="bind"
|
||||
labelWidth="140"
|
||||
labelAlign="center"
|
||||
>
|
||||
<uni-forms-item name="mobile" label="手机号">
|
||||
<uni-easyinput
|
||||
placeholder="请输入手机号"
|
||||
v-model="state.model.mobile"
|
||||
:inputBorder="false"
|
||||
type="number"
|
||||
>
|
||||
<template v-slot:right>
|
||||
<button
|
||||
class="ss-reset-button code-btn-start"
|
||||
:disabled="state.isMobileEnd"
|
||||
:class="{ 'code-btn-end': state.isMobileEnd }"
|
||||
@tap="getSmsCode('changeMobile', state.model.mobile)"
|
||||
>
|
||||
{{ getSmsTimer('changeMobile') }}
|
||||
</button>
|
||||
</template>
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
|
||||
<uni-forms-item name="code" label="验证码">
|
||||
<uni-easyinput
|
||||
placeholder="请输入验证码"
|
||||
v-model="state.model.code"
|
||||
:inputBorder="false"
|
||||
type="number"
|
||||
maxlength="4"
|
||||
>
|
||||
<template v-slot:right>
|
||||
<button class="ss-reset-button login-btn-start" @tap="changeMobileSubmit">
|
||||
确认
|
||||
</button>
|
||||
</template>
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
|
||||
<!-- 微信独有:读取手机号 -->
|
||||
<button
|
||||
v-if="'WechatMiniProgram' === sheep.$platform.name"
|
||||
class="ss-reset-button type-btn"
|
||||
open-type="getPhoneNumber"
|
||||
@getphonenumber="getPhoneNumber"
|
||||
>
|
||||
使用微信手机号
|
||||
</button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref, reactive, unref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { code, mobile } from '@/sheep/validate/form';
|
||||
import { closeAuthModal, getSmsCode, getSmsTimer } from '@/sheep/hooks/useModal';
|
||||
import UserApi from '@/sheep/api/member/user';
|
||||
|
||||
const changeMobileRef = ref(null);
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo);
|
||||
|
||||
// 数据
|
||||
const state = reactive({
|
||||
isMobileEnd: false, // 手机号输入完毕
|
||||
model: {
|
||||
mobile: '', // 手机号
|
||||
code: '', // 验证码
|
||||
},
|
||||
rules: {
|
||||
code,
|
||||
mobile,
|
||||
},
|
||||
});
|
||||
|
||||
// 绑定手机号
|
||||
async function changeMobileSubmit() {
|
||||
const validate = await unref(changeMobileRef)
|
||||
.validate()
|
||||
.catch((error) => {
|
||||
console.log('error: ', error);
|
||||
});
|
||||
if (!validate) {
|
||||
return;
|
||||
}
|
||||
// 提交更新请求
|
||||
const { code } = await UserApi.updateUserMobile(state.model);
|
||||
if (code !== 0) {
|
||||
return;
|
||||
}
|
||||
sheep.$store('user').getInfo();
|
||||
closeAuthModal();
|
||||
}
|
||||
|
||||
// 使用微信手机号
|
||||
async function getPhoneNumber(e) {
|
||||
if (e.detail.errMsg !== 'getPhoneNumber:ok') {
|
||||
return;
|
||||
}
|
||||
const result = await sheep.$platform.useProvider().bindUserPhoneNumber(e.detail);
|
||||
if (result) {
|
||||
sheep.$store('user').getInfo();
|
||||
closeAuthModal();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../index.scss';
|
||||
</style>
|
||||
@@ -1,106 +0,0 @@
|
||||
<!-- 修改密码(登录时) -->
|
||||
<template>
|
||||
<view>
|
||||
<!-- 标题栏 -->
|
||||
<view class="head-box ss-m-b-60">
|
||||
<view class="head-title ss-m-b-20">修改密码</view>
|
||||
<view class="head-subtitle">如密码丢失或未设置,请点击忘记密码重新设置</view>
|
||||
</view>
|
||||
|
||||
<!-- 表单项 -->
|
||||
<uni-forms
|
||||
ref="changePasswordRef"
|
||||
v-model="state.model"
|
||||
:rules="state.rules"
|
||||
validateTrigger="bind"
|
||||
labelWidth="140"
|
||||
labelAlign="center"
|
||||
>
|
||||
<uni-forms-item name="code" label="验证码">
|
||||
<uni-easyinput
|
||||
placeholder="请输入验证码"
|
||||
v-model="state.model.code"
|
||||
type="number"
|
||||
maxlength="4"
|
||||
:inputBorder="false"
|
||||
>
|
||||
<template v-slot:right>
|
||||
<button
|
||||
class="ss-reset-button code-btn code-btn-start"
|
||||
:disabled="state.isMobileEnd"
|
||||
:class="{ 'code-btn-end': state.isMobileEnd }"
|
||||
@tap="getSmsCode('changePassword')"
|
||||
>
|
||||
{{ getSmsTimer('resetPassword') }}
|
||||
</button>
|
||||
</template>
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
|
||||
<uni-forms-item name="reNewPassword" label="密码">
|
||||
<uni-easyinput
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
v-model="state.model.password"
|
||||
:inputBorder="false"
|
||||
>
|
||||
<template v-slot:right>
|
||||
<button class="ss-reset-button login-btn-start" @tap="changePasswordSubmit">
|
||||
确认
|
||||
</button>
|
||||
</template>
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
|
||||
<button class="ss-reset-button type-btn" @tap="closeAuthModal">
|
||||
取消修改
|
||||
</button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, unref } from 'vue';
|
||||
import { code, password } from '@/sheep/validate/form';
|
||||
import { closeAuthModal, getSmsCode, getSmsTimer } from '@/sheep/hooks/useModal';
|
||||
import UserApi from '@/sheep/api/member/user';
|
||||
|
||||
const changePasswordRef = ref(null);
|
||||
|
||||
// 数据
|
||||
const state = reactive({
|
||||
model: {
|
||||
mobile: '', // 手机号
|
||||
code: '', // 验证码
|
||||
password: '', // 密码
|
||||
},
|
||||
rules: {
|
||||
code,
|
||||
password,
|
||||
},
|
||||
});
|
||||
|
||||
// 更改密码
|
||||
async function changePasswordSubmit() {
|
||||
// 参数校验
|
||||
const validate = await unref(changePasswordRef)
|
||||
.validate()
|
||||
.catch((error) => {
|
||||
console.log('error: ', error);
|
||||
});
|
||||
if (!validate) {
|
||||
return;
|
||||
}
|
||||
// 发起请求
|
||||
const { code } = await UserApi.updateUserPassword(state.model);
|
||||
if (code !== 0) {
|
||||
return;
|
||||
}
|
||||
// 成功后,只需要关闭弹窗
|
||||
closeAuthModal();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../index.scss';
|
||||
</style>
|
||||
@@ -1,152 +0,0 @@
|
||||
<!-- 微信授权信息 mpAuthorization -->
|
||||
<template>
|
||||
<view>
|
||||
<!-- 标题栏 -->
|
||||
<view class="head-box ss-m-b-60 ss-flex-col">
|
||||
<view class="ss-flex ss-m-b-20">
|
||||
<view class="head-title ss-m-r-40 head-title-animation">授权信息</view>
|
||||
</view>
|
||||
<view class="head-subtitle">完善您的头像、昵称、手机号</view>
|
||||
</view>
|
||||
|
||||
<!-- 表单项 -->
|
||||
<uni-forms
|
||||
ref="accountLoginRef"
|
||||
v-model="state.model"
|
||||
:rules="state.rules"
|
||||
validateTrigger="bind"
|
||||
labelWidth="140"
|
||||
labelAlign="center"
|
||||
>
|
||||
<!-- 获取头像昵称:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/userProfile.html -->
|
||||
<uni-forms-item name="avatar" label="头像">
|
||||
<button
|
||||
class="ss-reset-button avatar-btn"
|
||||
open-type="chooseAvatar"
|
||||
@chooseavatar="onChooseAvatar"
|
||||
>
|
||||
<image
|
||||
class="avatar-img"
|
||||
:src="sheep.$url.cdn(state.model.avatar)"
|
||||
mode="aspectFill"
|
||||
@tap="sheep.$router.go('/pages/user/info')"
|
||||
/>
|
||||
<text class="cicon-forward" />
|
||||
</button>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item name="nickname" label="昵称">
|
||||
<uni-easyinput
|
||||
type="nickname"
|
||||
placeholder="请输入昵称"
|
||||
v-model="state.model.nickname"
|
||||
:inputBorder="false"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
<view class="foot-box">
|
||||
<button class="ss-reset-button authorization-btn" @tap="onConfirm"> 确认授权 </button>
|
||||
</view>
|
||||
</uni-forms>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref, reactive } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { closeAuthModal } from '@/sheep/hooks/useModal';
|
||||
import FileApi from '@/sheep/api/infra/file';
|
||||
import UserApi from '@/sheep/api/member/user';
|
||||
|
||||
const props = defineProps({
|
||||
agreeStatus: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo);
|
||||
|
||||
const accountLoginRef = ref(null);
|
||||
|
||||
// 数据
|
||||
const state = reactive({
|
||||
model: {
|
||||
nickname: userInfo.value.nickname,
|
||||
avatar: userInfo.value.avatar,
|
||||
},
|
||||
rules: {},
|
||||
disabledStyle: {
|
||||
color: '#999',
|
||||
disableColor: '#fff',
|
||||
},
|
||||
});
|
||||
|
||||
// 选择头像(来自微信)
|
||||
function onChooseAvatar(e) {
|
||||
const tempUrl = e.detail.avatarUrl || '';
|
||||
uploadAvatar(tempUrl);
|
||||
}
|
||||
|
||||
// 选择头像(来自文件系统)
|
||||
async function uploadAvatar(tempUrl) {
|
||||
if (!tempUrl) {
|
||||
return;
|
||||
}
|
||||
let { data } = await FileApi.uploadFile(tempUrl);
|
||||
state.model.avatar = data;
|
||||
}
|
||||
|
||||
// 确认授权
|
||||
async function onConfirm() {
|
||||
const { model } = state;
|
||||
const { nickname, avatar } = model;
|
||||
if (!nickname) {
|
||||
sheep.$helper.toast('请输入昵称');
|
||||
return;
|
||||
}
|
||||
if (!avatar) {
|
||||
sheep.$helper.toast('请选择头像');
|
||||
return;
|
||||
}
|
||||
// 发起更新
|
||||
const { code } = await UserApi.updateUser({
|
||||
avatar: state.model.avatar,
|
||||
nickname: state.model.nickname,
|
||||
});
|
||||
// 更新成功
|
||||
if (code === 0) {
|
||||
sheep.$helper.toast('授权成功');
|
||||
await sheep.$store('user').getInfo();
|
||||
closeAuthModal();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../index.scss';
|
||||
|
||||
.foot-box {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.authorization-btn {
|
||||
width: 686rpx;
|
||||
height: 80rpx;
|
||||
background-color: var(--ui-BG-Main);
|
||||
border-radius: 40rpx;
|
||||
color: #fff;
|
||||
}
|
||||
.avatar-img {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 36rpx;
|
||||
}
|
||||
.cicon-forward {
|
||||
font-size: 30rpx;
|
||||
color: #595959;
|
||||
}
|
||||
.avatar-btn {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
@@ -1,119 +0,0 @@
|
||||
<!-- 重置密码(未登录时) -->
|
||||
<template>
|
||||
<view>
|
||||
<!-- 标题栏 -->
|
||||
<view class="head-box ss-m-b-60">
|
||||
<view class="head-title ss-m-b-20">重置密码</view>
|
||||
<view class="head-subtitle">为了您的账号安全,设置密码前请先进行安全验证</view>
|
||||
</view>
|
||||
|
||||
<!-- 表单项 -->
|
||||
<uni-forms
|
||||
ref="resetPasswordRef"
|
||||
v-model="state.model"
|
||||
:rules="state.rules"
|
||||
validateTrigger="bind"
|
||||
labelWidth="140"
|
||||
labelAlign="center"
|
||||
>
|
||||
<uni-forms-item name="mobile" label="手机号">
|
||||
<uni-easyinput
|
||||
placeholder="请输入手机号"
|
||||
v-model="state.model.mobile"
|
||||
type="number"
|
||||
:inputBorder="false"
|
||||
>
|
||||
<template v-slot:right>
|
||||
<button
|
||||
class="ss-reset-button code-btn code-btn-start"
|
||||
:disabled="state.isMobileEnd"
|
||||
:class="{ 'code-btn-end': state.isMobileEnd }"
|
||||
@tap="getSmsCode('resetPassword', state.model.mobile)"
|
||||
>
|
||||
{{ getSmsTimer('resetPassword') }}
|
||||
</button>
|
||||
</template>
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
|
||||
<uni-forms-item name="code" label="验证码">
|
||||
<uni-easyinput
|
||||
placeholder="请输入验证码"
|
||||
v-model="state.model.code"
|
||||
type="number"
|
||||
maxlength="4"
|
||||
:inputBorder="false"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
|
||||
<uni-forms-item name="password" label="密码">
|
||||
<uni-easyinput
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
v-model="state.model.password"
|
||||
:inputBorder="false"
|
||||
>
|
||||
<template v-slot:right>
|
||||
<button class="ss-reset-button login-btn-start" @tap="resetPasswordSubmit">
|
||||
确认
|
||||
</button>
|
||||
</template>
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
|
||||
<button v-if="!isLogin" class="ss-reset-button type-btn" @tap="showAuthModal('accountLogin')">
|
||||
返回登录
|
||||
</button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref, reactive, unref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { code, mobile, password } from '@/sheep/validate/form';
|
||||
import { showAuthModal, closeAuthModal, getSmsCode, getSmsTimer } from '@/sheep/hooks/useModal';
|
||||
import UserApi from '@/sheep/api/member/user';
|
||||
|
||||
const resetPasswordRef = ref(null);
|
||||
const isLogin = computed(() => sheep.$store('user').isLogin);
|
||||
|
||||
// 数据
|
||||
const state = reactive({
|
||||
isMobileEnd: false, // 手机号输入完毕
|
||||
model: {
|
||||
mobile: '', // 手机号
|
||||
code: '', // 验证码
|
||||
password: '', // 密码
|
||||
},
|
||||
rules: {
|
||||
code,
|
||||
mobile,
|
||||
password,
|
||||
},
|
||||
});
|
||||
|
||||
// 重置密码
|
||||
const resetPasswordSubmit = async () => {
|
||||
// 参数校验
|
||||
const validate = await unref(resetPasswordRef)
|
||||
.validate()
|
||||
.catch((error) => {
|
||||
console.log('error: ', error);
|
||||
});
|
||||
if (!validate) {
|
||||
return;
|
||||
}
|
||||
// 发起请求
|
||||
const { code } = await UserApi.resetUserPassword(state.model);
|
||||
if (code !== 0) {
|
||||
return;
|
||||
}
|
||||
// 成功后,用户重新登录
|
||||
showAuthModal('accountLogin')
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../index.scss';
|
||||
</style>
|
||||
@@ -1,119 +0,0 @@
|
||||
<!-- 短信登录 - smsLogin -->
|
||||
<template>
|
||||
<view>
|
||||
<!-- 标题栏 -->
|
||||
<view class="head-box ss-m-b-60">
|
||||
<view class="ss-flex ss-m-b-20">
|
||||
<view class="head-title head-title-line head-title-animation">短信登录</view>
|
||||
<view class="head-title-active ss-m-r-40" @tap="showAuthModal('accountLogin')">
|
||||
账号登录
|
||||
</view>
|
||||
</view>
|
||||
<view class="head-subtitle">未注册的手机号,验证后自动注册账号</view>
|
||||
</view>
|
||||
|
||||
<!-- 表单项 -->
|
||||
<uni-forms
|
||||
ref="smsLoginRef"
|
||||
v-model="state.model"
|
||||
:rules="state.rules"
|
||||
validateTrigger="bind"
|
||||
labelWidth="140"
|
||||
labelAlign="center"
|
||||
>
|
||||
<uni-forms-item name="mobile" label="手机号">
|
||||
<uni-easyinput
|
||||
placeholder="请输入手机号"
|
||||
v-model="state.model.mobile"
|
||||
:inputBorder="false"
|
||||
type="number"
|
||||
>
|
||||
<template v-slot:right>
|
||||
<button
|
||||
class="ss-reset-button code-btn code-btn-start"
|
||||
:disabled="state.isMobileEnd"
|
||||
:class="{ 'code-btn-end': state.isMobileEnd }"
|
||||
@tap="getSmsCode('smsLogin', state.model.mobile)"
|
||||
>
|
||||
{{ getSmsTimer('smsLogin') }}
|
||||
</button>
|
||||
</template>
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
|
||||
<uni-forms-item name="code" label="验证码">
|
||||
<uni-easyinput
|
||||
placeholder="请输入验证码"
|
||||
v-model="state.model.code"
|
||||
:inputBorder="false"
|
||||
type="number"
|
||||
maxlength="4"
|
||||
>
|
||||
<template v-slot:right>
|
||||
<button class="ss-reset-button login-btn-start" @tap="smsLoginSubmit"> 登录 </button>
|
||||
</template>
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, unref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { code, mobile } from '@/sheep/validate/form';
|
||||
import { showAuthModal, closeAuthModal, getSmsCode, getSmsTimer } from '@/sheep/hooks/useModal';
|
||||
import AuthUtil from '@/sheep/api/member/auth';
|
||||
|
||||
const smsLoginRef = ref(null);
|
||||
|
||||
const emits = defineEmits(['onConfirm']);
|
||||
|
||||
const props = defineProps({
|
||||
agreeStatus: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
// 数据
|
||||
const state = reactive({
|
||||
isMobileEnd: false, // 手机号输入完毕
|
||||
codeText: '获取验证码',
|
||||
model: {
|
||||
mobile: '', // 手机号
|
||||
code: '', // 验证码
|
||||
},
|
||||
rules: {
|
||||
code,
|
||||
mobile,
|
||||
},
|
||||
});
|
||||
|
||||
// 短信登录
|
||||
async function smsLoginSubmit() {
|
||||
// 参数校验
|
||||
const validate = await unref(smsLoginRef)
|
||||
.validate()
|
||||
.catch((error) => {
|
||||
console.log('error: ', error);
|
||||
});
|
||||
if (!validate) {
|
||||
return;
|
||||
}
|
||||
if (!props.agreeStatus) {
|
||||
emits('onConfirm', true)
|
||||
sheep.$helper.toast('请勾选同意');
|
||||
return;
|
||||
}
|
||||
// 提交数据
|
||||
const { code } = await AuthUtil.smsLogin(state.model);
|
||||
if (code === 0) {
|
||||
closeAuthModal();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../index.scss';
|
||||
</style>
|
||||
@@ -1,151 +0,0 @@
|
||||
@keyframes title-animation {
|
||||
0% {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
100% {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.login-wrap {
|
||||
padding: 50rpx 34rpx;
|
||||
min-height: 500rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
}
|
||||
|
||||
.head-box {
|
||||
.head-title {
|
||||
min-width: 160rpx;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
.head-title-active {
|
||||
width: 160rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #999;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
.head-title-animation {
|
||||
animation-name: title-animation;
|
||||
animation-duration: 0.1s;
|
||||
animation-timing-function: ease-out;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
.head-title-line {
|
||||
position: relative;
|
||||
&::before {
|
||||
content: '';
|
||||
width: 1rpx;
|
||||
height: 34rpx;
|
||||
background-color: #e4e7ed;
|
||||
position: absolute;
|
||||
left: -30rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
.head-subtitle {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: #afb6c0;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
// .code-btn[disabled] {
|
||||
// background-color: #fff;
|
||||
// }
|
||||
.code-btn-start {
|
||||
width: 160rpx;
|
||||
height: 56rpx;
|
||||
line-height: normal;
|
||||
border: 2rpx solid var(--ui-BG-Main);
|
||||
border-radius: 28rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: var(--ui-BG-Main);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.forgot-btn {
|
||||
width: 160rpx;
|
||||
line-height: 56rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.login-btn-start {
|
||||
width: 158rpx;
|
||||
height: 56rpx;
|
||||
line-height: normal;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
border-radius: 28rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.type-btn {
|
||||
padding: 20rpx;
|
||||
margin: 40rpx auto;
|
||||
width: 200rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.auto-login-box {
|
||||
width: 100%;
|
||||
.auto-login-btn {
|
||||
width: 68rpx;
|
||||
height: 68rpx;
|
||||
border-radius: 50%;
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
.auto-login-img {
|
||||
width: 68rpx;
|
||||
height: 68rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.agreement-box {
|
||||
margin: 80rpx auto 0;
|
||||
.protocol-check {
|
||||
transform: scale(0.7);
|
||||
}
|
||||
.agreement-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
.tcp-text {
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 修改密码
|
||||
.editPwd-btn-box {
|
||||
.save-btn {
|
||||
width: 690rpx;
|
||||
line-height: 70rpx;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
border-radius: 35rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
.forgot-btn {
|
||||
width: 690rpx;
|
||||
line-height: 70rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
<template>
|
||||
<!-- 规格弹窗 -->
|
||||
<su-popup :show="authType !== ''" round="10" :showClose="true" @close="closeAuthModal">
|
||||
<view class="login-wrap">
|
||||
<!-- 1. 账号密码登录 accountLogin -->
|
||||
<account-login
|
||||
v-if="authType === 'accountLogin'"
|
||||
:agreeStatus="state.protocol"
|
||||
@onConfirm="onConfirm"
|
||||
/>
|
||||
|
||||
<!-- 2. 短信登录 smsLogin -->
|
||||
<sms-login
|
||||
v-if="authType === 'smsLogin'"
|
||||
:agreeStatus="state.protocol"
|
||||
@onConfirm="onConfirm"
|
||||
/>
|
||||
|
||||
<!-- 3. 忘记密码 resetPassword-->
|
||||
<reset-password v-if="authType === 'resetPassword'" />
|
||||
|
||||
<!-- 4. 绑定手机号 changeMobile -->
|
||||
<change-mobile v-if="authType === 'changeMobile'" />
|
||||
|
||||
<!-- 5. 修改密码 changePassword-->
|
||||
<changePassword v-if="authType === 'changePassword'" />
|
||||
|
||||
<!-- 6. 微信小程序授权 -->
|
||||
<mp-authorization v-if="authType === 'mpAuthorization'" />
|
||||
|
||||
<!-- 7. 第三方登录 -->
|
||||
<view
|
||||
v-if="['accountLogin', 'smsLogin'].includes(authType)"
|
||||
class="auto-login-box ss-flex ss-flex-col ss-row-center ss-col-center"
|
||||
>
|
||||
<!-- 7.1 微信小程序的快捷登录 -->
|
||||
<view v-if="sheep.$platform.name === 'WechatMiniProgram'" class="ss-flex register-box">
|
||||
<view class="register-title">还没有账号?</view>
|
||||
<button
|
||||
class="ss-reset-button login-btn"
|
||||
open-type="getPhoneNumber"
|
||||
@getphonenumber="getPhoneNumber"
|
||||
>
|
||||
快捷登录
|
||||
</button>
|
||||
<view class="circle" />
|
||||
</view>
|
||||
|
||||
<!-- 7.2 微信的公众号、App、小程序的登录,基于 openid + code -->
|
||||
<button
|
||||
v-if="
|
||||
['WechatOfficialAccount', 'WechatMiniProgram', 'App'].includes(sheep.$platform.name) &&
|
||||
sheep.$platform.isWechatInstalled
|
||||
"
|
||||
@tap="thirdLogin('wechat')"
|
||||
class="ss-reset-button auto-login-btn"
|
||||
>
|
||||
<image
|
||||
class="auto-login-img"
|
||||
:src="sheep.$url.static('/static/img/shop/platform/wechat.png')"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<!-- 7.3 iOS 登录 TODO 芋艿:等后面搞 App 再弄 -->
|
||||
<button
|
||||
v-if="sheep.$platform.os === 'ios' && sheep.$platform.name === 'App'"
|
||||
@tap="thirdLogin('apple')"
|
||||
class="ss-reset-button auto-login-btn"
|
||||
>
|
||||
<image
|
||||
class="auto-login-img"
|
||||
:src="sheep.$url.static('/static/img/shop/platform/apple.png')"
|
||||
/>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 用户协议的勾选 -->
|
||||
<view
|
||||
v-if="['accountLogin', 'smsLogin'].includes(authType)"
|
||||
class="agreement-box ss-flex ss-row-center"
|
||||
:class="{ shake: currentProtocol }"
|
||||
>
|
||||
<label class="radio ss-flex ss-col-center" @tap="onChange">
|
||||
<radio
|
||||
:checked="state.protocol"
|
||||
color="var(--ui-BG-Main)"
|
||||
style="transform: scale(0.8)"
|
||||
@tap.stop="onChange"
|
||||
/>
|
||||
<view class="agreement-text ss-flex ss-col-center ss-m-l-8">
|
||||
我已阅读并遵守
|
||||
<view class="tcp-text" @tap.stop="onProtocol('用户协议')"> 《用户协议》 </view>
|
||||
<view class="agreement-text">与</view>
|
||||
<view class="tcp-text" @tap.stop="onProtocol('隐私协议')"> 《隐私协议》 </view>
|
||||
</view>
|
||||
</label>
|
||||
</view>
|
||||
<view class="safe-box" />
|
||||
</view>
|
||||
</su-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import accountLogin from './components/account-login.vue';
|
||||
import smsLogin from './components/sms-login.vue';
|
||||
import resetPassword from './components/reset-password.vue';
|
||||
import changeMobile from './components/change-mobile.vue';
|
||||
import changePassword from './components/change-password.vue';
|
||||
import mpAuthorization from './components/mp-authorization.vue';
|
||||
import { closeAuthModal, showAuthModal } from '@/sheep/hooks/useModal';
|
||||
|
||||
const modalStore = sheep.$store('modal');
|
||||
// 授权弹窗类型
|
||||
const authType = computed(() => modalStore.auth);
|
||||
|
||||
const state = reactive({
|
||||
protocol: false,
|
||||
});
|
||||
|
||||
const currentProtocol = ref(false);
|
||||
|
||||
// 勾选协议
|
||||
function onChange() {
|
||||
state.protocol = !state.protocol;
|
||||
}
|
||||
|
||||
// 查看协议
|
||||
function onProtocol(title) {
|
||||
closeAuthModal();
|
||||
sheep.$router.go('/pages/public/richtext', {
|
||||
title,
|
||||
});
|
||||
}
|
||||
|
||||
// 点击登录 / 注册事件
|
||||
function onConfirm(e) {
|
||||
currentProtocol.value = e;
|
||||
setTimeout(() => {
|
||||
currentProtocol.value = false;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 第三方授权登陆(微信小程序、Apple)
|
||||
const thirdLogin = async (provider) => {
|
||||
if (!state.protocol) {
|
||||
currentProtocol.value = true;
|
||||
setTimeout(() => {
|
||||
currentProtocol.value = false;
|
||||
}, 1000);
|
||||
sheep.$helper.toast('请勾选同意');
|
||||
return;
|
||||
}
|
||||
const loginRes = await sheep.$platform.useProvider(provider).login();
|
||||
if (loginRes) {
|
||||
const userInfo = await sheep.$store('user').getInfo();
|
||||
closeAuthModal();
|
||||
// 如果用户已经有头像和昵称,不需要再次授权
|
||||
if (userInfo.avatar && userInfo.nickname) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 触发小程序授权信息弹框
|
||||
// #ifdef MP-WEIXIN
|
||||
showAuthModal('mpAuthorization');
|
||||
// #endif
|
||||
}
|
||||
};
|
||||
|
||||
// 微信小程序的“手机号快速验证”:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html
|
||||
const getPhoneNumber = async (e) => {
|
||||
if (e.detail.errMsg !== 'getPhoneNumber:ok') {
|
||||
sheep.$helper.toast('快捷登录失败');
|
||||
return;
|
||||
}
|
||||
let result = await sheep.$platform.useProvider().mobileLogin(e.detail);
|
||||
if (result) {
|
||||
closeAuthModal();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
|
||||
.shake {
|
||||
animation: shake 0.05s linear 4 alternate;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
from {
|
||||
transform: translateX(-10rpx);
|
||||
}
|
||||
to {
|
||||
transform: translateX(10rpx);
|
||||
}
|
||||
}
|
||||
|
||||
.register-box {
|
||||
position: relative;
|
||||
justify-content: center;
|
||||
.register-btn {
|
||||
color: #999999;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
.register-title {
|
||||
color: #999999;
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
.or-title {
|
||||
margin: 0 16rpx;
|
||||
color: #999999;
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
.login-btn {
|
||||
color: var(--ui-BG-Main);
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
.circle {
|
||||
position: absolute;
|
||||
right: 0rpx;
|
||||
top: 18rpx;
|
||||
width: 8rpx;
|
||||
height: 8rpx;
|
||||
border-radius: 8rpx;
|
||||
background: var(--ui-BG-Main);
|
||||
}
|
||||
}
|
||||
.safe-box {
|
||||
height: calc(constant(safe-area-inset-bottom) / 5 * 3);
|
||||
height: calc(env(safe-area-inset-bottom) / 5 * 3);
|
||||
}
|
||||
|
||||
.tcp-text {
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
|
||||
.agreement-text {
|
||||
color: $dark-9;
|
||||
}
|
||||
</style>
|
||||
@@ -1,134 +0,0 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- 基础组件:搜索框 -->
|
||||
<s-search-block v-if="type === 'SearchBar'" :data="data" :styles="styles" :navbar="false" />
|
||||
<!-- 基础组件:公告栏 -->
|
||||
<s-notice-block v-if="type === 'NoticeBar'" :data="data" />
|
||||
<!-- 基础组件:菜单导航 -->
|
||||
<s-menu-button v-if="type === 'MenuSwiper'" :data="data" :styles="styles" />
|
||||
<!-- 基础组件:列表导航 -->
|
||||
<s-menu-list v-if="type === 'MenuList'" :data="data" />
|
||||
<!-- 基础组件:宫格导航 -->
|
||||
<s-menu-grid v-if="type === 'MenuGrid'" :data="data" :styles="styles" />
|
||||
<!-- 基础组件:弹窗广告 -->
|
||||
<s-popup-image v-if="type === 'Popover'" :data="data" />
|
||||
<!-- 基础组件:悬浮按钮 -->
|
||||
<s-float-menu v-if="type === 'FloatingActionButton'" :data="data" />
|
||||
|
||||
<!-- 图文组件:图片展示 -->
|
||||
<s-image-block v-if="type === 'ImageBar'" :data="data" :styles="styles" />
|
||||
<!-- 图文组件:图片轮播 -->
|
||||
<s-image-banner v-if="type === 'Carousel'" :data="data" :styles="styles" />
|
||||
<!-- 基础组件:标题栏 -->
|
||||
<s-title-block v-if="type === 'TitleBar'" :data="data" :styles="styles" />
|
||||
<!-- 图文组件:广告魔方 -->
|
||||
<s-image-cube v-if="type === 'MagicCube'" :data="data" :styles="styles" />
|
||||
<!-- 图文组件:视频播放 -->
|
||||
<s-video-block v-if="type === 'VideoPlayer'" :data="data" :styles="styles" />
|
||||
<!-- 基础组件:分割线 -->
|
||||
<s-line-block v-if="type === 'Divider'" :data="data" />
|
||||
<!-- 图文组件:热区 -->
|
||||
<s-hotzone-block v-if="type === 'HotZone'" :data="data" :styles="styles" />
|
||||
|
||||
<!-- 商品组件:商品卡片 -->
|
||||
<s-goods-card v-if="type === 'ProductCard'" :data="data" :styles="styles" />
|
||||
<!-- 商品组件:商品栏 -->
|
||||
<s-goods-shelves v-if="type === 'ProductList'" :data="data" :styles="styles" />
|
||||
|
||||
<!-- 营销组件:拼团 -->
|
||||
<s-groupon-block v-if="type === 'PromotionCombination'" :data="data" :styles="styles" />
|
||||
<!-- 营销组件:秒杀 -->
|
||||
<s-seckill-block v-if="type === 'PromotionSeckill'" :data="data" :styles="styles" />
|
||||
<!-- 营销组件:积分商城 -->
|
||||
<s-point-block v-if="type === 'PromotionPoint'" :data="data" :styles="styles" />
|
||||
<!-- 营销组件:小程序直播(暂时没有这个功能) -->
|
||||
<s-live-block v-if="type === 'MpLive'" :data="data" :styles="styles" />
|
||||
<!-- 营销组件:优惠券 -->
|
||||
<s-coupon-block v-if="type === 'CouponCard'" :data="data" :styles="styles" />
|
||||
<!-- 营销组件:文章 -->
|
||||
<s-richtext-block v-if="type === 'PromotionArticle'" :data="data" :styles="styles" />
|
||||
|
||||
<!-- 用户组件:用户卡片 -->
|
||||
<s-user-card v-if="type === 'UserCard'" :data="data" :styles="styles" />
|
||||
<!-- 用户组件:用户订单 -->
|
||||
<s-order-card v-if="type === 'UserOrder'" :data="data" :styles="styles" />
|
||||
<!-- 用户组件:用户资产 -->
|
||||
<s-wallet-card v-if="type === 'UserWallet'" :data="data" :styles="styles" />
|
||||
<!-- 用户组件:用户卡券 -->
|
||||
<s-coupon-card v-if="type === 'UserCoupon'" :data="data" :styles="styles" />
|
||||
|
||||
<!-- 分销组件:分销中心 -->
|
||||
<!-- 分销组件:分销中心 -->
|
||||
<!-- <img src="@/static/fenxiao.png" alt="分销中心" width="1rpx" height="1rpx" /> -->
|
||||
|
||||
<view class="distribution-item" @click="goToDistribution" v-if="type === 'UserCoupon'">
|
||||
<view class="distribution-icon">
|
||||
<uni-icons type="link" size="30"></uni-icons>
|
||||
</view>
|
||||
<view class="distribution-text">分销中心</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
|
||||
/**
|
||||
* 装修组件 - 组件集
|
||||
*/
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
|
||||
function onSearch() {}
|
||||
|
||||
// 跳转到分销中心
|
||||
function goToDistribution() {
|
||||
sheep.$router.go('/pages/commission/index');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.distribution-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
padding-top: 16rpx;
|
||||
margin-left: 64rpx;
|
||||
/* margin: 20rpx; */
|
||||
cursor: pointer;
|
||||
width: auto;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.distribution-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.distribution-text {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
text-align: left;
|
||||
margin-left: -8rpx;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.distribution-item:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
</style>
|
||||
@@ -1,54 +0,0 @@
|
||||
<!-- 装修组件容器 -->
|
||||
<template>
|
||||
<view :style="[elStyles, elBackground]"><slot /></view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 容器组件 - 装修组件的样式容器
|
||||
*/
|
||||
import { computed, provide, unref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const props = defineProps({
|
||||
styles: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
|
||||
// 组件样式
|
||||
|
||||
const elBackground = computed(() => {
|
||||
if (props.styles) {
|
||||
if (props.styles.bgType === 'color')
|
||||
return { background: props.styles.bgColor };
|
||||
if (props.styles.bgType === 'img')
|
||||
return {
|
||||
background: `url(${sheep.$url.cdn(
|
||||
props.styles.bgImage,
|
||||
)}) no-repeat top center / 100% auto`,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const elStyles = computed(() => {
|
||||
if (props.styles) {
|
||||
return {
|
||||
marginTop: `${props.styles.marginTop || 0}px`,
|
||||
marginBottom: `${props.styles.marginBottom || 0}px`,
|
||||
marginLeft: `${props.styles.marginLeft || 0}px`,
|
||||
marginRight: `${props.styles.marginRight || 0}px`,
|
||||
paddingTop: `${props.styles.paddingTop || 0}px`,
|
||||
paddingRight: `${props.styles.paddingRight || 0}px`,
|
||||
paddingBottom: `${props.styles.paddingBottom || 0}px`,
|
||||
paddingLeft: `${props.styles.paddingLeft || 0}px`,
|
||||
borderTopLeftRadius: `${props.styles.borderTopLeftRadius || 0}px`,
|
||||
borderTopRightRadius: `${props.styles.borderTopRightRadius || 0}px`,
|
||||
borderBottomRightRadius: `${props.styles.borderBottomRightRadius || 0}px`,
|
||||
borderBottomLeftRadius: `${props.styles.borderBottomLeftRadius || 0}px`,
|
||||
overflow: 'hidden',
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -1,173 +0,0 @@
|
||||
<template>
|
||||
<view class="time" :style="justifyLeft">
|
||||
<text class="" v-if="tipText">{{ tipText }}</text>
|
||||
<text class="styleAll p6" v-if="isDay === true"
|
||||
:style="{background:bgColor.bgColor,color:bgColor.Color}">{{ day }}{{bgColor.isDay?'天':''}}</text>
|
||||
<text class="timeTxt" v-if="dayText"
|
||||
:style="{width:bgColor.timeTxtwidth,color:bgColor.bgColor}">{{ dayText }}</text>
|
||||
<text class="styleAll" :class='isCol?"timeCol":""'
|
||||
:style="{background:bgColor.bgColor,color:bgColor.Color,width:bgColor.width}">{{ hour }}</text>
|
||||
<text class="timeTxt" v-if="hourText" :class='isCol?"whit":""'
|
||||
:style="{width:bgColor.timeTxtwidth,color:bgColor.bgColor}">{{ hourText }}</text>
|
||||
<text class="styleAll" :class='isCol?"timeCol":""'
|
||||
:style="{background:bgColor.bgColor,color:bgColor.Color,width:bgColor.width}">{{ minute }}</text>
|
||||
<text class="timeTxt" v-if="minuteText" :class='isCol?"whit":""'
|
||||
:style="{width:bgColor.timeTxtwidth,color:bgColor.bgColor}">{{ minuteText }}</text>
|
||||
<text class="styleAll" :class='isCol?"timeCol":""'
|
||||
:style="{background:bgColor.bgColor,color:bgColor.Color,width:bgColor.width}">{{ second }}</text>
|
||||
<text class="timeTxt" v-if="secondText">{{ secondText }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "countDown",
|
||||
props: {
|
||||
justifyLeft: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
//距离开始提示文字
|
||||
tipText: {
|
||||
type: String,
|
||||
default: "倒计时"
|
||||
},
|
||||
dayText: {
|
||||
type: String,
|
||||
default: "天"
|
||||
},
|
||||
hourText: {
|
||||
type: String,
|
||||
default: "时"
|
||||
},
|
||||
minuteText: {
|
||||
type: String,
|
||||
default: "分"
|
||||
},
|
||||
secondText: {
|
||||
type: String,
|
||||
default: "秒"
|
||||
},
|
||||
datatime: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
isDay: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
isCol: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
bgColor: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
day: "00",
|
||||
hour: "00",
|
||||
minute: "00",
|
||||
second: "00"
|
||||
};
|
||||
},
|
||||
created: function() {
|
||||
this.show_time();
|
||||
},
|
||||
mounted: function() {},
|
||||
methods: {
|
||||
show_time: function() {
|
||||
let that = this;
|
||||
|
||||
function runTime() {
|
||||
//时间函数
|
||||
let intDiff = that.datatime - Date.parse(new Date()) / 1000; //获取数据中的时间戳的时间差;
|
||||
let day = 0,
|
||||
hour = 0,
|
||||
minute = 0,
|
||||
second = 0;
|
||||
if (intDiff > 0) {
|
||||
//转换时间
|
||||
if (that.isDay === true) {
|
||||
day = Math.floor(intDiff / (60 * 60 * 24));
|
||||
} else {
|
||||
day = 0;
|
||||
}
|
||||
hour = Math.floor(intDiff / (60 * 60)) - day * 24;
|
||||
minute = Math.floor(intDiff / 60) - day * 24 * 60 - hour * 60;
|
||||
second =
|
||||
Math.floor(intDiff) -
|
||||
day * 24 * 60 * 60 -
|
||||
hour * 60 * 60 -
|
||||
minute * 60;
|
||||
if (hour <= 9) hour = "0" + hour;
|
||||
if (minute <= 9) minute = "0" + minute;
|
||||
if (second <= 9) second = "0" + second;
|
||||
that.day = day;
|
||||
that.hour = hour;
|
||||
that.minute = minute;
|
||||
that.second = second;
|
||||
} else {
|
||||
that.day = "00";
|
||||
that.hour = "00";
|
||||
that.minute = "00";
|
||||
that.second = "00";
|
||||
}
|
||||
}
|
||||
runTime();
|
||||
setInterval(runTime, 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.p6 {
|
||||
padding: 0 8rpx;
|
||||
}
|
||||
|
||||
.styleAll {
|
||||
/* color: #fff; */
|
||||
font-size: 24rpx;
|
||||
height: 36rpx;
|
||||
line-height: 36rpx;
|
||||
border-radius: 6rpx;
|
||||
text-align: center;
|
||||
/* padding: 0 6rpx; */
|
||||
}
|
||||
|
||||
.timeTxt {
|
||||
text-align: center;
|
||||
/* width: 16rpx; */
|
||||
height: 36rpx;
|
||||
line-height: 36rpx;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.whit {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.time {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.red {
|
||||
color: #fc4141;
|
||||
margin: 0 4rpx;
|
||||
}
|
||||
|
||||
.timeCol {
|
||||
/* width: 40rpx;
|
||||
height: 40rpx;
|
||||
line-height: 40rpx;
|
||||
text-align:center;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
font-size: 24rpx; */
|
||||
color: #E93323;
|
||||
}
|
||||
</style>
|
||||
@@ -1,176 +0,0 @@
|
||||
<!-- 装修营销组件:优惠券 -->
|
||||
<template>
|
||||
<scroll-view class="scroll-box" scroll-x scroll-anchoring :style="[bgStyle, { marginLeft: `${data.space}px` }]">
|
||||
<view class="coupon-box ss-flex" :style="couponList.length === 2 ? couponBoxStyleTwo : couponBoxStyleNormal">
|
||||
<view class="coupon-item" :style="[couponBg, { marginLeft: `${data.space}px` }]"
|
||||
v-for="(item, index) in couponList" :key="index">
|
||||
<su-coupon :size="SIZE_LIST[columns - 1]" :textColor="data.textColor" background="" :couponId="item.id"
|
||||
:title="item.name" :type="formatCouponDiscountType(item)" :value="formatCouponDiscountValue(item)"
|
||||
:sellBy="formatValidityType(item)">
|
||||
<template v-slot:btn>
|
||||
<!-- 两列时,领取按钮坚排 -->
|
||||
<button v-if="columns === 2" @click.stop="onGetCoupon(item.id)"
|
||||
class="ss-reset-button card-btn vertical" :style="[btnStyles]">
|
||||
<view class="btn-text">立即领取</view>
|
||||
</button>
|
||||
<button v-else class="ss-reset-button card-btn" :style="[btnStyles]"
|
||||
@click.stop="onGetCoupon(item.id)">
|
||||
立即领取
|
||||
</button>
|
||||
</template>
|
||||
</su-coupon>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import CouponApi from '@/sheep/api/promotion/coupon';
|
||||
import {
|
||||
ref,
|
||||
onMounted,
|
||||
computed
|
||||
} from 'vue';
|
||||
import {
|
||||
CouponTemplateValidityTypeEnum,
|
||||
PromotionDiscountTypeEnum
|
||||
} from "@/sheep/util/const";
|
||||
import {
|
||||
floatToFixed2,
|
||||
formatDate
|
||||
} from "@/sheep/util";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
const {
|
||||
columns,
|
||||
button
|
||||
} = props.data;
|
||||
const SIZE_LIST = ['lg', 'md', 'xs']
|
||||
const couponBg = {
|
||||
background: `url(${sheep.$url.cdn(props.data.bgImg)}) no-repeat top center / 100% 100%`,
|
||||
};
|
||||
const btnStyles = {
|
||||
background: button.bgColor,
|
||||
color: button.color,
|
||||
};
|
||||
// 两列优惠券时的排版方式
|
||||
const couponBoxStyleNormal = {
|
||||
'display': 'flex',
|
||||
'justify-content': 'space-between'
|
||||
};
|
||||
// 非两列优惠券时的排版方式
|
||||
const couponBoxStyleTwo = {
|
||||
'display': 'flex',
|
||||
'justify-content': 'space-around'
|
||||
};
|
||||
// 设置背景样式
|
||||
const bgStyle = computed(() => {
|
||||
// 直接从 props.styles 解构
|
||||
const {
|
||||
bgType,
|
||||
bgImg,
|
||||
bgColor
|
||||
} = props.styles;
|
||||
|
||||
// 根据 bgType 返回相应的样式
|
||||
return {
|
||||
background: bgType === 'img' ? `url(${bgImg}) no-repeat top center / 100% 100%` : bgColor
|
||||
};
|
||||
});
|
||||
// 格式化【折扣类型】
|
||||
const formatCouponDiscountType = (coupon) => {
|
||||
if (coupon.discountType === PromotionDiscountTypeEnum.PRICE.type) {
|
||||
return 'reduce'
|
||||
}
|
||||
if (coupon.discountType === PromotionDiscountTypeEnum.PERCENT.type) {
|
||||
return 'percent'
|
||||
}
|
||||
return `未知【${coupon.discountType}】`
|
||||
}
|
||||
|
||||
// 格式化【折扣】
|
||||
const formatCouponDiscountValue = (coupon) => {
|
||||
if (coupon.discountType === PromotionDiscountTypeEnum.PRICE.type) {
|
||||
return floatToFixed2(coupon.discountPrice)
|
||||
}
|
||||
if (coupon.discountType === PromotionDiscountTypeEnum.PERCENT.type) {
|
||||
return coupon.discountPercent
|
||||
}
|
||||
return `未知【${coupon.discountType}】`
|
||||
}
|
||||
|
||||
// 格式化【有效期限】
|
||||
const formatValidityType = (row) => {
|
||||
if (row.validityType === CouponTemplateValidityTypeEnum.DATE.type) {
|
||||
return `${formatDate(row.validStartTime)} 至 ${formatDate(row.validEndTime)}`
|
||||
}
|
||||
if (row.validityType === CouponTemplateValidityTypeEnum.TERM.type) {
|
||||
return `领取后第 ${row.fixedStartTerm} - ${row.fixedEndTerm} 天内可用`
|
||||
}
|
||||
return '未知【' + row.validityType + '】'
|
||||
}
|
||||
|
||||
const couponList = ref([]);
|
||||
// 立即领取优惠券
|
||||
async function onGetCoupon(id) {
|
||||
const {
|
||||
error,
|
||||
msg
|
||||
} = await CouponApi.takeCoupon(id);
|
||||
if (error === 0) {
|
||||
uni.showToast({
|
||||
title: msg,
|
||||
icon: 'none',
|
||||
});
|
||||
return
|
||||
}
|
||||
await getCouponTemplateList()
|
||||
}
|
||||
const getCouponTemplateList = async () => {
|
||||
const {
|
||||
data
|
||||
} = await CouponApi.getCouponTemplateListByIds(props.data.couponIds.join(','));
|
||||
couponList.value = data;
|
||||
}
|
||||
onMounted(() => {
|
||||
getCouponTemplateList()
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-btn {
|
||||
width: 140rpx;
|
||||
height: 50rpx;
|
||||
border-radius: 25rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 50rpx;
|
||||
|
||||
&.vertical {
|
||||
width: 50rpx;
|
||||
height: 140rpx;
|
||||
margin: auto 20rpx auto 0;
|
||||
|
||||
.btn-text {
|
||||
font-size: 24rpx;
|
||||
text-align: center;
|
||||
writing-mode: vertical-lr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-item {
|
||||
&:nth-of-type(1) {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,102 +0,0 @@
|
||||
<!-- 装修用户组件:用户卡券 -->
|
||||
<template>
|
||||
<view class="ss-coupon-menu-wrap ss-flex ss-col-center" :style="[bgStyle, { marginLeft: `${data.space}px` }]">
|
||||
<view class="menu-item ss-flex-col ss-row-center ss-col-center" v-for="item in props.list" :key="item.title"
|
||||
@tap="sheep.$router.go(item.path, { type: item.type })"
|
||||
:class="item.type === 'all' ? 'menu-wallet' : 'ss-flex-1'">
|
||||
<image class="item-icon" :src="sheep.$url.static(item.icon)" mode="aspectFit"></image>
|
||||
<view class="menu-title ss-m-t-28">{{ item.title }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 装修组件 - 优惠券菜单
|
||||
*/
|
||||
import sheep from '@/sheep';
|
||||
import { computed } from 'vue';
|
||||
|
||||
// 接收参数
|
||||
const props = defineProps({
|
||||
list: {
|
||||
type: Array,
|
||||
default () {
|
||||
return [{
|
||||
title: '已领取',
|
||||
value: '0',
|
||||
icon: '/static/img/shop/order/nouse_coupon.png',
|
||||
path: '/pages/coupon/list',
|
||||
type: 'geted',
|
||||
},
|
||||
{
|
||||
title: '已使用',
|
||||
value: '0',
|
||||
icon: '/static/img/shop/order/useend_coupon.png',
|
||||
path: '/pages/coupon/list',
|
||||
type: 'used',
|
||||
},
|
||||
{
|
||||
title: '已失效',
|
||||
value: '0',
|
||||
icon: '/static/img/shop/order/out_coupon.png',
|
||||
path: '/pages/coupon/list',
|
||||
type: 'expired',
|
||||
},
|
||||
{
|
||||
title: '领券中心',
|
||||
value: '0',
|
||||
icon: '/static/img/shop/order/all_coupon.png',
|
||||
path: '/pages/coupon/list',
|
||||
type: 'all',
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
// 装修数据
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
// 装修样式
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
// 设置背景样式
|
||||
const bgStyle = computed(() => {
|
||||
// 直接从 props.styles 解构
|
||||
const { bgType, bgImg, bgColor } = props.styles;
|
||||
|
||||
// 根据 bgType 返回相应的样式
|
||||
return {
|
||||
background: bgType === 'img'
|
||||
? `url(${bgImg}) no-repeat top center / 100% 100%`
|
||||
: bgColor
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ss-coupon-menu-wrap {
|
||||
.menu-item {
|
||||
height: 160rpx;
|
||||
|
||||
.menu-title {
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-wallet {
|
||||
width: 144rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,109 +0,0 @@
|
||||
<!-- 商品详情 - 优惠劵领取 -->
|
||||
<template>
|
||||
<su-popup
|
||||
:show="show"
|
||||
type="bottom"
|
||||
round="20"
|
||||
@close="emits('close')"
|
||||
showClose
|
||||
backgroundColor="#f2f2f2"
|
||||
>
|
||||
<view class="model-box">
|
||||
<view class="title ss-m-t-16 ss-m-l-20 ss-flex">优惠券</view>
|
||||
<scroll-view
|
||||
class="model-content"
|
||||
scroll-y
|
||||
:scroll-with-animation="false"
|
||||
:enable-back-to-top="true"
|
||||
>
|
||||
<view class="subtitle ss-m-l-20">可使用优惠券</view>
|
||||
<view v-for="item in state.couponInfo" :key="item.id">
|
||||
<s-coupon-list :data="item">
|
||||
<template #default>
|
||||
<button
|
||||
class="ss-reset-button card-btn ss-flex ss-row-center ss-col-center"
|
||||
:class="!item.canTake ? 'boder-btn' : ''"
|
||||
@click.stop="getBuy(item.id)"
|
||||
:disabled="!item.canTake"
|
||||
>
|
||||
{{ item.canTake ? '立即领取' : '已领取' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-coupon-list>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</su-popup>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, reactive } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['get', 'close']);
|
||||
|
||||
const state = reactive({
|
||||
couponInfo: computed(() => props.modelValue)
|
||||
});
|
||||
|
||||
// 领取优惠劵
|
||||
const getBuy = (id) => {
|
||||
emits('get', id);
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.model-box {
|
||||
height: 60vh;
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
height: 80rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
.model-content {
|
||||
height: 54vh;
|
||||
}
|
||||
.modal-footer {
|
||||
width: 100%;
|
||||
height: 120rpx;
|
||||
background: #fff;
|
||||
}
|
||||
.confirm-btn {
|
||||
width: 710rpx;
|
||||
margin-left: 20rpx;
|
||||
height: 80rpx;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
border-radius: 40rpx;
|
||||
color: #fff;
|
||||
}
|
||||
// 优惠券按钮
|
||||
.card-btn {
|
||||
// width: 144rpx;
|
||||
padding: 0 16rpx;
|
||||
height: 50rpx;
|
||||
border-radius: 40rpx;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
color: #ffffff;
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
.boder-btn {
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main-opacity-4), var(--ui-BG-Main-light));
|
||||
color: #fff !important;
|
||||
}
|
||||
</style>
|
||||
@@ -1,195 +0,0 @@
|
||||
<template>
|
||||
<view class="ss-m-20" :style="{ opacity: disabled ? '0.5' : '1' }">
|
||||
<view class="content">
|
||||
<view
|
||||
class="tag ss-flex ss-row-center"
|
||||
:class="isDisable ? 'disabled-bg-color' : 'info-bg-color'"
|
||||
>
|
||||
{{ data.discountType === 1 ? '满减券' : '折扣券' }}
|
||||
</view>
|
||||
<view class="title ss-m-x-30 ss-p-t-18">
|
||||
<view class="ss-flex ss-row-between">
|
||||
<view
|
||||
class="value-text ss-flex-1 ss-m-r-10"
|
||||
:class="isDisable ? 'disabled-color' : 'info-color'"
|
||||
>
|
||||
{{ data.name }}
|
||||
</view>
|
||||
<view>
|
||||
<view
|
||||
class="ss-flex ss-col-bottom"
|
||||
:class="isDisable ? 'disabled-color' : 'price-text'"
|
||||
>
|
||||
<view class="value-reduce ss-m-b-10" v-if="data.discountType === 1">¥</view>
|
||||
<view class="value-price">
|
||||
{{
|
||||
data.discountType === 1
|
||||
? fen2yuan(data.discountPrice)
|
||||
: data.discountPercent / 10.0
|
||||
}}
|
||||
</view>
|
||||
<view class="value-discount ss-m-b-10 ss-m-l-4" v-if="data.discountType === 2"
|
||||
>折</view
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="ss-flex ss-row-between ss-m-t-16">
|
||||
<view
|
||||
class="sellby-text"
|
||||
:class="isDisable ? 'disabled-color' : 'subtitle-color'"
|
||||
v-if="data.validityType === 2"
|
||||
>
|
||||
有效期:领取后 {{ data.fixedEndTerm }} 天内可用
|
||||
</view>
|
||||
<view class="sellby-text" :class="isDisable ? 'disabled-color' : 'subtitle-color'" v-else>
|
||||
有效期: {{ sheep.$helper.timeFormat(data.validStartTime, 'yyyy-mm-dd') }} 至
|
||||
{{ sheep.$helper.timeFormat(data.validEndTime, 'yyyy-mm-dd') }}
|
||||
</view>
|
||||
<view class="value-enough" :class="isDisable ? 'disabled-color' : 'subtitle-color'">
|
||||
满 {{ fen2yuan(data.usePrice) }} 可用
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="desc ss-flex ss-row-between">
|
||||
<view>
|
||||
<view class="desc-title">{{ data.description }}</view>
|
||||
<view>
|
||||
<slot name="reason" />
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<slot />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { fen2yuan } from '../../hooks/useGoods';
|
||||
import sheep from '../../index';
|
||||
|
||||
const isDisable = computed(() => {
|
||||
if (props.type === 'coupon') {
|
||||
return false;
|
||||
}
|
||||
return props.disabled;
|
||||
});
|
||||
|
||||
// 接受参数
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'coupon', // coupon 优惠劵模版;user 用户优惠劵
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.info-bg-color {
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
}
|
||||
|
||||
.disabled-bg-color {
|
||||
background: #999;
|
||||
}
|
||||
|
||||
.info-color {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.subtitle-color {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.disabled-color {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
-webkit-mask: radial-gradient(circle at 12rpx 100%, #0000 12rpx, red 0) -12rpx;
|
||||
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.04);
|
||||
|
||||
.tag {
|
||||
width: 100rpx;
|
||||
|
||||
color: #fff;
|
||||
height: 40rpx;
|
||||
font-size: 24rpx;
|
||||
border-radius: 20rpx 0 20rpx 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
padding-bottom: 22rpx;
|
||||
border-bottom: 2rpx dashed #d3d3d3;
|
||||
|
||||
.value-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.sellby-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.value-price {
|
||||
font-size: 64rpx;
|
||||
font-weight: 500;
|
||||
line-height: normal;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
|
||||
.value-reduce {
|
||||
line-height: normal;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.value-discount {
|
||||
line-height: normal;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.value-enough {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.desc {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
-webkit-mask: radial-gradient(circle at 12rpx 0%, #0000 12rpx, red 0) -12rpx;
|
||||
box-shadow: rgba(#000, 0.1);
|
||||
box-sizing: border-box;
|
||||
padding: 24rpx 30rpx;
|
||||
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.04);
|
||||
border-radius: 0 0 20rpx 20rpx;
|
||||
|
||||
.desc-title {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.price-text {
|
||||
color: #ff0000;
|
||||
}
|
||||
</style>
|
||||
@@ -1,149 +0,0 @@
|
||||
<!-- 订单确认的优惠劵选择弹窗 -->
|
||||
<template>
|
||||
<su-popup
|
||||
:show="show"
|
||||
type="bottom"
|
||||
round="20"
|
||||
@close="emits('close')"
|
||||
showClose
|
||||
backgroundColor="#f2f2f2"
|
||||
>
|
||||
<view class="model-box">
|
||||
<view class="title ss-m-t-16 ss-m-l-20 ss-flex">优惠券</view>
|
||||
<scroll-view
|
||||
class="model-content"
|
||||
scroll-y
|
||||
:scroll-with-animation="false"
|
||||
:enable-back-to-top="true"
|
||||
>
|
||||
<!--可使用的优惠券区域-->
|
||||
<view class="subtitle ss-m-l-20">可使用优惠券</view>
|
||||
<view
|
||||
v-for="(item, index) in state.couponInfo.filter((coupon) => coupon.match)"
|
||||
:key="index"
|
||||
>
|
||||
<s-coupon-list :data="item" type="user" :disabled="false">
|
||||
<template #default>
|
||||
<label class="ss-flex ss-col-center" @tap="radioChange(item.id)">
|
||||
<radio
|
||||
color="var(--ui-BG-Main)"
|
||||
style="transform: scale(0.8)"
|
||||
:checked="state.couponId === item.id"
|
||||
@tap.stop="radioChange(item.id)"
|
||||
/>
|
||||
</label>
|
||||
</template>
|
||||
</s-coupon-list>
|
||||
</view>
|
||||
<!--不可使用的优惠券区域-->
|
||||
<view class="subtitle ss-m-t-40 ss-m-l-20">不可使用优惠券</view>
|
||||
<view v-for="item in state.couponInfo.filter((coupon) => !coupon.match)" :key="item.id">
|
||||
<s-coupon-list :data="item" type="user" :disabled="true">
|
||||
<template v-slot:reason>
|
||||
<view class="ss-flex ss-m-t-24">
|
||||
<view class="reason-title"> 不可用原因:</view>
|
||||
<view class="reason-desc">{{ item.mismatchReason || '未达到使用门槛' }}</view>
|
||||
</view>
|
||||
</template>
|
||||
</s-coupon-list>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view class="modal-footer ss-flex">
|
||||
<button class="confirm-btn ss-reset-button" @tap="onConfirm">确认</button>
|
||||
</view>
|
||||
</su-popup>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, reactive } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
// 优惠劵列表
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['confirm', 'close']);
|
||||
|
||||
const state = reactive({
|
||||
couponInfo: computed(() => props.modelValue), // 优惠劵列表
|
||||
couponId: undefined, // 选中的优惠劵编号
|
||||
});
|
||||
|
||||
// 选中优惠劵
|
||||
function radioChange(couponId) {
|
||||
if (state.couponId === couponId) {
|
||||
state.couponId = undefined;
|
||||
} else {
|
||||
state.couponId = couponId;
|
||||
}
|
||||
}
|
||||
|
||||
// 确认优惠劵
|
||||
const onConfirm = () => {
|
||||
emits('confirm', state.couponId);
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
:deep() {
|
||||
.uni-checkbox-input {
|
||||
background-color: var(--ui-BG-Main);
|
||||
}
|
||||
}
|
||||
|
||||
.model-box {
|
||||
height: 60vh;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
height: 80rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.model-content {
|
||||
height: 54vh;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
width: 100%;
|
||||
height: 120rpx;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
width: 710rpx;
|
||||
margin-left: 20rpx;
|
||||
height: 80rpx;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
border-radius: 40rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.reason-title {
|
||||
font-weight: 600;
|
||||
font-size: 20rpx;
|
||||
line-height: 26rpx;
|
||||
color: #ff0003;
|
||||
}
|
||||
|
||||
.reason-desc {
|
||||
font-weight: 600;
|
||||
font-size: 20rpx;
|
||||
line-height: 26rpx;
|
||||
color: #434343;
|
||||
}
|
||||
</style>
|
||||
@@ -1,67 +0,0 @@
|
||||
<!-- 顶部导航栏 - 单元格 -->
|
||||
<template>
|
||||
<view class="ss-flex ss-col-center">
|
||||
<!-- 类型一: 文字 -->
|
||||
<view
|
||||
v-if="data.type === 'text'"
|
||||
class="nav-title inline"
|
||||
:style="[{ color: data.textColor, width: width }]"
|
||||
@tap="sheep.$router.go(data.url)"
|
||||
>
|
||||
{{ data.text }}
|
||||
</view>
|
||||
<!-- 类型二: 图片 -->
|
||||
<view
|
||||
v-if="data.type === 'image'"
|
||||
:style="[{ width: width }]"
|
||||
class="menu-icon-wrap ss-flex ss-row-center ss-col-center"
|
||||
@tap="sheep.$router.go(data.url)"
|
||||
>
|
||||
<image class="nav-image" :src="sheep.$url.cdn(data.imgUrl)" mode="aspectFit"></image>
|
||||
</view>
|
||||
<!-- 类型三: 搜索框 -->
|
||||
<view class="ss-flex-1" v-if="data.type === 'search'" :style="[{ width: width }]">
|
||||
<s-search-block
|
||||
:placeholder="data.placeholder || '搜索关键字'"
|
||||
:radius="data.borderRadius"
|
||||
elBackground="#fff"
|
||||
:height="height"
|
||||
:width="width"
|
||||
@click="sheep.$router.go('/pages/index/search')"
|
||||
></s-search-block>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { computed } from 'vue';
|
||||
|
||||
// 接收参数
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '1px',
|
||||
},
|
||||
});
|
||||
|
||||
const height = computed(() => sheep.$platform.capsule.height);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.menu-icon-wrap {
|
||||
.nav-image {
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,314 +0,0 @@
|
||||
<template>
|
||||
<su-fixed
|
||||
:noFixed="props.noFixed"
|
||||
:alway="props.alway"
|
||||
:bgStyles="props.bgStyles"
|
||||
:val="0"
|
||||
:index="props.zIndex"
|
||||
noNav
|
||||
:bg="props.bg"
|
||||
:ui="props.ui"
|
||||
:opacity="props.opacity"
|
||||
:placeholder="props.placeholder"
|
||||
:sticky="props.sticky"
|
||||
>
|
||||
<su-status-bar />
|
||||
<!--
|
||||
:class="[{ 'border-bottom': !props.opacity && props.bg != 'bg-none' }]"
|
||||
-->
|
||||
<view class="ui-navbar-box">
|
||||
<view
|
||||
class="ui-bar"
|
||||
:class="
|
||||
props.status == '' ? `text-a` : props.status == 'light' ? 'text-white' : 'text-black'
|
||||
"
|
||||
:style="[{ height: sys_navBar - sys_statusBar + 'px' }]"
|
||||
>
|
||||
<slot name="item"></slot>
|
||||
<view class="right">
|
||||
<!-- #ifdef MP -->
|
||||
<view :style="[state.capsuleStyle]"></view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</su-fixed>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 标题栏 - 基础组件navbar
|
||||
*
|
||||
* @param {Number} zIndex = 100 - 层级
|
||||
* @param {Boolean} back = true - 是否返回上一页
|
||||
* @param {String} backtext = '' - 返回文本
|
||||
* @param {String} bg = 'bg-white' - 公共Class
|
||||
* @param {String} status = '' - 状态栏颜色
|
||||
* @param {Boolean} alway = true - 是否常驻
|
||||
* @param {Boolean} opacity = false - 是否开启透明渐变
|
||||
* @param {Boolean} opacityBg = false - 开启滑动渐变后,返回按钮是否添加背景
|
||||
* @param {Boolean} noFixed = false - 是否浮动
|
||||
* @param {String} ui = '' - 公共Class
|
||||
* @param {Boolean} capsule = false - 是否开启胶囊返回
|
||||
* @param {Boolean} stopBack = false - 是否禁用返回
|
||||
* @param {Boolean} placeholder = true - 是否开启占位
|
||||
* @param {Object} bgStyles = {} - 背景样式
|
||||
*
|
||||
*/
|
||||
|
||||
import { computed, reactive, onBeforeMount } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
// 本地数据
|
||||
const state = reactive({
|
||||
statusCur: '',
|
||||
capsuleStyle: {},
|
||||
capsuleBack: {},
|
||||
});
|
||||
|
||||
const sys_statusBar = sheep.$platform.device.statusBarHeight;
|
||||
const sys_navBar = sheep.$platform.navbar;
|
||||
|
||||
const props = defineProps({
|
||||
sticky: Boolean,
|
||||
zIndex: {
|
||||
type: Number,
|
||||
default: 100,
|
||||
},
|
||||
back: {
|
||||
//是否返回上一页
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
backtext: {
|
||||
//返回文本
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
bg: {
|
||||
type: String,
|
||||
default: 'bg-white',
|
||||
},
|
||||
status: {
|
||||
//状态栏颜色 可以选择light dark/其他字符串视为黑色
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// 常驻
|
||||
alway: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
opacity: {
|
||||
//是否开启滑动渐变
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
opacityBg: {
|
||||
//开启滑动渐变后 返回按钮是否添加背景
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
noFixed: {
|
||||
//是否浮动
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
ui: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
capsule: {
|
||||
//是否开启胶囊返回
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
stopBack: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
placeholder: {
|
||||
type: [Boolean],
|
||||
default: true,
|
||||
},
|
||||
bgStyles: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['navback']);
|
||||
|
||||
onBeforeMount(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
// 返回
|
||||
const onNavback = () => {
|
||||
sheep.$router.back();
|
||||
};
|
||||
|
||||
// 初始化
|
||||
const init = () => {
|
||||
state.capsuleStyle = {
|
||||
width: sheep.$platform.capsule.width + 'px',
|
||||
height: sheep.$platform.capsule.height + 'px',
|
||||
margin: '0 ' + (sheep.$platform.device.windowWidth - sheep.$platform.capsule.right) + 'px',
|
||||
};
|
||||
|
||||
state.capsuleBack = state.capsuleStyle;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ui-navbar-box {
|
||||
background-color: transparent;
|
||||
width: 100%;
|
||||
|
||||
.ui-bar {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.left {
|
||||
@include flex-bar;
|
||||
|
||||
.back {
|
||||
@include flex-bar;
|
||||
|
||||
.back-icon {
|
||||
@include flex-center;
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
margin: 0 10rpx;
|
||||
font-size: 46rpx !important;
|
||||
|
||||
&.opacityIcon {
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
background-color: rgba(127, 127, 127, 0.5);
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 200%;
|
||||
width: 200%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
border-radius: inherit;
|
||||
transform: scale(0.5);
|
||||
transform-origin: 0 0;
|
||||
opacity: 0.1;
|
||||
border: 1px solid currentColor;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&::before {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* #ifdef MP-ALIPAY */
|
||||
._icon-back {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.capsule {
|
||||
@include flex-bar;
|
||||
border-radius: 100px;
|
||||
position: relative;
|
||||
|
||||
&.dark {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
&.light {
|
||||
background-color: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 60%;
|
||||
width: 1px;
|
||||
left: 50%;
|
||||
top: 20%;
|
||||
background-color: currentColor;
|
||||
opacity: 0.1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 200%;
|
||||
width: 200%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
border-radius: inherit;
|
||||
transform: scale(0.5);
|
||||
transform-origin: 0 0;
|
||||
opacity: 0.1;
|
||||
border: 1px solid currentColor;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.capsule-back,
|
||||
.capsule-home {
|
||||
@include flex-center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&.isFristPage {
|
||||
.capsule-back,
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
@include flex-bar;
|
||||
|
||||
.right-content {
|
||||
@include flex;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
@include flex-center;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
height: 36px;
|
||||
max-width: calc(100vw - 200px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-bar-bg {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,207 +0,0 @@
|
||||
<!-- 顶部导航栏 -->
|
||||
<template>
|
||||
<navbar
|
||||
:alway="isAlways"
|
||||
:back="false"
|
||||
bg=""
|
||||
:placeholder="isPlaceholder"
|
||||
:bgStyles="bgStyles"
|
||||
:opacity="isOpacity"
|
||||
:sticky="sticky"
|
||||
>
|
||||
<template #item>
|
||||
<view class="nav-box">
|
||||
<view class="nav-icon" v-if="showLeftButton">
|
||||
<view class="icon-box ss-flex" :class="{ 'inner-icon-box': data.styleType === 'inner' }">
|
||||
<view class="icon-button icon-button-left ss-flex ss-row-center" @tap="onClickLeft">
|
||||
<text class="sicon-back" v-if="hasHistory" />
|
||||
<text class="sicon-home" v-else />
|
||||
</view>
|
||||
<view class="line"></view>
|
||||
<view class="icon-button icon-button-right ss-flex ss-row-center" @tap="onClickRight">
|
||||
<text class="sicon-more" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="nav-item"
|
||||
v-for="(item, index) in navList"
|
||||
:key="index"
|
||||
:style="[parseImgStyle(item)]"
|
||||
:class="[{ 'ss-flex ss-col-center ss-row-center': item.type !== 'search' }]"
|
||||
>
|
||||
<navbar-item :data="item" :width="parseImgStyle(item).width" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</navbar>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 装修组件 - 自定义标题栏
|
||||
*
|
||||
*
|
||||
* @property {Number | String} alwaysShow = [0,1] - 是否常驻
|
||||
* @property {Number | String} styleType = [inner] - 是否沉浸式
|
||||
* @property {String | Number} type - 标题背景模式
|
||||
* @property {String} color - 页面背景色
|
||||
* @property {String} src - 页面背景图片
|
||||
*/
|
||||
import { computed, unref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import Navbar from './components/navbar.vue';
|
||||
import NavbarItem from './components/navbar-item.vue';
|
||||
import { showMenuTools } from '@/sheep/hooks/useModal';
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
showLeftButton: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const hasHistory = sheep.$router.hasHistory();
|
||||
const sticky = computed(() => {
|
||||
if (props.data.styleType === 'inner') {
|
||||
if (props.data.alwaysShow) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (props.data.styleType === 'normal') {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
const navList = computed(() => {
|
||||
// #ifdef MP
|
||||
return props.data.mpCells || [];
|
||||
// #endif
|
||||
return props.data.otherCells || [];
|
||||
});
|
||||
// 页面宽度
|
||||
const windowWidth = sheep.$platform.device.windowWidth;
|
||||
// 单元格宽度
|
||||
const cell = computed(() => {
|
||||
if (unref(navList).length) {
|
||||
// 默认宽度为8个格子,微信公众号右上角有胶囊按钮所以是6个格子
|
||||
let cell = (windowWidth - 90) / 8;
|
||||
// #ifdef MP
|
||||
cell = (windowWidth - 80 - unref(sheep.$platform.capsule).width) / 6;
|
||||
// #endif
|
||||
return cell;
|
||||
}
|
||||
});
|
||||
// 解析位置
|
||||
const parseImgStyle = (item) => {
|
||||
let obj = {
|
||||
width: item.width * cell.value + (item.width - 1) * 10 + 'px',
|
||||
left: item.left * cell.value + (item.left + 1) * 10 + 'px',
|
||||
'border-radius': item.borderRadius + 'px',
|
||||
};
|
||||
return obj;
|
||||
};
|
||||
const isAlways = computed(() =>
|
||||
props.data.styleType === 'inner' ? Boolean(props.data.alwaysShow) : true,
|
||||
);
|
||||
const isOpacity = computed(() =>
|
||||
props.data.styleType === 'normal'
|
||||
? false
|
||||
: props.showLeftButton
|
||||
? false
|
||||
: props.data.styleType === 'inner',
|
||||
);
|
||||
const isPlaceholder = computed(() => props.data.styleType === 'normal');
|
||||
const bgStyles = computed(() => {
|
||||
return {
|
||||
background:
|
||||
props.data.bgType === 'img' && props.data.bgImg
|
||||
? `url(${sheep.$url.cdn(props.data.bgImg)}) no-repeat top center / 100% 100%`
|
||||
: props.data.bgColor,
|
||||
};
|
||||
});
|
||||
|
||||
// 左侧按钮:返回上一页或首页
|
||||
function onClickLeft() {
|
||||
if (hasHistory) {
|
||||
sheep.$router.back();
|
||||
} else {
|
||||
sheep.$router.go('/pages/index/index');
|
||||
}
|
||||
}
|
||||
|
||||
// 右侧按钮:打开快捷菜单
|
||||
function onClickRight() {
|
||||
showMenuTools();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.nav-box {
|
||||
width: 750rpx;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
.nav-item {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
left: 20rpx;
|
||||
|
||||
.inner-icon-box {
|
||||
border: 1px solid rgba(#fff, 0.4);
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
.icon-box {
|
||||
background: #ffffff;
|
||||
box-shadow: 0px 0px 4rpx rgba(51, 51, 51, 0.08), 0px 4rpx 6rpx 2rpx rgba(102, 102, 102, 0.12);
|
||||
border-radius: 30rpx;
|
||||
width: 134rpx;
|
||||
height: 56rpx;
|
||||
margin-left: 8rpx;
|
||||
|
||||
.line {
|
||||
width: 2rpx;
|
||||
height: 24rpx;
|
||||
background: #e5e5e7;
|
||||
}
|
||||
|
||||
.sicon-back {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.sicon-home {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.sicon-more {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
width: 67rpx;
|
||||
height: 56rpx;
|
||||
|
||||
&-left:hover {
|
||||
background: rgba(0, 0, 0, 0.16);
|
||||
border-radius: 30rpx 0px 0px 30rpx;
|
||||
}
|
||||
|
||||
&-right:hover {
|
||||
background: rgba(0, 0, 0, 0.16);
|
||||
border-radius: 0px 30rpx 30rpx 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,96 +0,0 @@
|
||||
<template>
|
||||
<su-popup
|
||||
:show="show"
|
||||
type="bottom"
|
||||
round="20"
|
||||
@close="emits('close')"
|
||||
showClose
|
||||
backgroundColor="#f2f2f2"
|
||||
>
|
||||
<view class="model-box">
|
||||
<view class="title ss-m-t-38 ss-m-l-20 ss-m-b-40">活动优惠</view>
|
||||
<scroll-view
|
||||
class="model-content ss-m-l-20"
|
||||
scroll-y
|
||||
:scroll-with-animation="false"
|
||||
:enable-back-to-top="true"
|
||||
>
|
||||
<view v-for="(item, index) in state.orderInfo.promotions" :key="index">
|
||||
<!-- 不展示积分、优惠劵、会员折扣,因为它们已经单独展示了 -->
|
||||
<view class="ss-flex ss-m-b-40 subtitle" v-if="[1, 2, 3, 4, 5].includes(item.type)">
|
||||
<view> {{ item.description }} </view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view class="modal-footer ss-flex">
|
||||
<button class="confirm-btn ss-reset-button" @tap="emits('close')">确认</button>
|
||||
</view>
|
||||
</su-popup>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, reactive } from 'vue';
|
||||
const props = defineProps({
|
||||
promoInfo: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
goodsList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(['close']);
|
||||
const state = reactive({
|
||||
orderInfo: computed(() => props.modelValue),
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.model-box {
|
||||
height: 60vh;
|
||||
}
|
||||
|
||||
.model-content {
|
||||
height: 54vh;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
width: 100%;
|
||||
height: 120rpx;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
width: 710rpx;
|
||||
margin-left: 20rpx;
|
||||
height: 80rpx;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
border-radius: 40rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.content-img {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
margin-right: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.price-text {
|
||||
color: #ff3000;
|
||||
}
|
||||
</style>
|
||||
@@ -1,93 +0,0 @@
|
||||
<template>
|
||||
<view
|
||||
class="ss-flex-col ss-col-center ss-row-center empty-box"
|
||||
:style="[{ paddingTop: paddingTop + 'rpx' }]"
|
||||
>
|
||||
<view class=""><image class="empty-icon" :src="icon" mode="widthFix"></image></view>
|
||||
<view class="empty-text ss-m-t-28 ss-m-b-40">
|
||||
<text v-if="text !== ''">{{ text }}</text>
|
||||
</view>
|
||||
<button class="ss-reset-button empty-btn" v-if="showAction" @tap="clickAction">
|
||||
{{ actionText }}
|
||||
</button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
/**
|
||||
* 容器组件 - 装修组件的样式容器
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
// 图标
|
||||
icon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// 描述
|
||||
text: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// 是否显示button
|
||||
showAction: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// button 文字
|
||||
actionText: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// 链接
|
||||
actionUrl: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// 间距
|
||||
paddingTop: {
|
||||
type: String,
|
||||
default: '260',
|
||||
},
|
||||
//主题色
|
||||
buttonColor: {
|
||||
type: String,
|
||||
default: 'var(--ui-BG-Main)',
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['clickAction']);
|
||||
|
||||
function clickAction() {
|
||||
if (props.actionUrl !== '') {
|
||||
sheep.$router.go(props.actionUrl);
|
||||
}
|
||||
emits('clickAction');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.empty-box {
|
||||
width: 100%;
|
||||
}
|
||||
.empty-icon {
|
||||
width: 240rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.empty-btn {
|
||||
width: 320rpx;
|
||||
height: 70rpx;
|
||||
border: 2rpx solid v-bind('buttonColor');
|
||||
border-radius: 35rpx;
|
||||
font-weight: 500;
|
||||
color: v-bind('buttonColor');
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -1,88 +0,0 @@
|
||||
<!-- 装修基础组件:悬浮按钮 -->
|
||||
<template>
|
||||
<!-- 模态背景:展开时显示,点击后折叠 -->
|
||||
<view class="modal-bg" v-if="fabRef?.isShow" @click="handleCollapseFab"></view>
|
||||
<!-- 悬浮按钮 -->
|
||||
<uni-fab
|
||||
ref="fabRef"
|
||||
horizontal="right"
|
||||
vertical="bottom"
|
||||
:direction="state.direction"
|
||||
:pattern="state.pattern"
|
||||
:content="state.content"
|
||||
@trigger="handleOpenLink"
|
||||
/>
|
||||
</template>
|
||||
<script setup>
|
||||
/**
|
||||
* 悬浮按钮
|
||||
*/
|
||||
|
||||
import sheep from '@/sheep';
|
||||
import { reactive, ref, unref } from 'vue';
|
||||
import { onBackPress } from '@dcloudio/uni-app';
|
||||
|
||||
// 定义属性
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default() {},
|
||||
}
|
||||
})
|
||||
|
||||
// 悬浮按钮配置: https://uniapp.dcloud.net.cn/component/uniui/uni-fab.html#fab-props
|
||||
const state = reactive({
|
||||
// 可选样式配置项
|
||||
pattern: [],
|
||||
// 展开菜单内容配置项
|
||||
content: [],
|
||||
// 展开菜单显示方式:horizontal-水平显示,vertical-垂直显示
|
||||
direction: '',
|
||||
});
|
||||
|
||||
// 悬浮按钮引用
|
||||
const fabRef = ref(null);
|
||||
// 按钮方向
|
||||
state.direction = props.data.direction;
|
||||
props.data?.list.forEach((item) => {
|
||||
// 按钮文字
|
||||
const text = props.data?.showText ? item.text : ''
|
||||
// 生成内容配置项
|
||||
state.content.push({ iconPath: sheep.$url.cdn(item.imgUrl), url: item.url, text });
|
||||
// 生成样式配置项
|
||||
state.pattern.push({ color: item.textColor });
|
||||
});
|
||||
|
||||
// 处理链接跳转
|
||||
function handleOpenLink(e) {
|
||||
sheep.$router.go(e.item.url);
|
||||
}
|
||||
|
||||
// 折叠
|
||||
function handleCollapseFab() {
|
||||
if (unref(fabRef)?.isShow) {
|
||||
unref(fabRef)?.close();
|
||||
}
|
||||
}
|
||||
|
||||
// 按返回值后,折叠悬浮按钮
|
||||
onBackPress(() => {
|
||||
if (unref(fabRef)?.isShow) {
|
||||
unref(fabRef)?.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
/* 模态背景 */
|
||||
.modal-bg {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 11;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(#000000, 0.4);
|
||||
}
|
||||
</style>
|
||||
@@ -1,306 +0,0 @@
|
||||
<!-- 装修商品组件:商品卡片 -->
|
||||
<template>
|
||||
<!-- 商品卡片 -->
|
||||
<view>
|
||||
<!-- 布局1. 单列大图(上图,下内容)-->
|
||||
<view
|
||||
v-if="layoutType === LayoutTypeEnum.ONE_COL_BIG_IMG && state.goodsList.length"
|
||||
class="goods-sl-box"
|
||||
>
|
||||
<view
|
||||
class="goods-box"
|
||||
v-for="item in state.goodsList"
|
||||
:key="item.id"
|
||||
:style="[{ marginBottom: data.space * 2 + 'rpx' }]"
|
||||
>
|
||||
<s-goods-column
|
||||
class=""
|
||||
size="sl"
|
||||
:goodsFields="data.fields"
|
||||
:tagStyle="data.badge"
|
||||
:data="item"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:subTitleColor="data.fields.introduction.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 布局2. 双列(每一列:上图,下内容)-->
|
||||
<view
|
||||
v-if="layoutType === LayoutTypeEnum.TWO_COL && state.goodsList.length"
|
||||
class="goods-md-wrap ss-flex ss-flex-wrap ss-col-top"
|
||||
>
|
||||
<view class="goods-list-box">
|
||||
<view
|
||||
class="left-list"
|
||||
:style="[{ paddingRight: data.space + 'rpx', marginBottom: data.space + 'px' }]"
|
||||
v-for="item in state.leftGoodsList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-md-box"
|
||||
size="md"
|
||||
:goodsFields="data.fields"
|
||||
:tagStyle="data.badge"
|
||||
:data="item"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:subTitleColor="data.fields.introduction.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
:titleWidth="330 - marginLeft - marginRight"
|
||||
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
@getHeight="calculateGoodsColumn($event, 'left')"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
<view class="goods-list-box">
|
||||
<view
|
||||
class="right-list"
|
||||
:style="[{ paddingLeft: data.space + 'rpx', marginBottom: data.space + 'px' }]"
|
||||
v-for="item in state.rightGoodsList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-md-box"
|
||||
size="md"
|
||||
:goodsFields="data.fields"
|
||||
:tagStyle="data.badge"
|
||||
:data="item"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:subTitleColor="data.fields.introduction.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
:titleWidth="330 - marginLeft - marginRight"
|
||||
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
@getHeight="calculateGoodsColumn($event, 'right')"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 布局3. 单列小图(左图,右内容) -->
|
||||
<view
|
||||
v-if="layoutType === LayoutTypeEnum.ONE_COL_SMALL_IMG && state.goodsList.length"
|
||||
class="goods-lg-box"
|
||||
>
|
||||
<view
|
||||
class="goods-box"
|
||||
:style="[{ marginBottom: data.space + 'px' }]"
|
||||
v-for="item in state.goodsList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-card"
|
||||
size="lg"
|
||||
:goodsFields="data.fields"
|
||||
:data="item"
|
||||
:tagStyle="data.badge"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:subTitleColor="data.fields.introduction.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
@tap="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 商品卡片
|
||||
*/
|
||||
import { computed, reactive, onMounted } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import SpuApi from '@/sheep/api/product/spu';
|
||||
import OrderApi from '@/sheep/api/trade/order';
|
||||
import { appendSettlementProduct } from '@/sheep/hooks/useGoods';
|
||||
|
||||
// 布局类型
|
||||
const LayoutTypeEnum = {
|
||||
// 单列大图
|
||||
ONE_COL_BIG_IMG: 'oneColBigImg',
|
||||
// 双列
|
||||
TWO_COL: 'twoCol',
|
||||
// 单列小图
|
||||
ONE_COL_SMALL_IMG: 'oneColSmallImg',
|
||||
};
|
||||
|
||||
const state = reactive({
|
||||
goodsList: [],
|
||||
leftGoodsList: [],
|
||||
rightGoodsList: [],
|
||||
});
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
const { layoutType, btnBuy, spuIds } = props.data || {};
|
||||
const { marginLeft, marginRight } = props.styles || {};
|
||||
|
||||
// 购买按钮样式
|
||||
const buyStyle = computed(() => {
|
||||
if (btnBuy.type === 'text') {
|
||||
// 文字按钮:线性渐变背景颜色
|
||||
return {
|
||||
background: `linear-gradient(to right, ${btnBuy.bgBeginColor}, ${btnBuy.bgEndColor})`,
|
||||
};
|
||||
}
|
||||
if (btnBuy.type === 'img') {
|
||||
// 图片按钮
|
||||
return {
|
||||
width: '54rpx',
|
||||
height: '54rpx',
|
||||
background: `url(${sheep.$url.cdn(btnBuy.imgUrl)}) no-repeat`,
|
||||
backgroundSize: '100% 100%',
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
//region 商品瀑布流布局
|
||||
// 下一个要处理的商品索引
|
||||
let count = 0;
|
||||
// 左列的高度
|
||||
let leftHeight = 0;
|
||||
// 右列的高度
|
||||
let rightHeight = 0;
|
||||
|
||||
/**
|
||||
* 计算商品在左列还是右列
|
||||
* @param height 商品的高度
|
||||
* @param where 添加到哪一列
|
||||
*/
|
||||
function calculateGoodsColumn(height = 0, where = 'left') {
|
||||
// 处理完
|
||||
if (!state.goodsList[count]) return;
|
||||
// 增加列的高度
|
||||
if (where === 'left') leftHeight += height;
|
||||
if (where === 'right') rightHeight += height;
|
||||
// 添加到矮的一列
|
||||
if (leftHeight <= rightHeight) {
|
||||
state.leftGoodsList.push(state.goodsList[count]);
|
||||
} else {
|
||||
state.rightGoodsList.push(state.goodsList[count]);
|
||||
}
|
||||
// 计数
|
||||
count++;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
/**
|
||||
* 根据商品编号列表,获取商品列表
|
||||
* @param ids 商品编号列表
|
||||
* @return {Promise<undefined>} 商品列表
|
||||
*/
|
||||
async function getGoodsListByIds(ids) {
|
||||
const { data } = await SpuApi.getSpuListByIds(ids);
|
||||
return data;
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
// 加载商品列表
|
||||
state.goodsList = await getGoodsListByIds(spuIds.join(','));
|
||||
// 拼接结算信息(营销)
|
||||
await OrderApi.getSettlementProduct(state.goodsList.map((item) => item.id).join(',')).then(
|
||||
(res) => {
|
||||
if (res.code !== 0) {
|
||||
return;
|
||||
}
|
||||
appendSettlementProduct(state.goodsList, res.data);
|
||||
},
|
||||
);
|
||||
// 只有双列布局时需要
|
||||
if (layoutType === LayoutTypeEnum.TWO_COL) {
|
||||
// 分列
|
||||
calculateGoodsColumn();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-md-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.goods-list-box {
|
||||
width: 50%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.left-list {
|
||||
&:nth-last-child(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.right-list {
|
||||
&:nth-last-child(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.goods-box {
|
||||
&:nth-last-of-type(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-md-box,
|
||||
.goods-sl-box,
|
||||
.goods-lg-box {
|
||||
position: relative;
|
||||
|
||||
.cart-btn {
|
||||
position: absolute;
|
||||
bottom: 18rpx;
|
||||
right: 20rpx;
|
||||
z-index: 11;
|
||||
height: 50rpx;
|
||||
line-height: 50rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 25rpx;
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,958 +0,0 @@
|
||||
<!-- 页面 -->
|
||||
<template>
|
||||
<view class="ss-goods-wrap">
|
||||
<!-- xs卡片:横向紧凑型,一行放两个,图片左内容右边 -->
|
||||
<view
|
||||
v-if="size === 'xs'"
|
||||
class="xs-goods-card ss-flex ss-col-stretch"
|
||||
:style="[elStyles]"
|
||||
@tap="onClick"
|
||||
>
|
||||
<view v-if="tagStyle.show" class="tag-icon-box">
|
||||
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
|
||||
</view>
|
||||
<image class="xs-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFit" />
|
||||
<view
|
||||
v-if="goodsFields.title?.show || goodsFields.name?.show || goodsFields.price?.show"
|
||||
class="xs-goods-content ss-flex-col ss-row-around"
|
||||
>
|
||||
<view
|
||||
v-if="goodsFields.title?.show || goodsFields.name?.show"
|
||||
class="xs-goods-title ss-line-1"
|
||||
:style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]"
|
||||
>
|
||||
{{ data.title || data.name }}
|
||||
</view>
|
||||
<!-- 活动信息 -->
|
||||
<view class="iconBox" v-if="data.promotionType > 0 || data.rewardActivity">
|
||||
<view class="card" v-if="discountText">{{ discountText }}</view>
|
||||
<view
|
||||
class="card2"
|
||||
v-for="item in getRewardActivityRuleItemDescriptions(data.rewardActivity).slice(0, 1)"
|
||||
:key="item"
|
||||
>
|
||||
{{ item }}
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
v-if="goodsFields.price?.show"
|
||||
class="xs-goods-price font-OPPOSANS"
|
||||
:style="[{ color: goodsFields.price.color }]"
|
||||
>
|
||||
<!-- 活动价格 -->
|
||||
<view class="ss-flex" v-if="data.activityType && data.activityType === PromotionActivityTypeEnum.POINT.type">
|
||||
<image
|
||||
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
|
||||
class="point-img"
|
||||
></image>
|
||||
<text class="point-text ss-m-r-16">
|
||||
{{ data.point }}
|
||||
{{ !data.pointPrice || data.pointPrice === 0 ? '' : `+${priceUnit}${fen2yuan(data.pointPrice)}` }}
|
||||
</text>
|
||||
</view>
|
||||
<template v-else>
|
||||
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
|
||||
<text v-if="data.promotionPrice > 0">{{ fen2yuan(data.promotionPrice) }}</text>
|
||||
<text v-else>
|
||||
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
||||
</text>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- sm卡片:竖向紧凑,一行放三个,图上内容下 -->
|
||||
<view v-if="size === 'sm'" class="sm-goods-card ss-flex-col" :style="[elStyles]" @tap="onClick">
|
||||
<view v-if="tagStyle.show" class="tag-icon-box">
|
||||
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
|
||||
</view>
|
||||
<image
|
||||
class="sm-img-box"
|
||||
:src="sheep.$url.cdn(data.image || data.picUrl)"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
|
||||
<view
|
||||
v-if="goodsFields.title?.show || goodsFields.name?.show || goodsFields.price?.show"
|
||||
class="sm-goods-content"
|
||||
:style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]"
|
||||
>
|
||||
<view
|
||||
v-if="goodsFields.title?.show || goodsFields.name?.show"
|
||||
class="sm-goods-title ss-line-1 ss-m-b-16"
|
||||
>
|
||||
{{ data.title || data.name }}
|
||||
</view>
|
||||
<!-- 活动信息 -->
|
||||
<view class="iconBox" v-if="data.promotionType > 0 || data.rewardActivity">
|
||||
<view class="card" v-if="discountText">{{ discountText }}</view>
|
||||
<view
|
||||
class="card2"
|
||||
v-for="item in getRewardActivityRuleItemDescriptions(data.rewardActivity).slice(0, 1)"
|
||||
:key="item"
|
||||
>
|
||||
{{ item }}
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
v-if="goodsFields.price?.show"
|
||||
class="sm-goods-price font-OPPOSANS"
|
||||
:style="[{ color: goodsFields.price.color }]"
|
||||
>
|
||||
<!-- 活动价格 -->
|
||||
<view class="ss-flex" v-if="data.activityType && data.activityType === PromotionActivityTypeEnum.POINT.type">
|
||||
<image
|
||||
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
|
||||
class="point-img"
|
||||
></image>
|
||||
<text class="point-text ss-m-r-16">
|
||||
{{ data.point }}
|
||||
{{ !data.pointPrice || data.pointPrice === 0 ? '' : `+${priceUnit}${fen2yuan(data.pointPrice)}` }}
|
||||
</text>
|
||||
</view>
|
||||
<template v-else>
|
||||
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
|
||||
<text v-if="data.promotionPrice > 0">{{ fen2yuan(data.promotionPrice) }}</text>
|
||||
<text v-else>
|
||||
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
||||
</text>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- md卡片:竖向,一行放两个,图上内容下 -->
|
||||
<view v-if="size === 'md'" class="md-goods-card ss-flex-col" :style="[elStyles]" @tap="onClick">
|
||||
<view v-if="tagStyle.show" class="tag-icon-box">
|
||||
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)" />
|
||||
</view>
|
||||
<image class="md-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="widthFix" />
|
||||
<view
|
||||
class="md-goods-content ss-flex-col ss-row-around ss-p-b-20 ss-p-t-20 ss-p-x-16"
|
||||
:id="elId"
|
||||
>
|
||||
<view
|
||||
v-if="goodsFields.title?.show || goodsFields.name?.show"
|
||||
class="md-goods-title ss-line-1"
|
||||
:style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]"
|
||||
>
|
||||
{{ data.title || data.name }}
|
||||
</view>
|
||||
<view
|
||||
v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show"
|
||||
class="md-goods-subtitle ss-m-t-16 ss-line-1"
|
||||
:style="[{ color: subTitleColor, background: subTitleBackground }]"
|
||||
>
|
||||
{{ data.subtitle || data.introduction }}
|
||||
</view>
|
||||
<slot name="activity">
|
||||
<view v-if="data.promos?.length" class="tag-box ss-flex-wrap ss-flex ss-col-center">
|
||||
<view
|
||||
class="activity-tag ss-m-r-10 ss-m-t-16"
|
||||
v-for="item in data.promos"
|
||||
:key="item.id"
|
||||
>
|
||||
{{ item.title }}
|
||||
</view>
|
||||
</view>
|
||||
</slot>
|
||||
<!-- 活动信息 -->
|
||||
<view class="iconBox" v-if="data.promotionType > 0 || data.rewardActivity">
|
||||
<view class="card" v-if="discountText">{{ discountText }}</view>
|
||||
<view
|
||||
class="card2"
|
||||
v-for="item in getRewardActivityRuleItemDescriptions(data.rewardActivity).slice(0, 1)"
|
||||
:key="item"
|
||||
>
|
||||
{{ item }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="ss-flex ss-col-bottom">
|
||||
<view
|
||||
v-if="goodsFields.price?.show"
|
||||
class="md-goods-price ss-m-t-16 font-OPPOSANS ss-m-r-10"
|
||||
:style="[{ color: goodsFields.price.color }]"
|
||||
>
|
||||
<!-- 活动价格 -->
|
||||
<view class="ss-flex" v-if="data.activityType && data.activityType === PromotionActivityTypeEnum.POINT.type">
|
||||
<image
|
||||
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
|
||||
class="point-img"
|
||||
></image>
|
||||
<text class="point-text ss-m-r-16">
|
||||
{{ data.point }}
|
||||
{{ !data.pointPrice || data.pointPrice === 0 ? '' : `+${priceUnit}${fen2yuan(data.pointPrice)}` }}
|
||||
</text>
|
||||
</view>
|
||||
<template v-else>
|
||||
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
|
||||
<text v-if="data.promotionPrice > 0">{{ fen2yuan(data.promotionPrice) }}</text>
|
||||
<text v-else>
|
||||
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
||||
</text>
|
||||
</template>
|
||||
</view>
|
||||
<view
|
||||
v-if="
|
||||
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
|
||||
(data.original_price > 0 || data.marketPrice > 0)
|
||||
"
|
||||
class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
|
||||
:style="[{ color: originPriceColor }]"
|
||||
>
|
||||
<text class="price-unit ss-font-20">{{ priceUnit }}</text>
|
||||
<view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="ss-m-t-16 ss-flex ss-col-center ss-flex-wrap">
|
||||
<view class="sales-text">{{ salesAndStock }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<slot name="cart">
|
||||
<view class="cart-box ss-flex ss-col-center ss-row-center">
|
||||
<image class="cart-icon" src="/static/img/shop/tabbar/category2.png" mode="" />
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
|
||||
<!-- lg卡片:横向型,一行放一个,图片左内容右边 -->
|
||||
<view
|
||||
v-if="size === 'lg'"
|
||||
class="lg-goods-card ss-flex ss-col-stretch"
|
||||
:style="[elStyles]"
|
||||
@tap="onClick"
|
||||
>
|
||||
<view v-if="tagStyle.show" class="tag-icon-box">
|
||||
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
|
||||
</view>
|
||||
<view v-if="seckillTag" class="seckill-tag ss-flex ss-row-center">秒杀</view>
|
||||
<view v-if="grouponTag" class="groupon-tag ss-flex ss-row-center">
|
||||
<view class="tag-icon">拼团</view>
|
||||
</view>
|
||||
<image
|
||||
class="lg-img-box"
|
||||
:src="sheep.$url.cdn(data.image || data.picUrl)"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="lg-goods-content ss-flex-1 ss-flex-col ss-row-between ss-p-b-10 ss-p-t-20">
|
||||
<view>
|
||||
<view
|
||||
v-if="goodsFields.title?.show || goodsFields.name?.show"
|
||||
class="lg-goods-title ss-line-2"
|
||||
:style="[{ color: titleColor }]"
|
||||
>
|
||||
{{ data.title || data.name }}
|
||||
</view>
|
||||
<view
|
||||
v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show"
|
||||
class="lg-goods-subtitle ss-m-t-10 ss-line-1"
|
||||
:style="[{ color: subTitleColor, background: subTitleBackground }]"
|
||||
>
|
||||
{{ data.subtitle || data.introduction }}
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<slot name="activity">
|
||||
<view v-if="data.promos?.length" class="tag-box ss-flex ss-col-center">
|
||||
<view class="activity-tag ss-m-r-10" v-for="item in data.promos" :key="item.id">
|
||||
{{ item.title }}
|
||||
</view>
|
||||
</view>
|
||||
</slot>
|
||||
<!-- 活动信息 -->
|
||||
<view class="iconBox" v-if="data.promotionType > 0 || data.rewardActivity">
|
||||
<view class="card" v-if="discountText">{{ discountText }}</view>
|
||||
<view
|
||||
class="card2"
|
||||
v-for="item in getRewardActivityRuleItemDescriptions(data.rewardActivity).slice(0, 1)"
|
||||
:key="item"
|
||||
>
|
||||
{{ item }}
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="goodsFields.price?.show" class="ss-flex ss-col-bottom font-OPPOSANS">
|
||||
<view class="sl-goods-price ss-m-r-12" :style="[{ color: goodsFields.price.color }]">
|
||||
<!-- 活动价格 -->
|
||||
<view class="ss-flex" v-if="data.activityType && data.activityType === PromotionActivityTypeEnum.POINT.type">
|
||||
<image
|
||||
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
|
||||
class="point-img"
|
||||
></image>
|
||||
<text class="point-text ss-m-r-16">
|
||||
{{ data.point }}
|
||||
{{ !data.pointPrice || data.pointPrice === 0 ? '' : `+${priceUnit}${fen2yuan(data.pointPrice)}` }}
|
||||
</text>
|
||||
</view>
|
||||
<template v-else>
|
||||
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
|
||||
<text v-if="data.promotionPrice > 0">{{ fen2yuan(data.promotionPrice) }}</text>
|
||||
<text v-else>
|
||||
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
||||
</text>
|
||||
</template>
|
||||
</view>
|
||||
<view
|
||||
v-if="
|
||||
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
|
||||
(data.original_price > 0 || data.marketPrice > 0)
|
||||
"
|
||||
class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
|
||||
:style="[{ color: originPriceColor }]"
|
||||
>
|
||||
<text class="price-unit ss-font-20">{{ priceUnit }}</text>
|
||||
<view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="ss-m-t-8 ss-flex ss-col-center ss-flex-wrap">
|
||||
<view class="sales-text">{{ salesAndStock }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<slot name="cart">
|
||||
<view class="buy-box ss-flex ss-col-center ss-row-center" v-if="buttonShow"> 去购买</view>
|
||||
</slot>
|
||||
</view>
|
||||
|
||||
<!-- sl卡片:竖向型,一行放一个,图片上内容下边 -->
|
||||
<view v-if="size === 'sl'" class="sl-goods-card ss-flex-col" :style="[elStyles]" @tap="onClick">
|
||||
<view v-if="tagStyle.show" class="tag-icon-box">
|
||||
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)" />
|
||||
</view>
|
||||
<image
|
||||
class="sl-img-box"
|
||||
:src="sheep.$url.cdn(data.image || data.picUrl)"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="sl-goods-content">
|
||||
<view>
|
||||
<view
|
||||
v-if="goodsFields.title?.show || goodsFields.name?.show"
|
||||
class="sl-goods-title ss-line-1"
|
||||
:style="[{ color: titleColor }]"
|
||||
>
|
||||
{{ data.title || data.name }}
|
||||
</view>
|
||||
<view
|
||||
v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show"
|
||||
class="sl-goods-subtitle ss-m-t-16"
|
||||
:style="[{ color: subTitleColor, background: subTitleBackground }]"
|
||||
>
|
||||
{{ data.subtitle || data.introduction }}
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<slot name="activity">
|
||||
<view v-if="data.promos?.length" class="tag-box ss-flex ss-col-center ss-flex-wrap">
|
||||
<view
|
||||
class="activity-tag ss-m-r-10 ss-m-t-16"
|
||||
v-for="item in data.promos"
|
||||
:key="item.id"
|
||||
>
|
||||
{{ item.title }}
|
||||
</view>
|
||||
</view>
|
||||
</slot>
|
||||
<!-- 活动信息 -->
|
||||
<view class="iconBox" v-if="data.promotionType > 0 || data.rewardActivity">
|
||||
<view class="card" v-if="discountText">{{ discountText }}</view>
|
||||
<view
|
||||
class="card2"
|
||||
v-for="item in getRewardActivityRuleItemDescriptions(data.rewardActivity).slice(0, 1)"
|
||||
:key="item"
|
||||
>
|
||||
{{ item }}
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="goodsFields.price?.show" class="ss-flex ss-col-bottom font-OPPOSANS">
|
||||
<view class="sl-goods-price ss-m-r-12" :style="[{ color: goodsFields.price.color }]">
|
||||
<!-- 活动价格 -->
|
||||
<view class="ss-flex" v-if="data.activityType && data.activityType === PromotionActivityTypeEnum.POINT.type">
|
||||
<image
|
||||
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
|
||||
class="point-img"
|
||||
></image>
|
||||
<text class="ss-m-r-16">
|
||||
{{ data.point }}
|
||||
{{ !data.pointPrice || data.pointPrice === 0 ? '' : `+${priceUnit}${fen2yuan(data.pointPrice)}` }}
|
||||
</text>
|
||||
</view>
|
||||
<template v-else>
|
||||
<text class="price-unit ss-font-24">{{ priceUnit }}</text>
|
||||
<text v-if="data.promotionPrice > 0">{{ fen2yuan(data.promotionPrice) }}</text>
|
||||
<text v-else>
|
||||
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
|
||||
</text>
|
||||
</template>
|
||||
</view>
|
||||
<view
|
||||
v-if="
|
||||
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
|
||||
(data.original_price > 0 || data.marketPrice > 0)
|
||||
"
|
||||
class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
|
||||
:style="[{ color: originPriceColor }]"
|
||||
>
|
||||
<text class="price-unit ss-font-20">{{ priceUnit }}</text>
|
||||
<view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="ss-m-t-16 ss-flex ss-flex-wrap">
|
||||
<view class="sales-text">{{ salesAndStock }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<slot name="cart">
|
||||
<view class="buy-box ss-flex ss-col-center ss-row-center">去购买</view>
|
||||
</slot>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 商品卡片
|
||||
*
|
||||
* @property {Array} size = [xs | sm | md | lg | sl ] - 列表数据
|
||||
* @property {String} tag - md及以上才有
|
||||
* @property {String} img - 图片
|
||||
* @property {String} background - 背景色
|
||||
* @property {String} topRadius - 上圆角
|
||||
* @property {String} bottomRadius - 下圆角
|
||||
* @property {String} title - 标题
|
||||
* @property {String} titleColor - 标题颜色
|
||||
* @property {Number} titleWidth = 0 - 标题宽度,默认0,单位rpx
|
||||
* @property {String} subTitle - 副标题
|
||||
* @property {String} subTitleColor - 副标题颜色
|
||||
* @property {String} subTitleBackground - 副标题背景
|
||||
* @property {String | Number} price - 价格
|
||||
* @property {String} priceColor - 价格颜色
|
||||
* @property {String | Number} originPrice - 原价/划线价
|
||||
* @property {String} originPriceColor - 原价颜色
|
||||
* @property {String | Number} sales - 销售数量
|
||||
* @property {String} salesColor - 销售数量颜色
|
||||
*
|
||||
* @slots activity - 活动插槽
|
||||
* @slots cart - 购物车插槽,默认包含文字,背景色,文字颜色 || 图片 || 行为
|
||||
*
|
||||
* @event {Function()} click - 点击卡片
|
||||
*
|
||||
*/
|
||||
import { computed, getCurrentInstance, nextTick, onMounted } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import {
|
||||
fen2yuan,
|
||||
formatExchange,
|
||||
formatSales,
|
||||
formatStock,
|
||||
getRewardActivityRuleItemDescriptions,
|
||||
} from '@/sheep/hooks/useGoods';
|
||||
import { isArray } from 'lodash-es';
|
||||
import { PromotionActivityTypeEnum } from '@/sheep/util/const';
|
||||
|
||||
// 接收参数
|
||||
const props = defineProps({
|
||||
goodsFields: {
|
||||
type: [Array, Object],
|
||||
default() {
|
||||
return {
|
||||
// 商品价格
|
||||
price: {
|
||||
show: true,
|
||||
},
|
||||
// 库存
|
||||
stock: {
|
||||
show: true,
|
||||
},
|
||||
// 商品名称
|
||||
name: {
|
||||
show: true,
|
||||
},
|
||||
// 商品介绍
|
||||
introduction: {
|
||||
show: true,
|
||||
},
|
||||
// 市场价
|
||||
marketPrice: {
|
||||
show: true,
|
||||
},
|
||||
// 销量
|
||||
salesCount: {
|
||||
show: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
tagStyle: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'sl',
|
||||
},
|
||||
background: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
topRadius: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
bottomRadius: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
titleWidth: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
titleColor: {
|
||||
type: String,
|
||||
default: '#333',
|
||||
},
|
||||
priceColor: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
originPriceColor: {
|
||||
type: String,
|
||||
default: '#C4C4C4',
|
||||
},
|
||||
priceUnit: {
|
||||
type: String,
|
||||
default: '¥',
|
||||
},
|
||||
subTitleColor: {
|
||||
type: String,
|
||||
default: '#999999',
|
||||
},
|
||||
subTitleBackground: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
buttonShow: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
seckillTag: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
grouponTag: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
// 优惠文案
|
||||
const discountText = computed(() => {
|
||||
const promotionType = props.data.promotionType;
|
||||
if (promotionType === 4) {
|
||||
return '限时优惠';
|
||||
} else if (promotionType === 6) {
|
||||
return '会员价';
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
// 组件样式
|
||||
const elStyles = computed(() => {
|
||||
return {
|
||||
background: props.background,
|
||||
'border-top-left-radius': props.topRadius + 'px',
|
||||
'border-top-right-radius': props.topRadius + 'px',
|
||||
'border-bottom-left-radius': props.bottomRadius + 'px',
|
||||
'border-bottom-right-radius': props.bottomRadius + 'px',
|
||||
};
|
||||
});
|
||||
|
||||
// 格式化销量、库存信息
|
||||
const salesAndStock = computed(() => {
|
||||
let text = [];
|
||||
if (props.goodsFields.salesCount?.show) {
|
||||
if (props.data.activityType && props.data.activityType === PromotionActivityTypeEnum.POINT.type) {
|
||||
text.push(formatExchange(props.data.sales_show_type, (props.data.pointTotalStock || 0) - (props.data.pointStock || 0)));
|
||||
} else {
|
||||
text.push(formatSales(props.data.sales_show_type, props.data.salesCount));
|
||||
}
|
||||
}
|
||||
if (props.goodsFields.stock?.show) {
|
||||
if (props.data.activityType && props.data.activityType === PromotionActivityTypeEnum.POINT.type) {
|
||||
text.push(formatStock(props.data.stock_show_type, props.data.pointTotalStock));
|
||||
} else {
|
||||
text.push(formatStock(props.data.stock_show_type, props.data.stock));
|
||||
}
|
||||
}
|
||||
return text.join(' | ');
|
||||
});
|
||||
|
||||
// 返回事件
|
||||
const emits = defineEmits(['click', 'getHeight']);
|
||||
|
||||
const onClick = () => {
|
||||
emits('click');
|
||||
};
|
||||
|
||||
// 获取卡片实时高度
|
||||
const { proxy } = getCurrentInstance();
|
||||
const elId = `sheep_${Math.ceil(Math.random() * 10e5).toString(36)}`;
|
||||
|
||||
function getGoodsPriceCardWH() {
|
||||
if (props.size === 'md') {
|
||||
const view = uni.createSelectorQuery().in(proxy);
|
||||
view.select(`#${elId}`).fields({
|
||||
size: true,
|
||||
scrollOffset: true,
|
||||
});
|
||||
view.exec((data) => {
|
||||
let totalHeight = 0;
|
||||
const goodsPriceCard = data[0];
|
||||
if (props.data.image_wh) {
|
||||
totalHeight =
|
||||
(goodsPriceCard.width / props.data.image_wh.w) * props.data.image_wh.h +
|
||||
goodsPriceCard.height;
|
||||
} else {
|
||||
totalHeight = goodsPriceCard.width;
|
||||
}
|
||||
emits('getHeight', totalHeight);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
getGoodsPriceCardWH();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tag-icon-box {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
|
||||
.tag-icon {
|
||||
width: 72rpx;
|
||||
height: 44rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.seckill-tag {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
width: 68rpx;
|
||||
height: 38rpx;
|
||||
background: linear-gradient(90deg, #ff5854 0%, #ff2621 100%);
|
||||
border-radius: 10rpx 0px 10rpx 0px;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
|
||||
.point-img {
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
margin: 0 4rpx;
|
||||
}
|
||||
|
||||
.groupon-tag {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
width: 68rpx;
|
||||
height: 38rpx;
|
||||
background: linear-gradient(90deg, #fe832a 0%, #ff6600 100%);
|
||||
border-radius: 10rpx 0px 10rpx 0px;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
|
||||
.goods-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.price-unit {
|
||||
margin-right: -4px;
|
||||
}
|
||||
|
||||
.sales-text {
|
||||
display: table;
|
||||
font-size: 24rpx;
|
||||
transform: scale(0.8);
|
||||
margin-left: 0rpx;
|
||||
color: #c4c4c4;
|
||||
}
|
||||
|
||||
.activity-tag {
|
||||
font-size: 20rpx;
|
||||
color: #ff0000;
|
||||
line-height: 30rpx;
|
||||
padding: 0 10rpx;
|
||||
border: 1px solid rgba(#ff0000, 0.25);
|
||||
border-radius: 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.goods-origin-price {
|
||||
font-size: 20rpx;
|
||||
color: #c4c4c4;
|
||||
line-height: 36rpx;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
// xs
|
||||
.xs-goods-card {
|
||||
overflow: hidden;
|
||||
// max-width: 375rpx;
|
||||
background-color: $white;
|
||||
position: relative;
|
||||
|
||||
.xs-img-box {
|
||||
width: 128rpx;
|
||||
height: 128rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.xs-goods-title {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.xs-goods-price {
|
||||
font-size: 30rpx;
|
||||
color: $red;
|
||||
}
|
||||
}
|
||||
|
||||
// sm
|
||||
.sm-goods-card {
|
||||
overflow: hidden;
|
||||
// width: 223rpx;
|
||||
// width: 100%;
|
||||
background-color: $white;
|
||||
position: relative;
|
||||
|
||||
.sm-img-box {
|
||||
// width: 228rpx;
|
||||
width: 100%;
|
||||
height: 208rpx;
|
||||
}
|
||||
|
||||
.sm-goods-content {
|
||||
padding: 20rpx 16rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.sm-goods-title {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.sm-goods-price {
|
||||
font-size: 30rpx;
|
||||
color: $red;
|
||||
}
|
||||
}
|
||||
|
||||
// md
|
||||
.md-goods-card {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background-color: $white;
|
||||
position: relative;
|
||||
|
||||
.md-img-box {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.md-goods-title {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.md-goods-subtitle {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.md-goods-price {
|
||||
font-size: 30rpx;
|
||||
color: $red;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
|
||||
.cart-box {
|
||||
width: 54rpx;
|
||||
height: 54rpx;
|
||||
background: linear-gradient(90deg, #fe8900, #ff5e00);
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
bottom: 50rpx;
|
||||
right: 20rpx;
|
||||
z-index: 2;
|
||||
|
||||
.cart-icon {
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lg
|
||||
.lg-goods-card {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background-color: $white;
|
||||
height: 280rpx;
|
||||
|
||||
.lg-img-box {
|
||||
width: 280rpx;
|
||||
height: 280rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.lg-goods-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
// line-height: 36rpx;
|
||||
// width: 410rpx;
|
||||
}
|
||||
|
||||
.lg-goods-subtitle {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
// line-height: 30rpx;
|
||||
// width: 410rpx;
|
||||
}
|
||||
|
||||
.lg-goods-price {
|
||||
font-size: 30rpx;
|
||||
color: $red;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
|
||||
.buy-box {
|
||||
position: absolute;
|
||||
bottom: 20rpx;
|
||||
right: 20rpx;
|
||||
z-index: 2;
|
||||
width: 120rpx;
|
||||
height: 50rpx;
|
||||
background: linear-gradient(90deg, #fe8900, #ff5e00);
|
||||
border-radius: 25rpx;
|
||||
font-size: 24rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.tag-box {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// sl
|
||||
|
||||
.sl-goods-card {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
background-color: $white;
|
||||
|
||||
.sl-goods-content {
|
||||
padding: 20rpx 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.sl-img-box {
|
||||
width: 100%;
|
||||
height: 360rpx;
|
||||
}
|
||||
|
||||
.sl-goods-title {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sl-goods-subtitle {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
|
||||
.sl-goods-price {
|
||||
font-size: 30rpx;
|
||||
color: $red;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
|
||||
.buy-box {
|
||||
position: absolute;
|
||||
bottom: 20rpx;
|
||||
right: 20rpx;
|
||||
z-index: 2;
|
||||
width: 148rpx;
|
||||
height: 50rpx;
|
||||
background: linear-gradient(90deg, #fe8900, #ff5e00);
|
||||
border-radius: 25rpx;
|
||||
font-size: 24rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
padding: 2rpx 10rpx;
|
||||
background-color: red;
|
||||
color: #ffffff;
|
||||
font-size: 24rpx;
|
||||
margin-top: 5rpx;
|
||||
}
|
||||
|
||||
.card2 {
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
padding: 2rpx 10rpx;
|
||||
background-color: rgb(255, 242, 241);
|
||||
color: #ff2621;
|
||||
font-size: 24rpx;
|
||||
margin: 5rpx 0 5rpx 5rpx;
|
||||
}
|
||||
|
||||
.iconBox {
|
||||
width: 100%;
|
||||
height: fit-content;
|
||||
margin-top: 10rpx;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
</style>
|
||||
@@ -1,189 +0,0 @@
|
||||
<template>
|
||||
<view>
|
||||
<view>
|
||||
<slot name="top"></slot>
|
||||
</view>
|
||||
<view
|
||||
class="ss-order-card-warp ss-flex ss-col-stretch ss-row-between bg-white"
|
||||
:style="[{ borderRadius: radius + 'rpx', marginBottom: marginBottom + 'rpx' }]"
|
||||
>
|
||||
<view class="img-box ss-m-r-24">
|
||||
<image class="order-img" :src="sheep.$url.cdn(img)" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view
|
||||
class="box-right ss-flex-col ss-row-between"
|
||||
:style="[{ width: titleWidth ? titleWidth + 'rpx' : '' }]"
|
||||
>
|
||||
<view class="title-text ss-line-2" v-if="title">{{ title }}</view>
|
||||
<view v-if="skuString" class="spec-text ss-m-t-8 ss-m-b-12">{{ skuString }}</view>
|
||||
<view class="groupon-box">
|
||||
<slot name="groupon"></slot>
|
||||
</view>
|
||||
<view class="ss-flex">
|
||||
<view class="ss-flex ss-col-center">
|
||||
<view
|
||||
class="price-text ss-flex ss-col-center"
|
||||
:style="[{ color: priceColor }]"
|
||||
v-if="price && Number(price) > 0"
|
||||
>
|
||||
¥{{ fen2yuan(price) }}
|
||||
</view>
|
||||
<view v-if="point && Number(price) > 0">+</view>
|
||||
<view class="price-text ss-flex ss-col-center" v-if="point">
|
||||
<image
|
||||
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
|
||||
class="point-img"
|
||||
></image>
|
||||
<view>{{ point }}</view>
|
||||
</view>
|
||||
<view v-if="num" class="total-text ss-flex ss-col-center">x {{ num }}</view>
|
||||
<slot name="priceSuffix"></slot>
|
||||
</view>
|
||||
</view>
|
||||
<view class="tool-box">
|
||||
<slot name="tool"></slot>
|
||||
</view>
|
||||
<view>
|
||||
<slot name="rightBottom"></slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { computed } from 'vue';
|
||||
import { fen2yuan } from '@/sheep/hooks/useGoods';
|
||||
/**
|
||||
* 订单卡片
|
||||
*
|
||||
* @property {String} img - 图片
|
||||
* @property {String} title - 标题
|
||||
* @property {Number} titleWidth = 0 - 标题宽度,默认0,单位rpx
|
||||
* @property {String} skuText - 规格
|
||||
* @property {String | Number} price - 价格
|
||||
* @property {String} priceColor - 价格颜色
|
||||
* @property {Number | String} num - 数量
|
||||
*
|
||||
*/
|
||||
const props = defineProps({
|
||||
img: {
|
||||
type: String,
|
||||
default: 'https://img1.baidu.com/it/u=1601695551,235775011&fm=26&fmt=auto',
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
titleWidth: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
skuText: {
|
||||
type: [String, Array],
|
||||
default: '',
|
||||
},
|
||||
price: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
priceColor: {
|
||||
type: [String],
|
||||
default: '',
|
||||
},
|
||||
num: {
|
||||
type: [String, Number],
|
||||
default: 0,
|
||||
},
|
||||
point: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
radius: {
|
||||
type: [String],
|
||||
default: '',
|
||||
},
|
||||
marginBottom: {
|
||||
type: [String],
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
const skuString = computed(() => {
|
||||
if (!props.skuText) {
|
||||
return '';
|
||||
}
|
||||
if (typeof props.skuText === 'object') {
|
||||
return props.skuText.join(',');
|
||||
}
|
||||
return props.skuText;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.point-img {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin: 0 4rpx;
|
||||
}
|
||||
.ss-order-card-warp {
|
||||
padding: 20rpx;
|
||||
|
||||
.img-box {
|
||||
width: 164rpx;
|
||||
height: 164rpx;
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.order-img {
|
||||
width: 164rpx;
|
||||
height: 164rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.box-right {
|
||||
flex: 1;
|
||||
// width: 500rpx;
|
||||
// height: 164rpx;
|
||||
position: relative;
|
||||
|
||||
.tool-box {
|
||||
position: absolute;
|
||||
right: 0rpx;
|
||||
bottom: -10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
.spec-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: $dark-9;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.price-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
|
||||
.total-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
line-height: 24rpx;
|
||||
color: $dark-9;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,33 +0,0 @@
|
||||
<!-- 商品组 - 横向滚动商品(目前暂时没用到) -->
|
||||
<template>
|
||||
<view class="goods-scroll-box">
|
||||
<scroll-view class="scroll-box" scroll-x scroll-anchoring>
|
||||
<view class="goods-box ss-flex">
|
||||
<view v-for="(item, index) in list" :key="index">
|
||||
<s-goods-column
|
||||
class="goods-card ss-m-l-20"
|
||||
size="sm"
|
||||
:data="item"
|
||||
:titleWidth="200 - marginLeft - marginRight"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 商品组 - 横向滚动商品
|
||||
*/
|
||||
const props = defineProps({
|
||||
list: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,147 +0,0 @@
|
||||
<!-- 装修商品组件:商品栏 -->
|
||||
<template>
|
||||
<view>
|
||||
<!-- 布局1. 两列商品,图片左文案右 -->
|
||||
<view
|
||||
v-if="layoutType === 'twoCol'"
|
||||
class="goods-xs-box ss-flex ss-flex-wrap"
|
||||
:style="[{ margin: '-' + data.space + 'rpx' }]"
|
||||
>
|
||||
<view
|
||||
class="goods-xs-list"
|
||||
v-for="item in goodsList"
|
||||
:key="item.id"
|
||||
:style="[
|
||||
{
|
||||
padding: data.space + 'rpx',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-card"
|
||||
size="xs"
|
||||
:goodsFields="data.fields"
|
||||
:tagStyle="data.badge"
|
||||
:data="item"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
:titleWidth="(454 - marginRight * 2 - data.space * 2 - marginLeft * 2) / 2"
|
||||
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 布局. 三列商品:图片上文案下 -->
|
||||
<view
|
||||
v-if="layoutType === 'threeCol'"
|
||||
class="goods-sm-box ss-flex ss-flex-wrap"
|
||||
:style="[{ margin: '-' + data.space + 'rpx' }]"
|
||||
>
|
||||
<view
|
||||
v-for="item in goodsList"
|
||||
:key="item.id"
|
||||
class="goods-card-box"
|
||||
:style="[
|
||||
{
|
||||
padding: data.space + 'rpx',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-card"
|
||||
size="sm"
|
||||
:goodsFields="data.fields"
|
||||
:tagStyle="data.badge"
|
||||
:data="item"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 布局3. 一行商品,水平滑动 -->
|
||||
<view v-if="layoutType === 'horizSwiper'" class="">
|
||||
<scroll-view class="scroll-box goods-scroll-box" scroll-x scroll-anchoring>
|
||||
<view class="goods-box ss-flex">
|
||||
<view
|
||||
class="goods-card-box"
|
||||
v-for="item in goodsList"
|
||||
:key="item.id"
|
||||
:style="[{ marginRight: data.space * 2 + 'rpx' }]"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-card"
|
||||
size="sm"
|
||||
:goodsFields="data.fields"
|
||||
:tagStyle="data.badge"
|
||||
:data="item"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:titleWidth="(750 - marginRight * 2 - data.space * 4 - marginLeft * 2) / 3"
|
||||
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 商品栏
|
||||
*/
|
||||
import { onMounted, ref, computed } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import SpuApi from "@/sheep/api/product/spu";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
const { layoutType, spuIds } = props.data;
|
||||
let { marginLeft, marginRight } = props.styles;
|
||||
const goodsList = ref([]);
|
||||
onMounted(async () => {
|
||||
if (spuIds.length > 0) {
|
||||
let { data } = await SpuApi.getSpuListByIds(spuIds.join(','));
|
||||
goodsList.value = data;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-xs-box {
|
||||
// margin: 0 auto;
|
||||
width: 100%;
|
||||
.goods-xs-list {
|
||||
box-sizing: border-box;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-sm-box {
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
.goods-card-box {
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
width: 33.3%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
.goods-scroll-box {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
@@ -1,328 +0,0 @@
|
||||
<!-- 装修商品组件:【拼团】商品卡片 -->
|
||||
<template>
|
||||
<!-- 商品卡片 -->
|
||||
<view>
|
||||
<!-- 布局1. 单列大图(上图,下内容)-->
|
||||
<view
|
||||
v-if="layoutType === LayoutTypeEnum.ONE_COL_BIG_IMG && state.spuList.length"
|
||||
class="goods-sl-box"
|
||||
>
|
||||
<view
|
||||
class="goods-box"
|
||||
v-for="item in state.spuList"
|
||||
:key="item.id"
|
||||
:style="[{ marginBottom: data.space * 2 + 'rpx' }]"
|
||||
>
|
||||
<s-goods-column
|
||||
class=""
|
||||
size="sl"
|
||||
:goodsFields="data.fields"
|
||||
:tagStyle="data.badge"
|
||||
:data="item"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:subTitleColor="data.fields.introduction.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
@click="sheep.$router.go('/pages/goods/groupon', { id: item.activityId })"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 布局2. 单列小图(左图,右内容) -->
|
||||
<view
|
||||
v-if="layoutType === LayoutTypeEnum.ONE_COL_SMALL_IMG && state.spuList.length"
|
||||
class="goods-lg-box"
|
||||
>
|
||||
<view
|
||||
class="goods-box"
|
||||
:style="[{ marginBottom: data.space + 'px' }]"
|
||||
v-for="item in state.spuList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-card"
|
||||
size="lg"
|
||||
:goodsFields="data.fields"
|
||||
:data="item"
|
||||
:tagStyle="data.badge"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:subTitleColor="data.fields.introduction.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
@tap="sheep.$router.go('/pages/goods/groupon', { id: item.activityId })"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 布局3. 双列(每一列:上图,下内容)-->
|
||||
<view
|
||||
v-if="layoutType === LayoutTypeEnum.TWO_COL && state.spuList.length"
|
||||
class="goods-md-wrap ss-flex ss-flex-wrap ss-col-top"
|
||||
>
|
||||
<view class="goods-list-box">
|
||||
<view
|
||||
class="left-list"
|
||||
:style="[{ paddingRight: data.space + 'rpx', marginBottom: data.space + 'px' }]"
|
||||
v-for="item in state.leftSpuList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-md-box"
|
||||
size="md"
|
||||
:goodsFields="data.fields"
|
||||
:tagStyle="data.badge"
|
||||
:data="item"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:subTitleColor="data.fields.introduction.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
:titleWidth="330 - marginLeft - marginRight"
|
||||
@click="sheep.$router.go('/pages/goods/groupon', { id: item.activityId })"
|
||||
@getHeight="calculateGoodsColumn($event, 'left')"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
<view class="goods-list-box">
|
||||
<view
|
||||
class="right-list"
|
||||
:style="[{ paddingLeft: data.space + 'rpx', marginBottom: data.space + 'px' }]"
|
||||
v-for="item in state.rightSpuList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-md-box"
|
||||
size="md"
|
||||
:goodsFields="data.fields"
|
||||
:tagStyle="data.badge"
|
||||
:data="item"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:subTitleColor="data.fields.introduction.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
:titleWidth="330 - marginLeft - marginRight"
|
||||
@click="sheep.$router.go('/pages/goods/groupon', { id: item.activityId })"
|
||||
@getHeight="calculateGoodsColumn($event, 'right')"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 商品卡片
|
||||
*/
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import CombinationApi from '@/sheep/api/promotion/combination';
|
||||
import SpuApi from '@/sheep/api/product/spu';
|
||||
|
||||
// 布局类型
|
||||
const LayoutTypeEnum = {
|
||||
// 单列大图
|
||||
ONE_COL_BIG_IMG: 'oneColBigImg',
|
||||
// 双列
|
||||
TWO_COL: 'twoCol',
|
||||
// 单列小图
|
||||
ONE_COL_SMALL_IMG: 'oneColSmallImg',
|
||||
};
|
||||
|
||||
const state = reactive({
|
||||
spuList: [],
|
||||
leftSpuList: [],
|
||||
rightSpuList: [],
|
||||
});
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
|
||||
const { layoutType, btnBuy, activityIds } = props.data || {};
|
||||
const { marginLeft, marginRight } = props.styles || {};
|
||||
|
||||
// 购买按钮样式
|
||||
const buyStyle = computed(() => {
|
||||
if (btnBuy.type === 'text') {
|
||||
// 文字按钮:线性渐变背景颜色
|
||||
return {
|
||||
background: `linear-gradient(to right, ${btnBuy.bgBeginColor}, ${btnBuy.bgEndColor})`,
|
||||
};
|
||||
}
|
||||
if (btnBuy.type === 'img') {
|
||||
// 图片按钮
|
||||
return {
|
||||
width: '54rpx',
|
||||
height: '54rpx',
|
||||
background: `url(${sheep.$url.cdn(btnBuy.imgUrl)}) no-repeat`,
|
||||
backgroundSize: '100% 100%',
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
//region 商品瀑布流布局
|
||||
// 下一个要处理的商品索引
|
||||
let count = 0;
|
||||
// 左列的高度
|
||||
let leftHeight = 0;
|
||||
// 右列的高度
|
||||
let rightHeight = 0;
|
||||
|
||||
/**
|
||||
* 计算商品在左列还是右列
|
||||
* @param height 商品的高度
|
||||
* @param where 添加到哪一列
|
||||
*/
|
||||
function calculateGoodsColumn(height = 0, where = 'left') {
|
||||
// 处理完
|
||||
if (!state.spuList[count]) return;
|
||||
// 增加列的高度
|
||||
if (where === 'left') leftHeight += height;
|
||||
if (where === 'right') rightHeight += height;
|
||||
// 添加到矮的一列
|
||||
if (leftHeight <= rightHeight) {
|
||||
state.leftSpuList.push(state.spuList[count]);
|
||||
} else {
|
||||
state.rightSpuList.push(state.spuList[count]);
|
||||
}
|
||||
// 计数
|
||||
count++;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
/**
|
||||
* 根据商品编号列表,获取商品列表
|
||||
* @param ids 商品编号列表
|
||||
* @return {Promise<undefined>} 商品列表
|
||||
*/
|
||||
async function getCombinationActivityDetailList(ids) {
|
||||
const { data } = await CombinationApi.getCombinationActivityListByIds(ids);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据商品编号,获取商品详情
|
||||
* @param ids 商品编号列表
|
||||
* @return {Promise<undefined>} 商品列表
|
||||
*/
|
||||
async function getSpuDetail(ids) {
|
||||
const { data: spu } = await SpuApi.getSpuDetail(ids);
|
||||
return spu;
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
// 加载活动列表
|
||||
const activityList = await getCombinationActivityDetailList(activityIds.join(','));
|
||||
// 循环获取活动商品SPU详情并添加到spuList
|
||||
for (const activity of activityList) {
|
||||
state.spuList.push(await getSpuDetail(activity.spuId));
|
||||
}
|
||||
|
||||
// 循环活动列表
|
||||
activityList.forEach((activity) => {
|
||||
// 提取活动价格
|
||||
const combinationPrice = activity.combinationPrice || Infinity;
|
||||
// 查找对应的 spu 并更新价格
|
||||
const spu = state.spuList.find((spu) => activity.spuId === spu.id);
|
||||
if (spu) {
|
||||
// 赋值活动名称
|
||||
// TODO 芋艿:暂定活动名。会在调研一些类似有赞、淘宝、京东的选择
|
||||
spu.name = activity.name;
|
||||
// 赋值最低价格
|
||||
spu.price = Math.min(combinationPrice, spu.price);
|
||||
// 赋值活动ID,为了点击跳转详情页
|
||||
spu.activityId = activity.id;
|
||||
}
|
||||
});
|
||||
|
||||
// 只有双列布局时需要
|
||||
if (layoutType === LayoutTypeEnum.TWO_COL) {
|
||||
// 分列
|
||||
calculateGoodsColumn();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-md-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.goods-list-box {
|
||||
width: 50%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.left-list {
|
||||
&:nth-last-child(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.right-list {
|
||||
&:nth-last-child(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.goods-box {
|
||||
&:nth-last-of-type(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-md-box,
|
||||
.goods-sl-box,
|
||||
.goods-lg-box {
|
||||
position: relative;
|
||||
|
||||
.cart-btn {
|
||||
position: absolute;
|
||||
bottom: 18rpx;
|
||||
right: 20rpx;
|
||||
z-index: 11;
|
||||
height: 50rpx;
|
||||
line-height: 50rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 25rpx;
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,46 +0,0 @@
|
||||
<!-- 装修图文组件:热区 -->
|
||||
<template>
|
||||
<view class="hotzone-wrap">
|
||||
<image :src="sheep.$url.cdn(data.imgUrl)" style="width: 100%" mode="widthFix"></image>
|
||||
<view
|
||||
class="hotzone-box"
|
||||
v-for="(item, index) in data.list"
|
||||
:key="index"
|
||||
:style="[
|
||||
{
|
||||
top: `${item.top}px`,
|
||||
left: `${item.left}px`,
|
||||
width: `${item.width}px`,
|
||||
height: `${item.height}px`,
|
||||
},
|
||||
]"
|
||||
@tap.stop="sheep.$router.go(item.url)"
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
|
||||
// 接收参数
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.hotzone-wrap {
|
||||
position: relative;
|
||||
}
|
||||
.hotzone-box {
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
@@ -1,44 +0,0 @@
|
||||
<!-- 装修图文组件:图片轮播 -->
|
||||
<template>
|
||||
<su-swiper
|
||||
:list="imgList"
|
||||
:dotStyle="data.indicator === 'dot' ? 'long' : 'tag'"
|
||||
imageMode="scaleToFill"
|
||||
dotCur="bg-mask-40"
|
||||
:seizeHeight="300"
|
||||
:autoplay="data.autoplay"
|
||||
:interval="data.interval * 1000"
|
||||
:mode="data.type"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
// 轮播图
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
const imgList = computed(() =>
|
||||
props.data.items.map((item) => {
|
||||
const src = item.type === 'img' ? item.imgUrl : item.videoUrl;
|
||||
return {
|
||||
...item,
|
||||
type: item.type === 'img' ? 'image' : 'video',
|
||||
src: sheep.$url.cdn(src),
|
||||
poster: sheep.$url.cdn(item.imgUrl),
|
||||
};
|
||||
}),
|
||||
);
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,27 +0,0 @@
|
||||
<!-- 装修图文组件:图片展示 -->
|
||||
<template>
|
||||
<view @tap="sheep.$router.go(data?.url)">
|
||||
<su-image :src="sheep.$url.cdn(data.imgUrl)" mode="widthFix" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 图片组件
|
||||
*/
|
||||
import sheep from '@/sheep';
|
||||
|
||||
// 接收参数
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,110 +0,0 @@
|
||||
<!-- 装修图文组件:广告魔方 -->
|
||||
<template>
|
||||
<view class="ss-cube-wrap" :style="[parseAdWrap]">
|
||||
<view v-for="(item, index) in data.list" :key="index">
|
||||
<view
|
||||
class="cube-img-wrap"
|
||||
:style="[parseImgStyle(item), { margin: data.space + 'px' }]"
|
||||
@tap="sheep.$router.go(item.url)"
|
||||
>
|
||||
<image class="cube-img" :src="sheep.$url.cdn(item.imgUrl)" mode="aspectFill"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
/**
|
||||
/**
|
||||
* 广告魔方
|
||||
*
|
||||
* @property {Array<Object>} list - 魔方列表
|
||||
* @property {Object} styles - 组件样式
|
||||
* @property {String} background - 组件背景色
|
||||
* @property {Number} topSpace - 组件顶部间距
|
||||
* @property {Number} bottomSpace - 组件底部间距
|
||||
* @property {Number} leftSpace - 容器左间距
|
||||
* @property {Number} rightSpace - 容器右间距
|
||||
* @property {Number} imgSpace - 图片间距
|
||||
* @property {Number} imgTopRadius - 图片上圆角
|
||||
* @property {Number} imgBottomRadius - 图片下圆角
|
||||
*
|
||||
*/
|
||||
|
||||
import { computed, inject, unref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
// 参数
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
|
||||
// 单元格大小
|
||||
const windowWidth = sheep.$platform.device.windowWidth;
|
||||
const cell = computed(() => {
|
||||
return (
|
||||
(windowWidth -
|
||||
((props.styles.marginLeft || 0) + (props.styles.marginRight || 0) + (props.styles.padding || 0) * 2)) /
|
||||
4
|
||||
);
|
||||
});
|
||||
|
||||
//包裹容器高度
|
||||
const parseAdWrap = computed(() => {
|
||||
let heightArr = props.data.list.reduce(
|
||||
(prev, cur) => (prev.includes(cur.height + cur.top) ? prev : [...prev, cur.height + cur.top]),
|
||||
[],
|
||||
);
|
||||
let heightMax = Math.max(...heightArr);
|
||||
return {
|
||||
height: heightMax * cell.value + 'px',
|
||||
width:
|
||||
windowWidth -
|
||||
(props.data?.style?.marginLeft +
|
||||
props.data?.style?.marginRight +
|
||||
props.styles.padding * 2) *
|
||||
2 +
|
||||
'px',
|
||||
};
|
||||
});
|
||||
|
||||
// 解析图片大小位置
|
||||
const parseImgStyle = (item) => {
|
||||
let obj = {
|
||||
width: item.width * cell.value - props.data.space + 'px',
|
||||
height: item.height * cell.value - props.data.space + 'px',
|
||||
left: item.left * cell.value + 'px',
|
||||
top: item.top * cell.value + 'px',
|
||||
'border-top-left-radius': props.data.borderRadiusTop + 'px',
|
||||
'border-top-right-radius': props.data.borderRadiusTop + 'px',
|
||||
'border-bottom-left-radius': props.data.borderRadiusBottom + 'px',
|
||||
'border-bottom-right-radius': props.data.borderRadiusBottom + 'px',
|
||||
};
|
||||
return obj;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ss-cube-wrap {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: 750rpx;
|
||||
}
|
||||
|
||||
.cube-img-wrap {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cube-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,258 +0,0 @@
|
||||
<template>
|
||||
<view
|
||||
class="page-app"
|
||||
:class="['theme-' + sys?.mode, 'main-' + sys?.theme, 'font-' + sys?.fontSize]"
|
||||
>
|
||||
<view class="page-main" :style="[bgMain]">
|
||||
<!-- 顶部导航栏-情况1:默认通用顶部导航栏 -->
|
||||
<su-navbar
|
||||
v-if="navbar === 'normal'"
|
||||
:title="title"
|
||||
statusBar
|
||||
:color="color"
|
||||
:tools="tools"
|
||||
:opacityBgUi="opacityBgUi"
|
||||
@search="(e) => emits('search', e)"
|
||||
:defaultSearch="defaultSearch"
|
||||
/>
|
||||
|
||||
<!-- 顶部导航栏-情况2:装修组件导航栏-标准 -->
|
||||
<s-custom-navbar
|
||||
v-else-if="navbar === 'custom' && navbarMode === 'normal'"
|
||||
:data="navbarStyle"
|
||||
:showLeftButton="showLeftButton"
|
||||
/>
|
||||
<view class="page-body" :style="[bgBody]">
|
||||
<!-- 顶部导航栏-情况3:沉浸式头部 -->
|
||||
<su-inner-navbar v-if="navbar === 'inner'" :title="title" />
|
||||
<view
|
||||
v-if="navbar === 'inner'"
|
||||
:style="[{ paddingTop: sheep?.$platform?.navbar + 'px' }]"
|
||||
></view>
|
||||
|
||||
<!-- 顶部导航栏-情况4:装修组件导航栏-沉浸式 -->
|
||||
<s-custom-navbar
|
||||
v-if="navbar === 'custom' && navbarMode === 'inner'"
|
||||
:data="navbarStyle"
|
||||
:showLeftButton="showLeftButton"
|
||||
/>
|
||||
|
||||
<!-- 页面内容插槽 -->
|
||||
<slot />
|
||||
|
||||
<!-- 底部导航 -->
|
||||
<s-tabbar v-if="tabbar !== ''" :path="tabbar" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="page-modal">
|
||||
<!-- 全局授权弹窗 -->
|
||||
<s-auth-modal />
|
||||
<!-- 全局分享弹窗 -->
|
||||
<s-share-modal :shareInfo="shareInfo" />
|
||||
<!-- 全局快捷入口 -->
|
||||
<s-menu-tools />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 模板组件 - 提供页面公共组件,属性,方法
|
||||
*/
|
||||
import { computed, onMounted } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
// #ifdef MP-WEIXIN
|
||||
import { onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app';
|
||||
// #endif
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
navbar: {
|
||||
type: String,
|
||||
default: 'normal',
|
||||
},
|
||||
opacityBgUi: {
|
||||
type: String,
|
||||
default: 'bg-white',
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
tools: {
|
||||
type: String,
|
||||
default: 'title',
|
||||
},
|
||||
keyword: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
navbarStyle: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
styleType: '',
|
||||
type: '',
|
||||
color: '',
|
||||
src: '',
|
||||
list: [],
|
||||
alwaysShow: 0,
|
||||
}),
|
||||
},
|
||||
bgStyle: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
src: '',
|
||||
color: 'var(--ui-BG-1)',
|
||||
}),
|
||||
},
|
||||
tabbar: {
|
||||
type: [String, Boolean],
|
||||
default: '',
|
||||
},
|
||||
onShareAppMessage: {
|
||||
type: [Boolean, Object],
|
||||
default: true,
|
||||
},
|
||||
leftWidth: {
|
||||
type: [Number, String],
|
||||
default: 100,
|
||||
},
|
||||
rightWidth: {
|
||||
type: [Number, String],
|
||||
default: 100,
|
||||
},
|
||||
defaultSearch: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
//展示返回按钮
|
||||
showLeftButton: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(['search']);
|
||||
|
||||
const sysStore = sheep.$store('sys');
|
||||
const userStore = sheep.$store('user');
|
||||
const appStore = sheep.$store('app');
|
||||
const modalStore = sheep.$store('modal');
|
||||
const sys = computed(() => sysStore);
|
||||
|
||||
// 导航栏模式(因为有自定义导航栏 需要计算)
|
||||
const navbarMode = computed(() => {
|
||||
if (props.navbar === 'normal' || props.navbarStyle.styleType === 'normal') {
|
||||
return 'normal';
|
||||
}
|
||||
return 'inner';
|
||||
});
|
||||
|
||||
// 背景1
|
||||
const bgMain = computed(() => {
|
||||
if (navbarMode.value === 'inner') {
|
||||
return {
|
||||
background: `${props.bgStyle.backgroundColor || props.bgStyle.color} url(${sheep.$url.cdn(
|
||||
props.bgStyle.backgroundImage,
|
||||
)}) no-repeat top center / 100% auto`,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
// 背景2
|
||||
const bgBody = computed(() => {
|
||||
if (navbarMode.value === 'normal') {
|
||||
return {
|
||||
background: `${props.bgStyle.backgroundColor || props.bgStyle.color} url(${sheep.$url.cdn(
|
||||
props.bgStyle.backgroundImage,
|
||||
)}) no-repeat top center / 100% auto`,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
// 分享信息
|
||||
const shareInfo = computed(() => {
|
||||
if (props.onShareAppMessage === true) {
|
||||
return sheep.$platform.share.getShareInfo();
|
||||
} else {
|
||||
if (!isEmpty(props.onShareAppMessage)) {
|
||||
sheep.$platform.share.updateShareInfo(props.onShareAppMessage);
|
||||
return props.onShareAppMessage;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.showShareMenu({
|
||||
withShareTicket: true,
|
||||
menus: ['shareAppMessage', 'shareTimeline'],
|
||||
});
|
||||
// 微信小程序分享好友
|
||||
onShareAppMessage(() => {
|
||||
return {
|
||||
title: shareInfo.value.title,
|
||||
path: shareInfo.value.forward.path,
|
||||
imageUrl: shareInfo.value.image,
|
||||
};
|
||||
});
|
||||
// 微信小程序分享朋友圈
|
||||
onShareTimeline(() => {
|
||||
return {
|
||||
title: shareInfo.value.title,
|
||||
query: shareInfo.value.forward.path,
|
||||
imageUrl: shareInfo.value.image,
|
||||
};
|
||||
});
|
||||
// #endif
|
||||
|
||||
// 组件中使用 onMounted 监听页面加载,不是页面组件不使用 onShow
|
||||
onMounted(()=>{
|
||||
if (!isEmpty(shareInfo.value)) {
|
||||
sheep.$platform.share.updateShareInfo(shareInfo.value);
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-app {
|
||||
position: relative;
|
||||
color: var(--ui-TC);
|
||||
background-color: var(--ui-BG-1) !important;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
|
||||
.page-main {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.page-body {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.page-img {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,15 +0,0 @@
|
||||
<!-- 装修基础组件:分割线 -->
|
||||
<template>
|
||||
<su-subline v-bind="data"></su-subline>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,144 +0,0 @@
|
||||
<template>
|
||||
<view>
|
||||
<view
|
||||
v-if="mode === 2 && state.liveList.length"
|
||||
class="goods-md-wrap ss-flex ss-flex-wrap ss-col-top"
|
||||
:style="[{ margin: '-' + data.space + 'rpx' }]"
|
||||
>
|
||||
<view
|
||||
:style="[
|
||||
{
|
||||
padding: data.space + 'rpx',
|
||||
},
|
||||
]"
|
||||
class="goods-list-box"
|
||||
v-for="item in state.liveList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-live-card
|
||||
class="goods-md-box"
|
||||
size="md"
|
||||
:goodsFields="goodsFields"
|
||||
:data="item"
|
||||
:titleColor="goodsFields.name?.color"
|
||||
:subTitleColor="goodsFields.anchor_name?.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
@click="goRoom(item.roomid)"
|
||||
>
|
||||
</s-live-card>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="mode === 1 && state.liveList.length" class="goods-lg-box">
|
||||
<view
|
||||
class="goods-box"
|
||||
:style="[{ marginBottom: data.space + 'px' }]"
|
||||
v-for="item in state.liveList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-live-card
|
||||
class="goods-card"
|
||||
size="sl"
|
||||
:goodsFields="goodsFields"
|
||||
:data="item"
|
||||
:titleColor="goodsFields.name?.color"
|
||||
:subTitleColor="goodsFields.anchor_name.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
@tap="goRoom(item.roomid)"
|
||||
>
|
||||
</s-live-card>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive, onMounted } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const state = reactive({
|
||||
liveList: [],
|
||||
mpLink: '',
|
||||
});
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
const { mode, goodsFields, mpliveIds } = props.data || {};
|
||||
const { marginLeft, marginRight } = props.styles || {};
|
||||
|
||||
async function getLiveListByIds(ids) {
|
||||
const { data } = await sheep.$api.app.mplive.getRoomList(ids);
|
||||
return data;
|
||||
}
|
||||
function goRoom(id) {
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.navigateTo({
|
||||
url: `plugin-private://wx2b03c6e691cd7370/pages/live-player-plugin?room_id=${id}`,
|
||||
});
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
confirmText: '允许',
|
||||
content: '将打开小程序访问',
|
||||
success: async function (res) {
|
||||
if (res.confirm) {
|
||||
getMpLink();
|
||||
}
|
||||
},
|
||||
});
|
||||
// #endif
|
||||
}
|
||||
|
||||
function goMpLink() {
|
||||
// #ifdef H5
|
||||
window.location = state.mpLink;
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
plus.runtime.openURL(state.mpLink);
|
||||
// #endif
|
||||
}
|
||||
|
||||
async function getMpLink() {
|
||||
// #ifndef MP-WEIXIN
|
||||
if (state.mpLink === '') {
|
||||
const { error, data } = await sheep.$api.app.mplive.getMpLink();
|
||||
if (error === 0) {
|
||||
state.mpLink = data;
|
||||
}
|
||||
}
|
||||
goMpLink();
|
||||
// #endif
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
state.liveList = await getLiveListByIds(mpliveIds);
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.goods-list-box {
|
||||
width: 50%;
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.goods-box {
|
||||
&:nth-last-of-type(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-md-box,
|
||||
.goods-sl-box {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
@@ -1,234 +0,0 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- md卡片:竖向,一行放两个,图上内容下 -->
|
||||
<view v-if="size === 'md'" class="md-goods-card ss-flex-col" :style="[elStyles]" @tap="onClick">
|
||||
<view class="icon-box ss-flex">
|
||||
<image class="icon" :src="state.liveStatus[data.status].img"></image>
|
||||
<view class="title ss-m-l-16">{{ state.liveStatus[data.status].title }}</view>
|
||||
</view>
|
||||
<img class="md-img-box" :src="sheep.$url.cdn(data.feeds_img)" referrerpolicy="no-referrer">
|
||||
<view class="md-goods-content">
|
||||
<view class="md-goods-title ss-line-1" :style="[{ color: titleColor }]">
|
||||
{{ data.name }}
|
||||
</view>
|
||||
<view class="md-goods-subtitle ss-m-t-14 ss-line-1" :style="[{ color: subTitleColor }]">
|
||||
主播:{{ data.anchor_name }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- sl卡片:竖向型,一行放一个,图片上内容下边 -->
|
||||
<view v-if="size === 'sl'" class="sl-goods-card ss-flex-col" :style="[elStyles]" @tap="onClick">
|
||||
<view class="icon-box ss-flex">
|
||||
<image class="icon" :src="state.liveStatus[data.status].img"></image>
|
||||
<view class="title ss-m-l-16">{{ state.liveStatus[data.status].title }}</view>
|
||||
</view>
|
||||
<img class="sl-img-box" :src="sheep.$url.cdn(data.feeds_img)" referrerpolicy="no-referrer">
|
||||
<view class="sl-goods-content">
|
||||
<view class="sl-goods-title ss-line-1" :style="[{ color: titleColor }]">
|
||||
{{ data.name }}
|
||||
</view>
|
||||
<view class="sl-goods-subtitle ss-m-t-14 ss-line-1" :style="[{ color: subTitleColor }]">
|
||||
主播:{{ data.anchor_name }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, reactive } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
/**
|
||||
* 直播卡片
|
||||
*
|
||||
* @property {String} img - 图片
|
||||
* @property {String} title - 标题
|
||||
* @property {Number} titleWidth = 0 - 标题宽度,默认0,单位rpx
|
||||
* @property {String} skuText - 规格
|
||||
* @property {String | Number} score - 积分
|
||||
* @property {String | Number} price - 价格
|
||||
* @property {String | Number} originalPrice - 单购价
|
||||
* @property {String} priceColor - 价格颜色
|
||||
* @property {Number | String} num - 数量
|
||||
*
|
||||
*/
|
||||
const props = defineProps({
|
||||
goodsFields: {
|
||||
type: [Array, Object],
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
tagStyle: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'sl',
|
||||
},
|
||||
background: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
topRadius: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
bottomRadius: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
titleColor: {
|
||||
type: String,
|
||||
default: '#333',
|
||||
},
|
||||
subTitleColor: {
|
||||
type: String,
|
||||
default: '#999999',
|
||||
},
|
||||
});
|
||||
// 组件样式
|
||||
const elStyles = computed(() => {
|
||||
return {
|
||||
background: props.background,
|
||||
'border-top-left-radius': props.topRadius + 'px',
|
||||
'border-top-right-radius': props.topRadius + 'px',
|
||||
'border-bottom-left-radius': props.bottomRadius + 'px',
|
||||
'border-bottom-right-radius': props.bottomRadius + 'px',
|
||||
};
|
||||
});
|
||||
const state = reactive({
|
||||
liveStatus: {
|
||||
101: {
|
||||
img: sheep.$url.static('/static/img/shop/app/mplive/living.png'),
|
||||
title: '直播中',
|
||||
},
|
||||
102: {
|
||||
img: sheep.$url.static('/static/img/shop/app/mplive/start.png'),
|
||||
title: '未开始',
|
||||
},
|
||||
103: {
|
||||
img: sheep.$url.static('/static/img/shop/app/mplive/ended.png'),
|
||||
title: '已结束',
|
||||
},
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(['click', 'getHeight']);
|
||||
const onClick = () => {
|
||||
emits('click');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// md
|
||||
.md-goods-card {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 424rpx;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background-color: $white;
|
||||
.icon-box {
|
||||
position: absolute;
|
||||
left: 20rpx;
|
||||
top: 10rpx;
|
||||
width: 136rpx;
|
||||
height: 40rpx;
|
||||
background: rgba(#000000, 0.5);
|
||||
border-radius: 20rpx;
|
||||
z-index: 1;
|
||||
.icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 20rpx 0px 20rpx 20rpx;
|
||||
}
|
||||
.title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
.md-goods-content {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
padding: 20rpx;
|
||||
width: 100%;
|
||||
background: linear-gradient(360deg, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.02) 100%);
|
||||
}
|
||||
|
||||
.md-img-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.md-goods-title {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
width: 100%;
|
||||
}
|
||||
.md-goods-subtitle {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
.sl-goods-card {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 400rpx;
|
||||
background-color: $white;
|
||||
.icon-box {
|
||||
position: absolute;
|
||||
left: 20rpx;
|
||||
top: 10rpx;
|
||||
width: 136rpx;
|
||||
height: 40rpx;
|
||||
background: rgba(#000000, 0.5);
|
||||
border-radius: 20rpx;
|
||||
z-index: 1;
|
||||
.icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 20rpx 0px 20rpx 20rpx;
|
||||
}
|
||||
.title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
.sl-goods-content {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
padding: 20rpx;
|
||||
width: 100%;
|
||||
background: linear-gradient(360deg, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.02) 100%);
|
||||
}
|
||||
|
||||
.sl-img-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.sl-goods-title {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
width: 100%;
|
||||
}
|
||||
.sl-goods-subtitle {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,343 +0,0 @@
|
||||
<!-- 装修基础组件:菜单导航(金刚区) -->
|
||||
<template>
|
||||
<!-- 包裹层 -->
|
||||
<view class="ui-swiper" :class="[props.mode, props.ui]"
|
||||
:style="[bgStyle, { height: swiperHeight + (menuList.length > 1 ? 50 : 0) + 'rpx' }]">
|
||||
<!-- 轮播 -->
|
||||
<swiper :circular="props.circular" :current="state.cur" :autoplay="props.autoplay" :interval="props.interval"
|
||||
:duration="props.duration" :style="[{ height: swiperHeight + 'rpx' }]" @change="swiperChange">
|
||||
<swiper-item v-for="(arr, index) in menuList" :key="index" :class="{ cur: state.cur == index }">
|
||||
<!-- 宫格 -->
|
||||
<view class="grid-wrap">
|
||||
<view v-for="(item, index) in arr" :key="index"
|
||||
class="grid-item ss-flex ss-flex-col ss-col-center ss-row-center"
|
||||
:style="[{ width: `${100 * (1 / data.column)}%`, height: '200rpx' }]" hover-class="ss-hover-btn"
|
||||
@tap="sheep.$router.go(item.url)">
|
||||
<view class="menu-box ss-flex ss-flex-col ss-col-center ss-row-center">
|
||||
<view v-if="item.badge.show" class="tag-box"
|
||||
:style="[{ background: item.badge.bgColor, color: item.badge.textColor }]">
|
||||
{{ item.badge.text }}
|
||||
</view>
|
||||
<image v-if="item.iconUrl" class="menu-icon" :style="[
|
||||
{
|
||||
width: props.iconSize + 'rpx',
|
||||
height: props.iconSize + 'rpx',
|
||||
},
|
||||
]" :src="sheep.$url.cdn(item.iconUrl)" mode="aspectFill"></image>
|
||||
<view v-if="data.layout === 'iconText'" class="menu-title"
|
||||
:style="[{ color: item.titleColor }]">
|
||||
{{ item.title }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<!-- 指示点 -->
|
||||
<template v-if="menuList.length > 1">
|
||||
<view class="ui-swiper-dot" :class="props.dotStyle" v-if="props.dotStyle != 'tag'">
|
||||
<view class="line-box" v-for="(item, index) in menuList.length" :key="index"
|
||||
:class="[state.cur == index ? 'cur' : '', props.dotCur]"></view>
|
||||
</view>
|
||||
<view class="ui-swiper-dot" :class="props.dotStyle" v-if="props.dotStyle == 'tag'">
|
||||
<view class="ui-tag radius" :class="[props.dotCur]" style="pointer-events: none">
|
||||
<view style="transform: scale(0.7)">{{ state.cur + 1 }} / {{ menuList.length }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 轮播menu
|
||||
*
|
||||
* @property {Boolean} circular = false - 是否采用衔接滑动,即播放到末尾后重新回到开头
|
||||
* @property {Boolean} autoplay = true - 是否自动切换
|
||||
* @property {Number} interval = 5000 - 自动切换时间间隔
|
||||
* @property {Number} duration = 500 - 滑动动画时长,app-nvue不支持
|
||||
* @property {Array} list = [] - 轮播数据
|
||||
* @property {String} ui = '' - 样式class
|
||||
* @property {String} mode - 模式
|
||||
* @property {String} dotStyle - 指示点样式
|
||||
* @property {String} dotCur= 'ui-BG-Main' - 当前指示点样式,默认主题色
|
||||
* @property {String} bg - 背景
|
||||
*
|
||||
* @property {String|Number} col = 4 - 一行数量
|
||||
* @property {String|Number} row = 1 - 几行
|
||||
* @property {String} hasBorder - 是否有边框
|
||||
* @property {String} borderColor - 边框颜色
|
||||
* @property {String} background - 背景
|
||||
* @property {String} hoverClass - 按压样式类
|
||||
* @property {String} hoverStayTime - 动画时间
|
||||
*
|
||||
* @property {Array} list - 导航列表
|
||||
* @property {Number} iconSize - 图标大小
|
||||
* @property {String} color - 标题颜色
|
||||
*
|
||||
*/
|
||||
|
||||
import {
|
||||
reactive,
|
||||
computed
|
||||
} from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
// 数据
|
||||
const state = reactive({
|
||||
cur: 0,
|
||||
});
|
||||
|
||||
// 接收参数
|
||||
|
||||
const props = defineProps({
|
||||
// 装修数据
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
// 装修样式
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
circular: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
autoplay: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
interval: {
|
||||
type: Number,
|
||||
default: 5000,
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 500,
|
||||
},
|
||||
ui: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
mode: {
|
||||
//default
|
||||
type: String,
|
||||
default: 'default',
|
||||
},
|
||||
dotStyle: {
|
||||
type: String,
|
||||
default: 'long', //default long tag
|
||||
},
|
||||
dotCur: {
|
||||
type: String,
|
||||
default: 'ui-BG-Main',
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 300,
|
||||
},
|
||||
// 是否有边框
|
||||
hasBorder: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 边框颜色
|
||||
borderColor: {
|
||||
type: String,
|
||||
default: 'red',
|
||||
},
|
||||
background: {
|
||||
type: String,
|
||||
default: 'blue',
|
||||
},
|
||||
hoverClass: {
|
||||
type: String,
|
||||
default: 'ss-hover-class', //'none'为没有hover效果
|
||||
},
|
||||
// 一排宫格数
|
||||
col: {
|
||||
type: [Number, String],
|
||||
default: 3,
|
||||
},
|
||||
iconSize: {
|
||||
type: Number,
|
||||
default: 80,
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#000',
|
||||
},
|
||||
});
|
||||
|
||||
// 设置背景样式
|
||||
const bgStyle = computed(() => {
|
||||
// 直接从 props.styles 解构
|
||||
const {
|
||||
bgType,
|
||||
bgImg,
|
||||
bgColor
|
||||
} = props.styles;
|
||||
|
||||
// 根据 bgType 返回相应的样式
|
||||
return {
|
||||
background: bgType === 'img' ? `url(${bgImg}) no-repeat top center / 100% 100%` : bgColor
|
||||
};
|
||||
});
|
||||
|
||||
// 生成数据
|
||||
const menuList = computed(() => splitData(props.data.list, props.data.row * props.data.column));
|
||||
const swiperHeight = computed(() => props.data.row * (props.data.layout === 'iconText' ? 200 : 180));
|
||||
const windowWidth = sheep.$platform.device.windowWidth;
|
||||
|
||||
// current 改变时会触发 change 事件
|
||||
const swiperChange = (e) => {
|
||||
state.cur = e.detail.current;
|
||||
};
|
||||
|
||||
// 重组数据
|
||||
const splitData = (oArr = [], length = 1) => {
|
||||
let arr = [];
|
||||
let minArr = [];
|
||||
oArr.forEach((c) => {
|
||||
if (minArr.length === length) {
|
||||
minArr = [];
|
||||
}
|
||||
if (minArr.length === 0) {
|
||||
arr.push(minArr);
|
||||
}
|
||||
minArr.push(c);
|
||||
});
|
||||
|
||||
return arr;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.grid-wrap {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.menu-box {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
transform: translate(0, 0);
|
||||
|
||||
.tag-box {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
right: -6rpx;
|
||||
font-size: 2em;
|
||||
line-height: 1;
|
||||
padding: 0.4em 0.6em 0.3em;
|
||||
transform: scale(0.4) translateX(0.5em) translatey(-0.6em);
|
||||
transform-origin: 100% 0;
|
||||
border-radius: 200rpx;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
transform: translate(0, 0);
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
padding-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.ui-swiper) {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
.ui-swiper-dot {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 20rpx;
|
||||
height: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
|
||||
&.default .line-box {
|
||||
display: inline-flex;
|
||||
border-radius: 50rpx;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border: 2px solid transparent;
|
||||
margin: 0 10rpx;
|
||||
opacity: 0.3;
|
||||
position: relative;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&.cur {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
opacity: 1;
|
||||
border: 0px solid transparent;
|
||||
}
|
||||
|
||||
&.cur::after {
|
||||
content: '';
|
||||
border-radius: 50rpx;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&.long .line-box {
|
||||
display: inline-block;
|
||||
border-radius: 100rpx;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
margin: 0 10rpx;
|
||||
opacity: 0.3;
|
||||
position: relative;
|
||||
|
||||
&.cur {
|
||||
width: 24rpx;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.cur::after {}
|
||||
}
|
||||
|
||||
&.line {
|
||||
bottom: 20rpx;
|
||||
|
||||
.line-box {
|
||||
display: inline-block;
|
||||
width: 30px;
|
||||
height: 3px;
|
||||
opacity: 0.3;
|
||||
position: relative;
|
||||
|
||||
&.cur {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.tag {
|
||||
justify-content: flex-end;
|
||||
position: absolute;
|
||||
bottom: 20rpx;
|
||||
right: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,104 +0,0 @@
|
||||
<!-- 装修基础组件:宫格导航 -->
|
||||
<template>
|
||||
<view :style="[bgStyle, { marginLeft: `${data.space}px` }]">
|
||||
<uni-grid :showBorder="Boolean(data.border)" :column="data.column">
|
||||
<uni-grid-item v-for="(item, index) in data.list" :key="index" @tap="sheep.$router.go(item.url)">
|
||||
<view class="grid-item-box ss-flex ss-flex-col ss-row-center ss-col-center">
|
||||
<view class="img-box">
|
||||
<view class="tag-box" v-if="item.badge.show"
|
||||
:style="[{ background: item.badge.bgColor, color: item.badge.textColor }]">
|
||||
{{ item.badge.text }}
|
||||
</view>
|
||||
<image class="menu-image" :src="sheep.$url.cdn(item.iconUrl)"></image>
|
||||
</view>
|
||||
|
||||
<view class="title-box ss-flex ss-flex-col ss-row-center ss-col-center">
|
||||
<view class="grid-text" :style="[{ color: item.titleColor }]">
|
||||
{{ item.title }}
|
||||
</view>
|
||||
<view class="grid-tip" :style="[{ color: item.subtitleColor }]">
|
||||
{{ item.subtitle }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-grid-item>
|
||||
</uni-grid>
|
||||
</view>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import {
|
||||
computed
|
||||
} from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
// 装修数据
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
// 装修样式
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
// 设置背景样式
|
||||
const bgStyle = computed(() => {
|
||||
// 直接从 props.styles 解构
|
||||
const {
|
||||
bgType,
|
||||
bgImg,
|
||||
bgColor
|
||||
} = props.styles;
|
||||
|
||||
// 根据 bgType 返回相应的样式
|
||||
return {
|
||||
background: bgType === 'img' ? `url(${bgImg}) no-repeat top center / 100% 100%` : bgColor
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.menu-image {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.grid-item-box {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
|
||||
.img-box {
|
||||
position: relative;
|
||||
|
||||
.tag-box {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
right: 0;
|
||||
font-size: 2em;
|
||||
line-height: 1;
|
||||
padding: 0.4em 0.6em 0.3em;
|
||||
transform: scale(0.4) translateX(0.5em) translatey(-0.6em);
|
||||
transform-origin: 100% 0;
|
||||
border-radius: 200rpx;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.title-box {
|
||||
.grid-tip {
|
||||
font-size: 24rpx;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,66 +0,0 @@
|
||||
<!-- 装修基础组件:列表导航 -->
|
||||
<template>
|
||||
<view class="menu-list-wrap">
|
||||
<uni-list :border="true">
|
||||
<uni-list-item
|
||||
v-for="(item, index) in data.list"
|
||||
:key="index"
|
||||
showArrow
|
||||
clickable
|
||||
@tap="sheep.$router.go(item.url)"
|
||||
>
|
||||
<template v-slot:header>
|
||||
<view class="ss-flex ss-col-center">
|
||||
<image
|
||||
v-if="item.iconUrl"
|
||||
class="list-icon"
|
||||
:src="sheep.$url.cdn(item.iconUrl)"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<view
|
||||
class="title-text ss-flex ss-row-center ss-col-center ss-m-l-20"
|
||||
:style="[{ color: item.titleColor }]"
|
||||
>
|
||||
{{ item.title }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<view
|
||||
class="notice-text ss-flex ss-row-center ss-col-center"
|
||||
:style="[{ color: item.subtitleColor }]"
|
||||
>
|
||||
{{ item.subtitle }}
|
||||
</view>
|
||||
</template>
|
||||
</uni-list-item>
|
||||
</uni-list>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* cell
|
||||
*/
|
||||
import sheep from '@/sheep';
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.list-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.notice-text {
|
||||
}
|
||||
.menu-list-wrap {
|
||||
::v-deep .uni-list {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,118 +0,0 @@
|
||||
<!-- 全局 - 快捷入口 -->
|
||||
<template>
|
||||
<su-popup :show="show" type="top" round="20" backgroundColor="#F0F0F0" @close="closeMenuTools">
|
||||
<su-status-bar />
|
||||
<view class="tools-wrap ss-m-x-30 ss-m-b-16">
|
||||
<view class="title ss-m-b-34 ss-p-t-20">快捷菜单</view>
|
||||
<view class="container-list ss-flex ss-flex-wrap">
|
||||
<view class="list-item ss-m-b-24" v-for="item in list" :key="item.title">
|
||||
<view class="ss-flex-col ss-col-center">
|
||||
<button
|
||||
class="ss-reset-button list-image ss-flex ss-row-center ss-col-center"
|
||||
@tap="onClick(item)"
|
||||
>
|
||||
<image v-if="show" :src="sheep.$url.static(item.icon)" class="list-icon" />
|
||||
</button>
|
||||
<view class="list-title ss-m-t-20">{{ item.title }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</su-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, computed } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { showMenuTools, closeMenuTools } from '@/sheep/hooks/useModal';
|
||||
|
||||
const show = computed(() => sheep.$store('modal').menu);
|
||||
|
||||
function onClick(item) {
|
||||
closeMenuTools();
|
||||
if (item.url) sheep.$router.go(item.url);
|
||||
}
|
||||
|
||||
const list = [
|
||||
{
|
||||
url: '/pages/index/index',
|
||||
icon: '/static/img/shop/tools/home.png',
|
||||
title: '首页',
|
||||
},
|
||||
{
|
||||
url: '/pages/index/search',
|
||||
icon: '/static/img/shop/tools/search.png',
|
||||
title: '搜索',
|
||||
},
|
||||
{
|
||||
url: '/pages/index/user',
|
||||
icon: '/static/img/shop/tools/user.png',
|
||||
title: '个人中心',
|
||||
},
|
||||
{
|
||||
url: '/pages/index/cart',
|
||||
icon: '/static/img/shop/tools/cart.png',
|
||||
title: '购物车',
|
||||
},
|
||||
{
|
||||
url: '/pages/user/goods-log',
|
||||
icon: '/static/img/shop/tools/browse.png',
|
||||
title: '浏览记录',
|
||||
},
|
||||
{
|
||||
url: '/pages/user/goods-collect',
|
||||
icon: '/static/img/shop/tools/collect.png',
|
||||
title: '我的收藏',
|
||||
},
|
||||
{
|
||||
url: '/pages/chat/index',
|
||||
icon: '/static/img/shop/tools/service.png',
|
||||
title: '客服',
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tools-wrap {
|
||||
// background: #F0F0F0;
|
||||
// box-shadow: 0px 0px 28rpx 7rpx rgba(0, 0, 0, 0.13);
|
||||
// opacity: 0.98;
|
||||
// border-radius: 0 0 20rpx 20rpx;
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
width: calc(25vw - 20rpx);
|
||||
|
||||
.list-image {
|
||||
width: 104rpx;
|
||||
height: 104rpx;
|
||||
border-radius: 52rpx;
|
||||
background: var(--ui-BG);
|
||||
|
||||
.list-icon {
|
||||
width: 54rpx;
|
||||
height: 54rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.list-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.uni-popup {
|
||||
top: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.button-hover) {
|
||||
background: #fafafa !important;
|
||||
}
|
||||
</style>
|
||||
@@ -1,38 +0,0 @@
|
||||
<template>
|
||||
<view class="ss-flex ss-col-center notice-wrap">
|
||||
<image class="icon-img" :src="sheep.$url.cdn(data.iconUrl)" mode="heightFix"></image>
|
||||
<!-- todo:@owen 暂时只支持一个公告 -->
|
||||
<su-notice-bar
|
||||
style="flex: 1"
|
||||
:showIcon="false"
|
||||
scrollable
|
||||
single
|
||||
:text="data.contents[0].text"
|
||||
:speed="50"
|
||||
:color="data.textColor"
|
||||
@tap="sheep.$router.go(data.contents[0].url)"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 装修组件 - 通知栏
|
||||
*
|
||||
*/
|
||||
import sheep from '@/sheep';
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.notice-wrap {
|
||||
.icon-img {
|
||||
height: 56rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,132 +0,0 @@
|
||||
<!-- 装修用户组件:用户订单 -->
|
||||
<template>
|
||||
<view class="ss-order-menu-wrap ss-flex ss-col-center" :style="[style, { marginLeft: `${data.space}px` }]">
|
||||
<view
|
||||
class="menu-item ss-flex-1 ss-flex-col ss-row-center ss-col-center"
|
||||
v-for="item in orderMap"
|
||||
:key="item.title"
|
||||
@tap="sheep.$router.go(item.path, { type: item.value })"
|
||||
>
|
||||
<uni-badge
|
||||
class="uni-badge-left-margin"
|
||||
:text="numData.orderCount[item.count]"
|
||||
absolute="rightTop"
|
||||
size="small"
|
||||
>
|
||||
<image class="item-icon" :src="sheep.$url.static(item.icon)" mode="aspectFit" />
|
||||
</uni-badge>
|
||||
<view class="menu-title ss-m-t-28">{{ item.title }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 装修组件 - 订单菜单组
|
||||
*/
|
||||
import sheep from '@/sheep';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const orderMap = [
|
||||
{
|
||||
title: '待付款',
|
||||
value: '1',
|
||||
icon: '/static/img/shop/order/no_pay.png',
|
||||
path: '/pages/order/list',
|
||||
type: 'unpaid',
|
||||
count: 'unpaidCount',
|
||||
},
|
||||
{
|
||||
title: '待收货',
|
||||
value: '3',
|
||||
icon: '/static/img/shop/order/no_take.png',
|
||||
path: '/pages/order/list',
|
||||
type: 'noget',
|
||||
count: 'deliveredCount',
|
||||
},
|
||||
{
|
||||
title: '待评价',
|
||||
value: '4',
|
||||
icon: '/static/img/shop/order/no_comment.png',
|
||||
path: '/pages/order/list',
|
||||
type: 'nocomment',
|
||||
count: 'uncommentedCount',
|
||||
},
|
||||
{
|
||||
title: '售后单',
|
||||
value: '0',
|
||||
icon: '/static/img/shop/order/change_order.png',
|
||||
path: '/pages/order/aftersale/list',
|
||||
type: 'aftersale',
|
||||
count: 'afterSaleCount',
|
||||
},
|
||||
{
|
||||
title: '全部订单',
|
||||
value: '0',
|
||||
icon: '/static/img/shop/order/all_order.png',
|
||||
path: '/pages/order/list',
|
||||
},
|
||||
];
|
||||
// 接收参数
|
||||
const props = defineProps({
|
||||
// 装修数据
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
// 装修样式
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
// 设置角标
|
||||
const numData = computed(() => sheep.$store('user').numData);
|
||||
// 设置背景样式
|
||||
const style = computed(() => {
|
||||
// 直接从 props.styles 解构
|
||||
const { bgType, bgImg, bgColor } = props.styles;
|
||||
// 根据 bgType 返回相应的样式
|
||||
return {
|
||||
background: bgType === 'img'
|
||||
? `url(${bgImg}) no-repeat top center / 100% 100%`
|
||||
: bgColor
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ss-order-menu-wrap {
|
||||
.menu-item {
|
||||
height: 160rpx;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
.menu-title {
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
color: #333333;
|
||||
}
|
||||
.item-icon {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
}
|
||||
.num-icon {
|
||||
position: absolute;
|
||||
right: 18rpx;
|
||||
top: 18rpx;
|
||||
// width: 40rpx;
|
||||
padding: 0 8rpx;
|
||||
height: 26rpx;
|
||||
background: #ff4d4f;
|
||||
border-radius: 13rpx;
|
||||
color: #fefefe;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.num {
|
||||
font-size: 24rpx;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,328 +0,0 @@
|
||||
<!-- 装修商品组件:【积分商城】商品卡片 -->
|
||||
<template>
|
||||
<!-- 商品卡片 -->
|
||||
<view>
|
||||
<!-- 布局1. 单列大图(上图,下内容)-->
|
||||
<view
|
||||
v-if="layoutType === LayoutTypeEnum.ONE_COL_BIG_IMG && state.spuList.length"
|
||||
class="goods-sl-box"
|
||||
>
|
||||
<view
|
||||
class="goods-box"
|
||||
v-for="item in state.spuList"
|
||||
:key="item.id"
|
||||
:style="[{ marginBottom: data.space * 2 + 'rpx' }]"
|
||||
>
|
||||
<s-goods-column
|
||||
class=""
|
||||
size="sl"
|
||||
:goodsFields="data.fields"
|
||||
:tagStyle="data.badge"
|
||||
:data="item"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:subTitleColor="data.fields.introduction.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
@click="sheep.$router.go('/pages/goods/point', { id: item.activityId })"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 布局2. 单列小图(左图,右内容) -->
|
||||
<view
|
||||
v-if="layoutType === LayoutTypeEnum.ONE_COL_SMALL_IMG && state.spuList.length"
|
||||
class="goods-lg-box"
|
||||
>
|
||||
<view
|
||||
class="goods-box"
|
||||
:style="[{ marginBottom: data.space + 'px' }]"
|
||||
v-for="item in state.spuList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-card"
|
||||
size="lg"
|
||||
:goodsFields="data.fields"
|
||||
:data="item"
|
||||
:tagStyle="data.badge"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:subTitleColor="data.fields.introduction.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
@tap="sheep.$router.go('/pages/goods/point', { id: item.activityId })"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 布局3. 双列(每一列:上图,下内容)-->
|
||||
<view
|
||||
v-if="layoutType === LayoutTypeEnum.TWO_COL && state.spuList.length"
|
||||
class="goods-md-wrap ss-flex ss-flex-wrap ss-col-top"
|
||||
>
|
||||
<view class="goods-list-box">
|
||||
<view
|
||||
class="left-list"
|
||||
:style="[{ paddingRight: data.space + 'rpx', marginBottom: data.space + 'px' }]"
|
||||
v-for="item in state.leftSpuList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-md-box"
|
||||
size="md"
|
||||
:goodsFields="data.fields"
|
||||
:tagStyle="data.badge"
|
||||
:data="item"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:subTitleColor="data.fields.introduction.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
:titleWidth="330 - marginLeft - marginRight"
|
||||
@click="sheep.$router.go('/pages/goods/point', { id: item.activityId })"
|
||||
@getHeight="calculateGoodsColumn($event, 'left')"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
<view class="goods-list-box">
|
||||
<view
|
||||
class="right-list"
|
||||
:style="[{ paddingLeft: data.space + 'rpx', marginBottom: data.space + 'px' }]"
|
||||
v-for="item in state.rightSpuList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-md-box"
|
||||
size="md"
|
||||
:goodsFields="data.fields"
|
||||
:tagStyle="data.badge"
|
||||
:data="item"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:subTitleColor="data.fields.introduction.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
:titleWidth="330 - marginLeft - marginRight"
|
||||
@click="sheep.$router.go('/pages/goods/point', { id: item.activityId })"
|
||||
@getHeight="calculateGoodsColumn($event, 'right')"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 商品卡片
|
||||
*/
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import SpuApi from '@/sheep/api/product/spu';
|
||||
import PointApi from '@/sheep/api/promotion/point';
|
||||
import { PromotionActivityTypeEnum } from '@/sheep/util/const';
|
||||
|
||||
// 布局类型
|
||||
const LayoutTypeEnum = {
|
||||
// 单列大图
|
||||
ONE_COL_BIG_IMG: 'oneColBigImg',
|
||||
// 双列
|
||||
TWO_COL: 'twoCol',
|
||||
// 单列小图
|
||||
ONE_COL_SMALL_IMG: 'oneColSmallImg',
|
||||
};
|
||||
|
||||
const state = reactive({
|
||||
spuList: [],
|
||||
leftSpuList: [],
|
||||
rightSpuList: [],
|
||||
});
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
|
||||
const { layoutType, btnBuy, activityIds } = props.data || {};
|
||||
const { marginLeft, marginRight } = props.styles || {};
|
||||
|
||||
// 购买按钮样式
|
||||
const buyStyle = computed(() => {
|
||||
if (btnBuy.type === 'text') {
|
||||
// 文字按钮:线性渐变背景颜色
|
||||
return {
|
||||
background: `linear-gradient(to right, ${btnBuy.bgBeginColor}, ${btnBuy.bgEndColor})`,
|
||||
};
|
||||
}
|
||||
if (btnBuy.type === 'img') {
|
||||
// 图片按钮
|
||||
return {
|
||||
width: '54rpx',
|
||||
height: '54rpx',
|
||||
background: `url(${sheep.$url.cdn(btnBuy.imgUrl)}) no-repeat`,
|
||||
backgroundSize: '100% 100%',
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
//region 商品瀑布流布局
|
||||
// 下一个要处理的商品索引
|
||||
let count = 0;
|
||||
// 左列的高度
|
||||
let leftHeight = 0;
|
||||
// 右列的高度
|
||||
let rightHeight = 0;
|
||||
|
||||
/**
|
||||
* 计算商品在左列还是右列
|
||||
* @param height 商品的高度
|
||||
* @param where 添加到哪一列
|
||||
*/
|
||||
function calculateGoodsColumn(height = 0, where = 'left') {
|
||||
// 处理完
|
||||
if (!state.spuList[count]) return;
|
||||
// 增加列的高度
|
||||
if (where === 'left') leftHeight += height;
|
||||
if (where === 'right') rightHeight += height;
|
||||
// 添加到矮的一列
|
||||
if (leftHeight <= rightHeight) {
|
||||
state.leftSpuList.push(state.spuList[count]);
|
||||
} else {
|
||||
state.rightSpuList.push(state.spuList[count]);
|
||||
}
|
||||
// 计数
|
||||
count++;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
/**
|
||||
* 根据商品编号列表,获取商品列表
|
||||
* @param ids 商品编号列表
|
||||
* @return {Promise<undefined>} 商品列表
|
||||
*/
|
||||
async function getPointActivityDetailList(ids) {
|
||||
const { data } = await PointApi.getPointActivityListByIds(ids);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据商品编号,获取商品详情
|
||||
* @param ids 商品编号列表
|
||||
* @return {Promise<undefined>} 商品列表
|
||||
*/
|
||||
async function getSpuDetail(ids) {
|
||||
const { data: spu } = await SpuApi.getSpuDetail(ids);
|
||||
return spu;
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
// 加载活动列表
|
||||
const activityList = await getPointActivityDetailList(activityIds.join(','));
|
||||
// 循环获取活动商品SPU详情并添加到spuList
|
||||
for (const activity of activityList) {
|
||||
state.spuList.push(await getSpuDetail(activity.spuId));
|
||||
}
|
||||
|
||||
// 循环活动列表
|
||||
activityList.forEach((activity) => {
|
||||
// 查找对应的 spu 并更新价格
|
||||
const spu = state.spuList.find((spu) => activity.spuId === spu.id);
|
||||
if (spu) {
|
||||
spu.pointStock = activity.stock
|
||||
spu.pointTotalStock = activity.totalStock
|
||||
spu.point = activity.point
|
||||
spu.pointPrice = activity.price
|
||||
// 赋值活动ID,为了点击跳转详情页
|
||||
spu.activityId = activity.id;
|
||||
// 赋值活动类型
|
||||
spu.activityType = PromotionActivityTypeEnum.POINT.type;
|
||||
}
|
||||
});
|
||||
|
||||
// 只有双列布局时需要
|
||||
if (layoutType === LayoutTypeEnum.TWO_COL) {
|
||||
// 分列
|
||||
calculateGoodsColumn();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-md-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.goods-list-box {
|
||||
width: 50%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.left-list {
|
||||
&:nth-last-child(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.right-list {
|
||||
&:nth-last-child(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.goods-box {
|
||||
&:nth-last-of-type(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-md-box,
|
||||
.goods-sl-box,
|
||||
.goods-lg-box {
|
||||
position: relative;
|
||||
|
||||
.cart-btn {
|
||||
position: absolute;
|
||||
bottom: 18rpx;
|
||||
right: 20rpx;
|
||||
z-index: 11;
|
||||
height: 50rpx;
|
||||
line-height: 50rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 25rpx;
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,369 +0,0 @@
|
||||
<!-- 装修商品组件:【积分商城】商品卡片 -->
|
||||
<template>
|
||||
<!-- 商品卡片 -->
|
||||
<view>
|
||||
<!-- 布局1. 单列大图(上图,下内容)-->
|
||||
<view
|
||||
v-if="state.property.layoutType === LayoutTypeEnum.ONE_COL_BIG_IMG && state.spuList.length"
|
||||
class="goods-sl-box"
|
||||
>
|
||||
<view
|
||||
class="goods-box"
|
||||
v-for="item in state.spuList"
|
||||
:key="item.id"
|
||||
:style="[{ marginBottom: state.property.space * 2 + 'rpx' }]"
|
||||
>
|
||||
<s-goods-column
|
||||
class=""
|
||||
size="sl"
|
||||
:goodsFields="state.property.fields"
|
||||
:tagStyle="state.property.badge"
|
||||
:data="item"
|
||||
:titleColor="state.property.fields.name?.color"
|
||||
:subTitleColor="state.property.fields.introduction.color"
|
||||
:topRadius="state.property.borderRadiusTop"
|
||||
:bottomRadius="state.property.borderRadiusBottom"
|
||||
@click="sheep.$router.go('/pages/goods/point', { id: item.activityId })"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ state.property.btnBuy.type === 'text' ? state.property.btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 布局2. 单列小图(左图,右内容) -->
|
||||
<view
|
||||
v-if="state.property.layoutType === LayoutTypeEnum.ONE_COL_SMALL_IMG && state.spuList.length"
|
||||
class="goods-lg-box"
|
||||
>
|
||||
<view
|
||||
class="goods-box"
|
||||
:style="[{ marginBottom: state.property.space + 'px' }]"
|
||||
v-for="item in state.spuList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-card"
|
||||
size="lg"
|
||||
:goodsFields="state.property.fields"
|
||||
:data="item"
|
||||
:tagStyle="state.property.badge"
|
||||
:titleColor="state.property.fields.name?.color"
|
||||
:subTitleColor="state.property.fields.introduction.color"
|
||||
:topRadius="state.property.borderRadiusTop"
|
||||
:bottomRadius="state.property.borderRadiusBottom"
|
||||
@tap="sheep.$router.go('/pages/goods/point', { id: item.activityId })"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ state.property.btnBuy.type === 'text' ? state.property.btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 布局3. 双列(每一列:上图,下内容)-->
|
||||
<view
|
||||
v-if="state.property.layoutType === LayoutTypeEnum.TWO_COL && state.spuList.length"
|
||||
class="goods-md-wrap ss-flex ss-flex-wrap ss-col-top"
|
||||
>
|
||||
<view class="goods-list-box">
|
||||
<view
|
||||
class="left-list"
|
||||
:style="[{ paddingRight: state.property.space + 'rpx', marginBottom: state.property.space + 'px' }]"
|
||||
v-for="item in state.leftSpuList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-md-box"
|
||||
size="md"
|
||||
:goodsFields="state.property.fields"
|
||||
:tagStyle="state.property.badge"
|
||||
:data="item"
|
||||
:titleColor="state.property.fields.name?.color"
|
||||
:subTitleColor="state.property.fields.introduction.color"
|
||||
:topRadius="state.property.borderRadiusTop"
|
||||
:bottomRadius="state.property.borderRadiusBottom"
|
||||
:titleWidth="330 - marginLeft - marginRight"
|
||||
@click="sheep.$router.go('/pages/goods/point', { id: item.activityId })"
|
||||
@getHeight="calculateGoodsColumn($event, 'left')"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ state.property.btnBuy.type === 'text' ? state.property.btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
<view class="goods-list-box">
|
||||
<view
|
||||
class="right-list"
|
||||
:style="[{ paddingLeft: state.property.space + 'rpx', marginBottom: state.property.space + 'px' }]"
|
||||
v-for="item in state.rightSpuList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-md-box"
|
||||
size="md"
|
||||
:goodsFields="state.property.fields"
|
||||
:tagStyle="state.property.badge"
|
||||
:data="item"
|
||||
:titleColor="state.property.fields.name?.color"
|
||||
:subTitleColor="state.property.fields.introduction.color"
|
||||
:topRadius="state.property.borderRadiusTop"
|
||||
:bottomRadius="state.property.borderRadiusBottom"
|
||||
:titleWidth="330 - marginLeft - marginRight"
|
||||
@click="sheep.$router.go('/pages/goods/point', { id: item.activityId })"
|
||||
@getHeight="calculateGoodsColumn($event, 'right')"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ state.property.btnBuy.type === 'text' ? state.property.btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 商品卡片
|
||||
*/
|
||||
import { computed, nextTick, onMounted, reactive, watch } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import SpuApi from '@/sheep/api/product/spu';
|
||||
import { PromotionActivityTypeEnum } from '@/sheep/util/const';
|
||||
import { isEmpty } from '@/sheep/helper/utils';
|
||||
|
||||
// 布局类型
|
||||
const LayoutTypeEnum = {
|
||||
// 单列大图
|
||||
ONE_COL_BIG_IMG: 'oneColBigImg',
|
||||
// 双列
|
||||
TWO_COL: 'twoCol',
|
||||
// 单列小图
|
||||
ONE_COL_SMALL_IMG: 'oneColSmallImg',
|
||||
};
|
||||
|
||||
const state = reactive({
|
||||
spuList: [],
|
||||
leftSpuList: [],
|
||||
rightSpuList: [],
|
||||
property: {
|
||||
'layoutType': 'oneColBigImg',
|
||||
'fields': {
|
||||
'name': {
|
||||
'show': true,
|
||||
'color': '#000',
|
||||
},
|
||||
'introduction': {
|
||||
'show': true,
|
||||
'color': '#999',
|
||||
},
|
||||
'price': {
|
||||
'show': true,
|
||||
'color': '#ff3000',
|
||||
},
|
||||
'marketPrice': {
|
||||
'show': true,
|
||||
'color': '#c4c4c4',
|
||||
},
|
||||
'salesCount': {
|
||||
'show': true,
|
||||
'color': '#c4c4c4',
|
||||
},
|
||||
'stock': {
|
||||
'show': true,
|
||||
'color': '#c4c4c4',
|
||||
},
|
||||
},
|
||||
'badge': {
|
||||
'show': false,
|
||||
'imgUrl': '',
|
||||
},
|
||||
'btnBuy': {
|
||||
'type': 'text',
|
||||
'text': '立即兑换',
|
||||
'bgBeginColor': '#FF6000',
|
||||
'bgEndColor': '#FE832A',
|
||||
'imgUrl': '',
|
||||
},
|
||||
'borderRadiusTop': 8,
|
||||
'borderRadiusBottom': 8,
|
||||
'space': 8,
|
||||
'style': {
|
||||
'bgType': 'color',
|
||||
'bgColor': '',
|
||||
'marginLeft': 8,
|
||||
'marginRight': 8,
|
||||
'marginBottom': 8,
|
||||
},
|
||||
},
|
||||
});
|
||||
const props = defineProps({
|
||||
property: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
// 动态更新 property
|
||||
watch(() => props.property, (newVal) => {
|
||||
state.property = { ...state.property, ...newVal };
|
||||
}, { immediate: true, deep: true });
|
||||
const { marginLeft, marginRight } = state.property.styles || {};
|
||||
|
||||
// 购买按钮样式
|
||||
const buyStyle = computed(() => {
|
||||
if (state.property.btnBuy.type === 'text') {
|
||||
// 文字按钮:线性渐变背景颜色
|
||||
return {
|
||||
background: `linear-gradient(to right, ${state.property.btnBuy.bgBeginColor}, ${state.property.btnBuy.bgEndColor})`,
|
||||
};
|
||||
}
|
||||
if (state.property.btnBuy.type === 'img') {
|
||||
// 图片按钮
|
||||
return {
|
||||
width: '54rpx',
|
||||
height: '54rpx',
|
||||
background: `url(${sheep.$url.cdn(state.property.btnBuy.imgUrl)}) no-repeat`,
|
||||
backgroundSize: '100% 100%',
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
//region 商品瀑布流布局
|
||||
// 下一个要处理的商品索引
|
||||
let count = 0;
|
||||
// 左列的高度
|
||||
let leftHeight = 0;
|
||||
// 右列的高度
|
||||
let rightHeight = 0;
|
||||
|
||||
/**
|
||||
* 计算商品在左列还是右列
|
||||
* @param height 商品的高度
|
||||
* @param where 添加到哪一列
|
||||
*/
|
||||
function calculateGoodsColumn(height = 0, where = 'left') {
|
||||
// 处理完
|
||||
if (!state.spuList[count]) return;
|
||||
// 增加列的高度
|
||||
if (where === 'left') leftHeight += height;
|
||||
if (where === 'right') rightHeight += height;
|
||||
// 添加到矮的一列
|
||||
if (leftHeight <= rightHeight) {
|
||||
state.leftSpuList.push(state.spuList[count]);
|
||||
} else {
|
||||
state.rightSpuList.push(state.spuList[count]);
|
||||
}
|
||||
// 计数
|
||||
count++;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
/**
|
||||
* 根据商品编号,获取商品详情
|
||||
* @param ids 商品编号列表
|
||||
* @return {Promise<undefined>} 商品列表
|
||||
*/
|
||||
async function getSpuDetail(ids) {
|
||||
const { data: spu } = await SpuApi.getSpuDetail(ids);
|
||||
return spu;
|
||||
}
|
||||
|
||||
async function concatActivity(list) {
|
||||
if (isEmpty(list)) {
|
||||
return;
|
||||
}
|
||||
// 循环获取活动商品SPU详情并添加到spuList
|
||||
for (const activity of list) {
|
||||
state.spuList.push(await getSpuDetail(activity.spuId));
|
||||
}
|
||||
|
||||
// 循环活动列表
|
||||
list.forEach((activity) => {
|
||||
// 查找对应的 spu 并更新价格
|
||||
const spu = state.spuList.find((spu) => activity.spuId === spu.id);
|
||||
if (spu) {
|
||||
spu.pointStock = activity.stock;
|
||||
spu.pointTotalStock = activity.totalStock;
|
||||
spu.point = activity.point;
|
||||
spu.pointPrice = activity.price;
|
||||
// 赋值活动ID,为了点击跳转详情页
|
||||
spu.activityId = activity.id;
|
||||
// 赋值活动类型
|
||||
spu.activityType = PromotionActivityTypeEnum.POINT.type;
|
||||
}
|
||||
});
|
||||
// 只有双列布局时需要
|
||||
if (state.property.layoutType === LayoutTypeEnum.TWO_COL) {
|
||||
// 分列
|
||||
calculateGoodsColumn();
|
||||
}
|
||||
}
|
||||
function getActivityCount() {
|
||||
return state.spuList.length;
|
||||
}
|
||||
defineExpose({ concatActivity,getActivityCount,calculateGoodsColumn });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-md-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.goods-list-box {
|
||||
width: 50%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.left-list {
|
||||
&:nth-last-child(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.right-list {
|
||||
&:nth-last-child(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.goods-box {
|
||||
&:nth-last-of-type(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-md-box,
|
||||
.goods-sl-box,
|
||||
.goods-lg-box {
|
||||
position: relative;
|
||||
|
||||
.cart-btn {
|
||||
position: absolute;
|
||||
bottom: 18rpx;
|
||||
right: 20rpx;
|
||||
z-index: 11;
|
||||
height: 50rpx;
|
||||
line-height: 50rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 25rpx;
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,85 +0,0 @@
|
||||
<template>
|
||||
<view>
|
||||
<view v-for="(item, index) in popupList" :key="index">
|
||||
<su-popup
|
||||
v-if="index === currentIndex"
|
||||
:show="item.isShow"
|
||||
type="center"
|
||||
backgroundColor="none"
|
||||
round="0"
|
||||
:showClose="true"
|
||||
:isMaskClick="false"
|
||||
@close="onClose(index)"
|
||||
>
|
||||
<view class="img-box">
|
||||
<image
|
||||
class="modal-img"
|
||||
:src="sheep.$url.cdn(item.imgUrl)"
|
||||
mode="widthFix"
|
||||
@tap.stop="onPopup(item.url)"
|
||||
/>
|
||||
</view>
|
||||
</su-popup>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { computed, ref } from 'vue';
|
||||
import { saveAdvHistory } from '@/sheep/hooks/useModal';
|
||||
|
||||
// 定义属性
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default() {},
|
||||
}
|
||||
})
|
||||
|
||||
// const modalStore = sheep.$store('modal');
|
||||
const modalStore = JSON.parse(uni.getStorageSync('modal-store') || '{}');
|
||||
console.log(modalStore)
|
||||
const advHistory = modalStore.advHistory || [];
|
||||
const currentIndex = ref(0);
|
||||
const popupList = computed(() => {
|
||||
const list = props.data.list || [];
|
||||
const newList = [];
|
||||
if (list.length > 0) {
|
||||
list.forEach((adv) => {
|
||||
if (adv.showType === 'once' && advHistory.includes(adv.imgUrl)) {
|
||||
adv.isShow = false;
|
||||
} else {
|
||||
adv.isShow = true;
|
||||
newList.push(adv);
|
||||
}
|
||||
|
||||
// 记录弹窗已显示过
|
||||
saveAdvHistory(adv);
|
||||
});
|
||||
}
|
||||
return newList;
|
||||
});
|
||||
|
||||
// 跳转链接
|
||||
function onPopup(path) {
|
||||
sheep.$router.go(path);
|
||||
}
|
||||
|
||||
// 关闭
|
||||
function onClose(index) {
|
||||
currentIndex.value = index + 1;
|
||||
popupList.value[index].isShow = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.img-box {
|
||||
width: 610rpx;
|
||||
// height: 800rpx;
|
||||
}
|
||||
.modal-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,40 +0,0 @@
|
||||
<!-- 装修营销组件:营销文章 -->
|
||||
<template>
|
||||
<view
|
||||
:style="[
|
||||
{
|
||||
marginLeft: styles.marginLeft + 'px',
|
||||
marginRight: styles.marginRight + 'px',
|
||||
marginBottom: styles.marginBottom + 'px',
|
||||
marginTop: styles.marginTop + 'px',
|
||||
padding: styles.padding + 'px',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<mp-html class="richtext" :content="state.content"></mp-html>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive, onMounted } from 'vue';
|
||||
import ArticleApi from '@/sheep/api/promotion/article';
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
content: '',
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
const { data } = await ArticleApi.getArticle(props.data.id);
|
||||
state.content = data.content;
|
||||
});
|
||||
</script>
|
||||
@@ -1,164 +0,0 @@
|
||||
<template>
|
||||
<view
|
||||
class="search-content ss-flex ss-col-center ss-row-between"
|
||||
@tap="click"
|
||||
:style="[
|
||||
{
|
||||
borderRadius: radius + 'px',
|
||||
background: elBackground,
|
||||
height: height + 'px',
|
||||
width: width,
|
||||
},
|
||||
]"
|
||||
:class="[{ 'border-content': navbar }]"
|
||||
>
|
||||
<view class="ss-flex ss-col-center" v-if="navbar">
|
||||
<view class="search-icon _icon-search ss-m-l-10" :style="[{ color: props.iconColor }]"></view>
|
||||
<view class="search-input ss-flex-1 ss-line-1" :style="[{ color: fontColor, width: width }]">
|
||||
{{ placeholder }}
|
||||
</view>
|
||||
</view>
|
||||
<uni-search-bar
|
||||
v-if="!navbar"
|
||||
class="ss-flex-1"
|
||||
:radius="data.borderRadius"
|
||||
:placeholder="data.placeholder"
|
||||
cancelButton="none"
|
||||
clearButton="none"
|
||||
@confirm="onSearch"
|
||||
v-model="state.searchVal"
|
||||
/>
|
||||
<view class="keyword-link ss-flex">
|
||||
<view v-for="(item, index) in data.hotKeywords" :key="index">
|
||||
<view
|
||||
class="ss-m-r-16"
|
||||
:style="[{ color: data.textColor }]"
|
||||
@tap.stop="sheep.$router.go('/pages/goods/list', { keyword: item })"
|
||||
>{{ item }}</view
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="data.hotKeywords && data.hotKeywords.length && navbar" class="ss-flex">
|
||||
<button
|
||||
class="ss-reset-button keyword-btn"
|
||||
v-for="(item, index) in data.hotKeywords"
|
||||
:key="index"
|
||||
:style="[{ color: data.textColor, marginRight: '10rpx' }]"
|
||||
>
|
||||
{{ item }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 基础组件 - 搜索栏
|
||||
*
|
||||
* @property {String} elBackground - 输入框背景色
|
||||
* @property {String} iconColor - 图标颜色
|
||||
* @property {String} fontColor - 字体颜色
|
||||
* @property {Number} placeholder - 默认placeholder
|
||||
* @property {Number} topRadius - 组件上圆角
|
||||
* @property {Number} bottomRadius - 组件下圆角
|
||||
*
|
||||
* @slot keywords - 关键字
|
||||
* @event {Function} click - 点击组件时触发
|
||||
*/
|
||||
|
||||
import { computed, reactive } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
// 组件数据
|
||||
const state = reactive({
|
||||
searchVal: '',
|
||||
});
|
||||
|
||||
// 事件页面
|
||||
const emits = defineEmits(['click']);
|
||||
|
||||
// 接收参数
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
// 输入框背景色
|
||||
elBackground: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 36,
|
||||
},
|
||||
// 图标颜色
|
||||
iconColor: {
|
||||
type: String,
|
||||
default: '#b0b3bf',
|
||||
},
|
||||
// 字体颜色
|
||||
fontColor: {
|
||||
type: String,
|
||||
default: '#b0b3bf',
|
||||
},
|
||||
// placeholder
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '这是一个搜索框',
|
||||
},
|
||||
radius: {
|
||||
type: Number,
|
||||
default: 10,
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%',
|
||||
},
|
||||
navbar: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
// 点击
|
||||
const click = () => {
|
||||
emits('click');
|
||||
};
|
||||
|
||||
function onSearch(e) {
|
||||
if (e.value) {
|
||||
sheep.$router.go('/pages/goods/list', { keyword: e.value });
|
||||
setTimeout(() => {
|
||||
state.searchVal = '';
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.border-content {
|
||||
border: 2rpx solid #eee;
|
||||
}
|
||||
|
||||
.search-content {
|
||||
flex: 1;
|
||||
// height: 80rpx;
|
||||
position: relative;
|
||||
|
||||
.search-icon {
|
||||
font-size: 38rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.keyword-link {
|
||||
position: absolute;
|
||||
right: 16rpx;
|
||||
top: 18rpx;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,328 +0,0 @@
|
||||
<!-- 装修商品组件:【秒杀】商品卡片 -->
|
||||
<template>
|
||||
<!-- 商品卡片 -->
|
||||
<view>
|
||||
<!-- 布局1. 单列大图(上图,下内容)-->
|
||||
<view
|
||||
v-if="layoutType === LayoutTypeEnum.ONE_COL_BIG_IMG && state.spuList.length"
|
||||
class="goods-sl-box"
|
||||
>
|
||||
<view
|
||||
class="goods-box"
|
||||
v-for="item in state.spuList"
|
||||
:key="item.id"
|
||||
:style="[{ marginBottom: data.space * 2 + 'rpx' }]"
|
||||
>
|
||||
<s-goods-column
|
||||
class=""
|
||||
size="sl"
|
||||
:goodsFields="data.fields"
|
||||
:tagStyle="data.badge"
|
||||
:data="item"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:subTitleColor="data.fields.introduction.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
@click="sheep.$router.go('/pages/goods/seckill', { id: item.activityId })"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 布局2. 单列小图(左图,右内容) -->
|
||||
<view
|
||||
v-if="layoutType === LayoutTypeEnum.ONE_COL_SMALL_IMG && state.spuList.length"
|
||||
class="goods-lg-box"
|
||||
>
|
||||
<view
|
||||
class="goods-box"
|
||||
:style="[{ marginBottom: data.space + 'px' }]"
|
||||
v-for="item in state.spuList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-card"
|
||||
size="lg"
|
||||
:goodsFields="data.fields"
|
||||
:data="item"
|
||||
:tagStyle="data.badge"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:subTitleColor="data.fields.introduction.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
@tap="sheep.$router.go('/pages/goods/seckill', { id: item.activityId })"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 布局3. 双列(每一列:上图,下内容)-->
|
||||
<view
|
||||
v-if="layoutType === LayoutTypeEnum.TWO_COL && state.spuList.length"
|
||||
class="goods-md-wrap ss-flex ss-flex-wrap ss-col-top"
|
||||
>
|
||||
<view class="goods-list-box">
|
||||
<view
|
||||
class="left-list"
|
||||
:style="[{ paddingRight: data.space + 'rpx', marginBottom: data.space + 'px' }]"
|
||||
v-for="item in state.leftSpuList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-md-box"
|
||||
size="md"
|
||||
:goodsFields="data.fields"
|
||||
:tagStyle="data.badge"
|
||||
:data="item"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:subTitleColor="data.fields.introduction.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
:titleWidth="330 - marginLeft - marginRight"
|
||||
@click="sheep.$router.go('/pages/goods/seckill', { id: item.activityId })"
|
||||
@getHeight="calculateGoodsColumn($event, 'left')"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
<view class="goods-list-box">
|
||||
<view
|
||||
class="right-list"
|
||||
:style="[{ paddingLeft: data.space + 'rpx', marginBottom: data.space + 'px' }]"
|
||||
v-for="item in state.rightSpuList"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-goods-column
|
||||
class="goods-md-box"
|
||||
size="md"
|
||||
:goodsFields="data.fields"
|
||||
:tagStyle="data.badge"
|
||||
:data="item"
|
||||
:titleColor="data.fields.name?.color"
|
||||
:subTitleColor="data.fields.introduction.color"
|
||||
:topRadius="data.borderRadiusTop"
|
||||
:bottomRadius="data.borderRadiusBottom"
|
||||
:titleWidth="330 - marginLeft - marginRight"
|
||||
@click="sheep.$router.go('/pages/goods/seckill', { id: item.activityId })"
|
||||
@getHeight="calculateGoodsColumn($event, 'right')"
|
||||
>
|
||||
<!-- 购买按钮 -->
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
|
||||
{{ btnBuy.type === 'text' ? btnBuy.text : '' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 商品卡片
|
||||
*/
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import SeckillApi from '@/sheep/api/promotion/seckill';
|
||||
import SpuApi from '@/sheep/api/product/spu';
|
||||
|
||||
// 布局类型
|
||||
const LayoutTypeEnum = {
|
||||
// 单列大图
|
||||
ONE_COL_BIG_IMG: 'oneColBigImg',
|
||||
// 双列
|
||||
TWO_COL: 'twoCol',
|
||||
// 单列小图
|
||||
ONE_COL_SMALL_IMG: 'oneColSmallImg',
|
||||
};
|
||||
|
||||
const state = reactive({
|
||||
spuList: [],
|
||||
leftSpuList: [],
|
||||
rightSpuList: [],
|
||||
});
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
|
||||
const { layoutType, btnBuy, activityIds } = props.data || {};
|
||||
const { marginLeft, marginRight } = props.styles || {};
|
||||
|
||||
// 购买按钮样式
|
||||
const buyStyle = computed(() => {
|
||||
if (btnBuy.type === 'text') {
|
||||
// 文字按钮:线性渐变背景颜色
|
||||
return {
|
||||
background: `linear-gradient(to right, ${btnBuy.bgBeginColor}, ${btnBuy.bgEndColor})`,
|
||||
};
|
||||
}
|
||||
if (btnBuy.type === 'img') {
|
||||
// 图片按钮
|
||||
return {
|
||||
width: '54rpx',
|
||||
height: '54rpx',
|
||||
background: `url(${sheep.$url.cdn(btnBuy.imgUrl)}) no-repeat`,
|
||||
backgroundSize: '100% 100%',
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
//region 商品瀑布流布局
|
||||
// 下一个要处理的商品索引
|
||||
let count = 0;
|
||||
// 左列的高度
|
||||
let leftHeight = 0;
|
||||
// 右列的高度
|
||||
let rightHeight = 0;
|
||||
|
||||
/**
|
||||
* 计算商品在左列还是右列
|
||||
* @param height 商品的高度
|
||||
* @param where 添加到哪一列
|
||||
*/
|
||||
function calculateGoodsColumn(height = 0, where = 'left') {
|
||||
// 处理完
|
||||
if (!state.spuList[count]) return;
|
||||
// 增加列的高度
|
||||
if (where === 'left') leftHeight += height;
|
||||
if (where === 'right') rightHeight += height;
|
||||
// 添加到矮的一列
|
||||
if (leftHeight <= rightHeight) {
|
||||
state.leftSpuList.push(state.spuList[count]);
|
||||
} else {
|
||||
state.rightSpuList.push(state.spuList[count]);
|
||||
}
|
||||
// 计数
|
||||
count++;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
/**
|
||||
* 根据商品编号列表,获取商品列表
|
||||
* @param ids 商品编号列表
|
||||
* @return {Promise<undefined>} 商品列表
|
||||
*/
|
||||
async function getSeckillActivityDetailList(ids) {
|
||||
const { data } = await SeckillApi.getSeckillActivityListByIds(ids);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据商品编号,获取商品详情
|
||||
* @param ids 商品编号列表
|
||||
* @return {Promise<undefined>} 商品列表
|
||||
*/
|
||||
async function getSpuDetail(ids) {
|
||||
const { data: spu } = await SpuApi.getSpuDetail(ids);
|
||||
return spu;
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
// 加载活动列表
|
||||
const activityList = await getSeckillActivityDetailList(activityIds.join(','));
|
||||
// 循环获取活动商品SPU详情并添加到spuList
|
||||
for (const activity of activityList) {
|
||||
state.spuList.push(await getSpuDetail(activity.spuId));
|
||||
}
|
||||
|
||||
// 循环活动列表
|
||||
activityList.forEach((activity) => {
|
||||
// 提取活动价格
|
||||
const seckillPrice = activity.seckillPrice || Infinity;
|
||||
// 查找对应的 spu 并更新价格
|
||||
const spu = state.spuList.find((spu) => activity.spuId === spu.id);
|
||||
if (spu) {
|
||||
// 赋值活动名称
|
||||
// TODO 芋艿:暂定活动名。会在调研一些类似有赞、淘宝、京东的选择
|
||||
spu.name = activity.name;
|
||||
// 赋值最低价格
|
||||
spu.price = Math.min(seckillPrice, spu.price);
|
||||
// 赋值活动ID,为了点击跳转详情页
|
||||
spu.activityId = activity.id;
|
||||
}
|
||||
});
|
||||
|
||||
// 只有双列布局时需要
|
||||
if (layoutType === LayoutTypeEnum.TWO_COL) {
|
||||
// 分列
|
||||
calculateGoodsColumn();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-md-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.goods-list-box {
|
||||
width: 50%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.left-list {
|
||||
&:nth-last-child(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.right-list {
|
||||
&:nth-last-child(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.goods-box {
|
||||
&:nth-last-of-type(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-md-box,
|
||||
.goods-sl-box,
|
||||
.goods-lg-box {
|
||||
position: relative;
|
||||
|
||||
.cart-btn {
|
||||
position: absolute;
|
||||
bottom: 18rpx;
|
||||
right: 20rpx;
|
||||
z-index: 11;
|
||||
height: 50rpx;
|
||||
line-height: 50rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 25rpx;
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,508 +0,0 @@
|
||||
<template>
|
||||
<!-- 拼团商品规格弹窗 -->
|
||||
<su-popup :show="show" round="10" @close="emits('close')">
|
||||
<!-- SKU 信息 -->
|
||||
<view class="ss-modal-box bg-white ss-flex-col">
|
||||
<view class="modal-header ss-flex ss-col-center">
|
||||
<view class="header-left ss-m-r-30">
|
||||
<image
|
||||
class="sku-image"
|
||||
:src="sheep.$url.cdn(state.selectedSku.picUrl || goodsInfo.picUrl)"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</view>
|
||||
<view class="header-right ss-flex-col ss-row-between ss-flex-1">
|
||||
<view class="goods-title ss-line-2">
|
||||
<view class="tig ss-flex ss-col-center">
|
||||
<view class="tig-icon ss-flex ss-col-center ss-row-center">
|
||||
<view class="groupon-tag">
|
||||
<image :src="sheep.$url.static('/static/img/shop/goods/groupon-tag-white.png')" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="tig-title">拼团价</view>
|
||||
</view>
|
||||
<view class="info-title">
|
||||
{{ goodsInfo.name }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="header-right-bottom ss-flex ss-col-center ss-row-between">
|
||||
<view class="price-text">
|
||||
{{
|
||||
fen2yuan(
|
||||
state.selectedSku.price || goodsInfo.price || state.selectedSku.marketPrice,
|
||||
)
|
||||
}}</view
|
||||
>
|
||||
|
||||
<view class="stock-text ss-m-l-20">
|
||||
库存{{ state.selectedSku.stock || goodsInfo.stock }}件
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="modal-content ss-flex-1">
|
||||
<scroll-view scroll-y="true" class="modal-content-scroll">
|
||||
<view class="sku-item ss-m-b-20" v-for="property in propertyList" :key="property.id">
|
||||
<view class="label-text ss-m-b-20">{{ property.name }}</view>
|
||||
<view class="ss-flex ss-col-center ss-flex-wrap">
|
||||
<button
|
||||
class="ss-reset-button spec-btn"
|
||||
v-for="value in property.values"
|
||||
:class="[
|
||||
{
|
||||
'checked-btn': state.currentPropertyArray[property.id] === value.id,
|
||||
},
|
||||
{
|
||||
'disabled-btn': value.disabled === true,
|
||||
},
|
||||
]"
|
||||
:key="value.id"
|
||||
:disabled="value.disabled === true"
|
||||
@tap="onSelectSku(property.id, value.id)"
|
||||
>
|
||||
{{ value.name }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="buy-num-box ss-flex ss-col-center ss-row-between">
|
||||
<view class="label-text">购买数量</view>
|
||||
<su-number-box
|
||||
:min="1"
|
||||
:max="state.selectedSku.stock"
|
||||
:step="1"
|
||||
v-model="state.selectedSku.count"
|
||||
@change="onNumberChange($event)"
|
||||
activity="groupon"
|
||||
/>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 操作区 -->
|
||||
<view class="modal-footer ss-p-y-20">
|
||||
<view class="buy-box ss-flex ss-col-center ss-flex ss-col-center ss-row-center">
|
||||
<view class="ss-flex">
|
||||
<button class="ss-reset-button origin-price-btn ss-flex-col">
|
||||
<view class="btn-title">{{ grouponNum + '人团' }}</view>
|
||||
</button>
|
||||
<button class="ss-reset-button btn-tox ss-flex-col" @tap="onBuy">
|
||||
<view class="btn-price">
|
||||
{{
|
||||
fen2yuan(
|
||||
state.selectedSku.price * state.selectedSku.count ||
|
||||
goodsInfo.price * state.selectedSku.count ||
|
||||
state.selectedSku.marketPrice * state.selectedSku.count ||
|
||||
goodsInfo.price,
|
||||
)
|
||||
}}
|
||||
</view>
|
||||
<view v-if="grouponAction === 'create'">立即开团</view>
|
||||
<view v-else-if="grouponAction === 'join'">参与拼团</view>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</su-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive, watch } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { convertProductPropertyList, fen2yuan } from '@/sheep/hooks/useGoods';
|
||||
|
||||
const headerBg = sheep.$url.css('/static/img/shop/goods/groupon-btn-long.png');
|
||||
const emits = defineEmits(['change', 'addCart', 'buy', 'close', 'ladder']);
|
||||
const props = defineProps({
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
goodsInfo: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
grouponAction: {
|
||||
type: String,
|
||||
default: 'create',
|
||||
},
|
||||
grouponNum: {
|
||||
type: [Number, String],
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
const state = reactive({
|
||||
selectedSku: {}, // 选中的 SKU
|
||||
currentPropertyArray: [], // 当前选中的属性,实际是个 Map。key 是 property 编号,value 是 value 编号
|
||||
grouponNum: props.grouponNum,
|
||||
});
|
||||
|
||||
const propertyList = convertProductPropertyList(props.goodsInfo.skus);
|
||||
|
||||
// SKU 列表
|
||||
const skuList = computed(() => {
|
||||
let skuPrices = props.goodsInfo.skus;
|
||||
for (let price of skuPrices) {
|
||||
price.value_id_array = price.properties.map((item) => item.valueId);
|
||||
}
|
||||
return skuPrices;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => state.selectedSku,
|
||||
(newVal) => {
|
||||
emits('change', newVal);
|
||||
},
|
||||
{
|
||||
immediate: true, // 立即执行
|
||||
deep: true, // 深度监听
|
||||
},
|
||||
);
|
||||
|
||||
// 输入框改变数量
|
||||
function onNumberChange(e) {
|
||||
if (e === 0) return;
|
||||
if (state.selectedSku.count === e) return;
|
||||
state.selectedSku.count = e;
|
||||
}
|
||||
|
||||
// 点击购买
|
||||
function onBuy() {
|
||||
if (!state.selectedSku.id || state.selectedSku.id <= 0) {
|
||||
sheep.$helper.toast('请选择规格');
|
||||
return;
|
||||
}
|
||||
if (state.selectedSku.stock <= 0) {
|
||||
sheep.$helper.toast('库存不足');
|
||||
return;
|
||||
}
|
||||
emits('buy', state.selectedSku);
|
||||
}
|
||||
|
||||
// 改变禁用状态:计算每个 property 属性值的按钮,是否禁用
|
||||
function changeDisabled(isChecked = false, propertyId = 0, valueId = 0) {
|
||||
let newSkus = []; // 所有可以选择的 sku 数组
|
||||
if (isChecked) {
|
||||
// 情况一:选中 property
|
||||
// 获得当前点击选中 property 的、所有可用 SKU
|
||||
for (let price of skuList.value) {
|
||||
if (price.stock <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (price.value_id_array.indexOf(valueId) >= 0) {
|
||||
newSkus.push(price);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 情况二:取消选中 property
|
||||
// 当前所选 property 下,所有可以选择的 SKU
|
||||
newSkus = getCanUseSkuList();
|
||||
}
|
||||
|
||||
// 所有存在并且有库存未选择的 SKU 的 value 属性值 id
|
||||
let noChooseValueIds = [];
|
||||
for (let price of newSkus) {
|
||||
noChooseValueIds = noChooseValueIds.concat(price.value_id_array);
|
||||
}
|
||||
noChooseValueIds = Array.from(new Set(noChooseValueIds)); // 去重
|
||||
|
||||
if (isChecked) {
|
||||
// 去除当前选中的 value 属性值 id
|
||||
let index = noChooseValueIds.indexOf(valueId);
|
||||
noChooseValueIds.splice(index, 1);
|
||||
} else {
|
||||
// 循环去除当前已选择的 value 属性值 id
|
||||
state.currentPropertyArray.forEach((currentPropertyId) => {
|
||||
if (currentPropertyId.toString() !== '') {
|
||||
return;
|
||||
}
|
||||
// currentPropertyId 为空是反选 填充的
|
||||
let index = noChooseValueIds.indexOf(currentPropertyId);
|
||||
if (index >= 0) {
|
||||
// currentPropertyId 存在于 noChooseValueIds
|
||||
noChooseValueIds.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 当前已选择的 property 数组
|
||||
let choosePropertyIds = [];
|
||||
if (!isChecked) {
|
||||
// 当前已选择的 property
|
||||
state.currentPropertyArray.forEach((currentPropertyId, currentValueId) => {
|
||||
if (currentPropertyId !== '') {
|
||||
// currentPropertyId 为空是反选 填充的
|
||||
choosePropertyIds.push(currentValueId);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 当前点击选择的 property
|
||||
choosePropertyIds = [propertyId];
|
||||
}
|
||||
|
||||
for (let propertyIndex in propertyList) {
|
||||
// 当前点击的 property、或者取消选择时候,已选中的 property 不进行处理
|
||||
if (choosePropertyIds.indexOf(propertyList[propertyIndex]['id']) >= 0) {
|
||||
continue;
|
||||
}
|
||||
// 如果当前 property id 不存在于有库存的 SKU 中,则禁用
|
||||
for (let valueIndex in propertyList[propertyIndex]['values']) {
|
||||
propertyList[propertyIndex]['values'][valueIndex]['disabled'] =
|
||||
noChooseValueIds.indexOf(propertyList[propertyIndex]['values'][valueIndex]['id']) < 0; // true 禁用 or false 不禁用
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 当前所选属性下,获取所有有库存的 SKU 们
|
||||
function getCanUseSkuList() {
|
||||
let newSkus = [];
|
||||
for (let sku of skuList.value) {
|
||||
if (sku.stock <= 0) {
|
||||
continue;
|
||||
}
|
||||
let isOk = true;
|
||||
state.currentPropertyArray.forEach((propertyId) => {
|
||||
// propertyId 不为空,并且,这个 条 sku 没有被选中,则排除
|
||||
if (propertyId.toString() !== '' && sku.value_id_array.indexOf(propertyId) < 0) {
|
||||
isOk = false;
|
||||
}
|
||||
});
|
||||
if (isOk) {
|
||||
newSkus.push(sku);
|
||||
}
|
||||
}
|
||||
return newSkus;
|
||||
}
|
||||
|
||||
// 选择规格
|
||||
function onSelectSku(propertyId, valueId) {
|
||||
// 清空已选择
|
||||
let isChecked = true; // 选中 or 取消选中
|
||||
if (
|
||||
state.currentPropertyArray[propertyId] !== undefined &&
|
||||
state.currentPropertyArray[propertyId] === valueId
|
||||
) {
|
||||
// 点击已被选中的,删除并填充 ''
|
||||
isChecked = false;
|
||||
state.currentPropertyArray.splice(propertyId, 1, '');
|
||||
} else {
|
||||
// 选中
|
||||
state.currentPropertyArray[propertyId] = valueId;
|
||||
}
|
||||
|
||||
// 选中的 property 大类
|
||||
let choosePropertyId = [];
|
||||
state.currentPropertyArray.forEach((currentPropertyId) => {
|
||||
if (currentPropertyId !== '') {
|
||||
// currentPropertyId 为空是反选 填充的
|
||||
choosePropertyId.push(currentPropertyId);
|
||||
}
|
||||
});
|
||||
|
||||
// 当前所选 property 下,所有可以选择的 SKU 们
|
||||
let newSkuList = getCanUseSkuList();
|
||||
|
||||
// 判断所有 property 大类是否选择完成
|
||||
if (choosePropertyId.length === propertyList.length && newSkuList.length) {
|
||||
newSkuList[0].count = state.selectedSku.count || 1;
|
||||
state.selectedSku = newSkuList[0];
|
||||
} else {
|
||||
state.selectedSku = {};
|
||||
}
|
||||
|
||||
// 改变 property 禁用状态
|
||||
changeDisabled(isChecked, propertyId, valueId);
|
||||
}
|
||||
|
||||
changeDisabled(false);
|
||||
// TODO 芋艿:待讨论的优化点:1)单规格,要不要默认选中;2)默认要不要选中第一个规格
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 购买
|
||||
.buy-btn {
|
||||
margin: 0 20rpx;
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
background: linear-gradient(90deg, #ff6000, #fe832a);
|
||||
color: #fff;
|
||||
}
|
||||
.btn-tox {
|
||||
width: 382rpx;
|
||||
height: 80rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
margin-left: -50rpx;
|
||||
background-image: v-bind(headerBg);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
color: #ffffff;
|
||||
line-height: normal;
|
||||
border-radius: 0px 40rpx 40rpx 0px;
|
||||
|
||||
.btn-price {
|
||||
font-family: OPPOSANS;
|
||||
|
||||
&::before {
|
||||
content: '¥';
|
||||
}
|
||||
}
|
||||
}
|
||||
.origin-price-btn {
|
||||
width: 370rpx;
|
||||
height: 80rpx;
|
||||
background: rgba(#ff5651, 0.1);
|
||||
color: #ff6000;
|
||||
border-radius: 40rpx 0px 0px 40rpx;
|
||||
line-height: normal;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
|
||||
.btn-price {
|
||||
font-family: OPPOSANS;
|
||||
|
||||
&::before {
|
||||
content: '¥';
|
||||
}
|
||||
}
|
||||
|
||||
.btn-title {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.ss-modal-box {
|
||||
border-radius: 30rpx 30rpx 0 0;
|
||||
max-height: 1000rpx;
|
||||
|
||||
.modal-header {
|
||||
position: relative;
|
||||
padding: 80rpx 20rpx 40rpx;
|
||||
|
||||
.sku-image {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
position: absolute;
|
||||
top: 10rpx;
|
||||
right: 20rpx;
|
||||
font-size: 46rpx;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.goods-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
line-height: 42rpx;
|
||||
position: relative;
|
||||
.tig {
|
||||
border: 2rpx solid #ff6000;
|
||||
border-radius: 4rpx;
|
||||
width: 126rpx;
|
||||
height: 38rpx;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
.tig-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background: #ff6000;
|
||||
margin-left: -2rpx;
|
||||
border-radius: 4rpx 0 0 4rpx;
|
||||
|
||||
.groupon-tag {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.tig-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
line-height: normal;
|
||||
color: #ff6000;
|
||||
width: 86rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
.info-title {
|
||||
text-indent: 132rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.price-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: $red;
|
||||
font-family: OPPOSANS;
|
||||
|
||||
&::before {
|
||||
content: '¥';
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.stock-text {
|
||||
font-size: 26rpx;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 0 20rpx;
|
||||
|
||||
.modal-content-scroll {
|
||||
max-height: 600rpx;
|
||||
|
||||
.label-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.buy-num-box {
|
||||
height: 100rpx;
|
||||
}
|
||||
|
||||
.spec-btn {
|
||||
height: 60rpx;
|
||||
min-width: 100rpx;
|
||||
padding: 0 30rpx;
|
||||
background: #f4f4f4;
|
||||
border-radius: 30rpx;
|
||||
color: #434343;
|
||||
font-size: 26rpx;
|
||||
margin-right: 10rpx;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.checked-btn {
|
||||
background: linear-gradient(90deg, #ff6000, #fe832a);
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.disabled-btn {
|
||||
font-weight: 400;
|
||||
color: #c6c6c6;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
Loaded 100 of 622 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user