feat: 初始化项目配置和样式- 添加项目环境变量配置文件 (.env)
- 创建代码忽略文件列表 (.gitignore) - 设置代码格式化规则 (.prettierrc)- 添加各种样式文件,包括背景、边框、按钮、卡片等组件样式 - 创建暗黑主题样式文件 - 添加表单和代码样式文件
This commit is contained in:
@@ -0,0 +1,315 @@
|
||||
<template>
|
||||
<s-layout :bgStyle="{ color: '#fff' }" tabbar="/pages/index/cart" title="购物车">
|
||||
<s-empty
|
||||
v-if="state.list.length === 0"
|
||||
icon="/static/cart-empty.png"
|
||||
text="购物车空空如也,快去逛逛吧~"
|
||||
/>
|
||||
|
||||
<!-- 头部 -->
|
||||
<view v-if="state.list.length" class="cart-box ss-flex ss-flex-col ss-row-between">
|
||||
<view class="cart-header ss-flex ss-col-center ss-row-between ss-p-x-30">
|
||||
<view class="header-left ss-flex ss-col-center ss-font-26">
|
||||
共
|
||||
<text class="goods-number ui-TC-Main ss-flex">{{ state.list.length }}</text>
|
||||
件商品
|
||||
</view>
|
||||
<view class="header-right">
|
||||
<button v-if="state.editMode" class="ss-reset-button" @tap="onChangeEditMode(false)">
|
||||
取消
|
||||
</button>
|
||||
<button v-else class="ss-reset-button ui-TC-Main" @tap="onChangeEditMode(true)">
|
||||
编辑
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 内容 -->
|
||||
<view class="cart-content ss-flex-1 ss-p-x-30 ss-m-b-40">
|
||||
<view v-for="item in state.list" :key="item.id" class="goods-box ss-r-10 ss-m-b-14">
|
||||
<view class="ss-flex ss-col-center">
|
||||
<label class="check-box ss-flex ss-col-center ss-p-l-10" @tap="onSelectSingle(item.id)">
|
||||
<radio
|
||||
:checked="state.selectedIds.includes(item.id)"
|
||||
color="var(--ui-BG-Main)"
|
||||
style="transform: scale(0.8)"
|
||||
@tap.stop="onSelectSingle(item.id)"
|
||||
/>
|
||||
</label>
|
||||
<view v-if="item.spu?.status !== 1 && !state.editMode" class="down-box">
|
||||
该商品已下架
|
||||
</view>
|
||||
<view v-else-if="item.spu?.stock <= 0 && !state.editMode" class="down-box">
|
||||
该商品无库存
|
||||
</view>
|
||||
<s-goods-item
|
||||
:img="item.spu.picUrl || item.goods.image"
|
||||
:price="item.sku.price"
|
||||
:skuText="
|
||||
item.sku.properties.length > 1
|
||||
? item.sku.properties.reduce(
|
||||
(items2, items) => items2.valueName + ' ' + items.valueName,
|
||||
)
|
||||
: item.sku.properties[0].valueName
|
||||
"
|
||||
:title="item.spu.name"
|
||||
:titleWidth="400"
|
||||
priceColor="#FF3000"
|
||||
>
|
||||
<template v-if="!state.editMode" v-slot:tool>
|
||||
<su-number-box
|
||||
v-model="item.count"
|
||||
:max="item.sku.stock"
|
||||
:min="0"
|
||||
:step="1"
|
||||
@change="onNumberChange($event, item)"
|
||||
/>
|
||||
</template>
|
||||
</s-goods-item>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 底部 -->
|
||||
<su-fixed v-if="state.list.length > 0" :isInset="false" :val="48" bottom placeholder>
|
||||
<view class="cart-footer ss-flex ss-col-center ss-row-between ss-p-x-30 border-bottom">
|
||||
<view class="footer-left ss-flex ss-col-center">
|
||||
<label class="check-box ss-flex ss-col-center ss-p-r-30" @tap="onSelectAll">
|
||||
<radio
|
||||
:checked="state.isAllSelected"
|
||||
color="var(--ui-BG-Main)"
|
||||
style="transform: scale(0.8)"
|
||||
@tap.stop="onSelectAll"
|
||||
/>
|
||||
<view class="ss-m-l-8"> 全选</view>
|
||||
</label>
|
||||
<text>合计:</text>
|
||||
<view class="text-price price-text">
|
||||
{{ fen2yuan(state.totalPriceSelected) }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="footer-right">
|
||||
<button
|
||||
v-if="state.editMode"
|
||||
class="ss-reset-button ui-BG-Main-Gradient pay-btn ui-Shadow-Main"
|
||||
@tap="onDelete"
|
||||
>
|
||||
删除
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="ss-reset-button ui-BG-Main-Gradient pay-btn ui-Shadow-Main"
|
||||
@tap="onConfirm"
|
||||
>
|
||||
去结算
|
||||
{{ state.selectedIds?.length ? `(${state.selectedIds.length})` : '' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</su-fixed>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onShow } from '@dcloudio/uni-app';
|
||||
import SpuApi from '@/sheep/api/product/spu';
|
||||
import { computed, reactive } from 'vue';
|
||||
import { fen2yuan } from '@/sheep/hooks/useGoods';
|
||||
import { isEmpty } from '@/sheep/helper/utils';
|
||||
|
||||
// 隐藏原生tabBar
|
||||
uni.hideTabBar({
|
||||
fail: () => {},
|
||||
});
|
||||
|
||||
const sys_navBar = sheep.$platform.navbar;
|
||||
const cart = sheep.$store('cart');
|
||||
|
||||
const state = reactive({
|
||||
editMode: computed(() => cart.editMode),
|
||||
list: computed(() => cart.list),
|
||||
selectedList: [],
|
||||
selectedIds: computed(() => cart.selectedIds),
|
||||
isAllSelected: computed(() => cart.isAllSelected),
|
||||
totalPriceSelected: computed(() => cart.totalPriceSelected),
|
||||
});
|
||||
|
||||
// 单选选中
|
||||
function onSelectSingle(id) {
|
||||
cart.selectSingle(id);
|
||||
}
|
||||
|
||||
// 编辑、取消
|
||||
function onChangeEditMode(flag) {
|
||||
cart.onChangeEditMode(flag);
|
||||
}
|
||||
|
||||
// 全选
|
||||
function onSelectAll() {
|
||||
cart.selectAll(!state.isAllSelected);
|
||||
}
|
||||
|
||||
// 结算
|
||||
async function onConfirm() {
|
||||
const items = [];
|
||||
state.selectedList = state.list.filter((item) => state.selectedIds.includes(item.id));
|
||||
state.selectedList.map((item) => {
|
||||
// 此处前端做出修改
|
||||
items.push({
|
||||
skuId: item.sku.id,
|
||||
count: item.count,
|
||||
cartId: item.id,
|
||||
categoryId: item.spu.categoryId,
|
||||
});
|
||||
});
|
||||
if (isEmpty(items)) {
|
||||
sheep.$helper.toast('请先选择商品');
|
||||
return;
|
||||
}
|
||||
await validateDeliveryType(state.selectedList.map((item) => item.spu).map((spu) => spu.id));
|
||||
sheep.$router.go('/pages/order/confirm', {
|
||||
data: JSON.stringify({
|
||||
items,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验配送方式冲突
|
||||
*
|
||||
* @param {string[]} spuIds - 商品ID数组
|
||||
* @returns {Promise<void>}
|
||||
* @throws {Error} 当配送方式冲突或获取商品信息失败时抛出错误
|
||||
*/
|
||||
async function validateDeliveryType(spuIds) {
|
||||
// 获取商品信息
|
||||
const { data: spuList } = await SpuApi.getSpuListByIds(spuIds.join(','));
|
||||
if (isEmpty(spuList)) {
|
||||
sheep.$helper.toast('未找到商品信息');
|
||||
throw new Error('未找到商品信息');
|
||||
}
|
||||
// 获取所有商品的配送方式列表
|
||||
const deliveryTypesList = spuList.map((item) => item.deliveryTypes);
|
||||
// 检查配送方式冲突
|
||||
const hasConflict = checkDeliveryConflicts(deliveryTypesList);
|
||||
if (hasConflict) {
|
||||
sheep.$helper.toast('选中商品支持的配送方式冲突,不允许提交');
|
||||
throw new Error('选中商品支持的配送方式冲突,不允许提交');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查配送方式列表中是否存在冲突
|
||||
* @description
|
||||
* 示例场景:
|
||||
* A 商品支持:[快递, 自提]
|
||||
* B 商品支持:[快递]
|
||||
* C 商品支持:[自提]
|
||||
*
|
||||
* 对比结果:
|
||||
* A 和 B:不冲突 (有交集:快递)
|
||||
* A 和 C:不冲突 (有交集:自提)
|
||||
* B 和 C:冲突 (无交集)
|
||||
* @param {Array<Array<number>>} deliveryTypesList - 配送方式列表的数组
|
||||
* @returns {boolean} 是否存在冲突
|
||||
*/
|
||||
function checkDeliveryConflicts(deliveryTypesList) {
|
||||
for (let i = 0; i < deliveryTypesList.length - 1; i++) {
|
||||
const currentTypes = deliveryTypesList[i];
|
||||
for (let j = i + 1; j < deliveryTypesList.length; j++) {
|
||||
const nextTypes = deliveryTypesList[j];
|
||||
// 检查是否没有交集(即冲突)
|
||||
const hasNoIntersection = !currentTypes.some((type) => nextTypes.includes(type));
|
||||
if (hasNoIntersection) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function onNumberChange(e, cartItem) {
|
||||
if (e === 0) {
|
||||
cart.delete(cartItem.id);
|
||||
return;
|
||||
}
|
||||
if (cartItem.goods_num === e) return;
|
||||
cartItem.goods_num = e;
|
||||
cart.update({
|
||||
goods_id: cartItem.id,
|
||||
goods_num: e,
|
||||
goods_sku_price_id: cartItem.goods_sku_price_id,
|
||||
});
|
||||
}
|
||||
|
||||
async function onDelete() {
|
||||
cart.delete(state.selectedIds);
|
||||
}
|
||||
|
||||
function getCartList() {
|
||||
cart.getList();
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
getCartList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.ui-fixed) {
|
||||
height: 72rpx;
|
||||
}
|
||||
|
||||
.cart-box {
|
||||
width: 100%;
|
||||
|
||||
.cart-header {
|
||||
height: 70rpx;
|
||||
background-color: #f6f6f6;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: v-bind('sys_navBar') rpx;
|
||||
z-index: 1000;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.cart-footer {
|
||||
height: 100rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.pay-btn {
|
||||
width: 180rpx;
|
||||
height: 70rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 28rpx;
|
||||
font-weight: 500;
|
||||
border-radius: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.cart-content {
|
||||
margin-top: 70rpx;
|
||||
|
||||
.goods-box {
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
}
|
||||
// 下架商品
|
||||
.down-box {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(#fff, 0.8);
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #999;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,240 @@
|
||||
<!-- 商品分类列表 -->
|
||||
<template>
|
||||
<s-layout :bgStyle="{ color: '#fff' }" tabbar="/pages/index/category" title="分类">
|
||||
<view class="s-category">
|
||||
<view class="three-level-wrap ss-flex ss-col-top">
|
||||
<!-- 商品分类(左) -->
|
||||
<view class="side-menu-wrap" :style="[{ top: Number(statusBarHeight + 88) + 'rpx' }]">
|
||||
<scroll-view scroll-y :style="[{ height: pageHeight + 'px' }]">
|
||||
<view
|
||||
class="menu-item ss-flex"
|
||||
v-for="(item, index) in state.categoryList"
|
||||
:key="item.id"
|
||||
:class="[{ 'menu-item-active': index === state.activeMenu }]"
|
||||
@tap="onMenu(index)"
|
||||
>
|
||||
<view class="menu-title ss-line-1">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<!-- 商品分类(右) -->
|
||||
<view class="goods-list-box" v-if="state.categoryList?.length">
|
||||
<scroll-view scroll-y :style="[{ height: pageHeight + 'px' }]">
|
||||
<image
|
||||
v-if="state.categoryList[state.activeMenu].picUrl"
|
||||
class="banner-img"
|
||||
:src="sheep.$url.cdn(state.categoryList[state.activeMenu].picUrl)"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<first-one v-if="state.style === 'first_one'" :pagination="state.pagination" />
|
||||
<first-two v-if="state.style === 'first_two'" :pagination="state.pagination" />
|
||||
<second-one
|
||||
v-if="state.style === 'second_one'"
|
||||
:data="state.categoryList"
|
||||
:activeMenu="state.activeMenu"
|
||||
/>
|
||||
<uni-load-more
|
||||
v-if="
|
||||
(state.style === 'first_one' || state.style === 'first_two') &&
|
||||
state.pagination.total > 0
|
||||
"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '点击查看更多',
|
||||
}"
|
||||
@tap="loadMore"
|
||||
/>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import secondOne from './components/second-one.vue';
|
||||
import firstOne from './components/first-one.vue';
|
||||
import firstTwo from './components/first-two.vue';
|
||||
import sheep from '@/sheep';
|
||||
import CategoryApi from '@/sheep/api/product/category';
|
||||
import SpuApi from '@/sheep/api/product/spu';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
import _ from 'lodash-es';
|
||||
import { handleTree } from '@/sheep/helper/utils';
|
||||
|
||||
const state = reactive({
|
||||
style: 'second_one', // first_one(一级 - 样式一), first_two(二级 - 样式二), second_one(二级)
|
||||
categoryList: [], // 商品分类树
|
||||
activeMenu: 0, // 选中的一级菜单,在 categoryList 的下标
|
||||
|
||||
pagination: {
|
||||
// 商品分页
|
||||
list: [], // 商品列表
|
||||
total: [], // 商品总数
|
||||
pageNo: 1,
|
||||
pageSize: 6,
|
||||
},
|
||||
loadStatus: '',
|
||||
});
|
||||
|
||||
const { safeArea } = sheep.$platform.device;
|
||||
const pageHeight = computed(() => safeArea.height - 44 - 50);
|
||||
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
|
||||
|
||||
// 加载商品分类
|
||||
async function getList() {
|
||||
const { code, data } = await CategoryApi.getCategoryList();
|
||||
if (code !== 0) {
|
||||
return;
|
||||
}
|
||||
state.categoryList = handleTree(data);
|
||||
}
|
||||
|
||||
// 选中菜单
|
||||
const onMenu = (val) => {
|
||||
state.activeMenu = val;
|
||||
if (state.style === 'first_one' || state.style === 'first_two') {
|
||||
state.pagination.pageNo = 1;
|
||||
state.pagination.list = [];
|
||||
state.pagination.total = 0;
|
||||
getGoodsList();
|
||||
}
|
||||
};
|
||||
|
||||
// 加载商品列表
|
||||
async function getGoodsList() {
|
||||
// 加载列表
|
||||
state.loadStatus = 'loading';
|
||||
const res = await SpuApi.getSpuPage({
|
||||
categoryId: state.categoryList[state.activeMenu].id,
|
||||
pageNo: state.pagination.pageNo,
|
||||
pageSize: state.pagination.pageSize,
|
||||
});
|
||||
if (res.code !== 0) {
|
||||
return;
|
||||
}
|
||||
// 合并列表
|
||||
state.pagination.list = _.concat(state.pagination.list, res.data.list);
|
||||
state.pagination.total = res.data.total;
|
||||
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
|
||||
}
|
||||
|
||||
// 加载更多商品
|
||||
function loadMore() {
|
||||
if (state.loadStatus === 'noMore') {
|
||||
return;
|
||||
}
|
||||
state.pagination.pageNo++;
|
||||
getGoodsList();
|
||||
}
|
||||
|
||||
onLoad(async (params) => {
|
||||
await getList();
|
||||
|
||||
// 首页点击分类的处理:查找满足条件的分类
|
||||
const foundCategory = state.categoryList.find((category) => category.id === Number(params.id));
|
||||
// 如果找到则调用 onMenu 自动勾选相应分类,否则调用 onMenu(0) 勾选第一个分类
|
||||
onMenu(foundCategory ? state.categoryList.indexOf(foundCategory) : 0);
|
||||
});
|
||||
|
||||
function handleScrollToLower() {
|
||||
loadMore();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.s-category {
|
||||
:deep() {
|
||||
.side-menu-wrap {
|
||||
width: 200rpx;
|
||||
height: 100%;
|
||||
padding-left: 12rpx;
|
||||
background-color: #f6f6f6;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
|
||||
.menu-item {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
position: relative;
|
||||
transition: all linear 0.2s;
|
||||
|
||||
.menu-title {
|
||||
line-height: 32rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
margin-left: 28rpx;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 64rpx;
|
||||
height: 12rpx;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
var(--ui-BG-Main-gradient),
|
||||
var(--ui-BG-Main-light)
|
||||
) !important;
|
||||
position: absolute;
|
||||
left: -64rpx;
|
||||
bottom: 0;
|
||||
z-index: -1;
|
||||
transition: all linear 0.2s;
|
||||
}
|
||||
}
|
||||
|
||||
&.menu-item-active {
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx 0 0 20rpx;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: -20rpx;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background: radial-gradient(circle at 0 100%, transparent 20rpx, #fff 0);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -20rpx;
|
||||
right: 0;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background: radial-gradient(circle at 0% 0%, transparent 20rpx, #fff 0);
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-weight: 600;
|
||||
|
||||
&::before {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.goods-list-box {
|
||||
background-color: #fff;
|
||||
width: calc(100vw - 200rpx);
|
||||
padding: 10px;
|
||||
margin-left: 200rpx;
|
||||
}
|
||||
|
||||
.banner-img {
|
||||
width: calc(100vw - 130px);
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,26 @@
|
||||
<!-- 分类展示:first-one 风格 -->
|
||||
<template>
|
||||
<view class="ss-flex-col">
|
||||
<view class="goods-box" v-for="item in pagination.list" :key="item.id">
|
||||
<s-goods-column
|
||||
size="sl"
|
||||
:data="item"
|
||||
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const props = defineProps({
|
||||
pagination: Object,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-box {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,66 @@
|
||||
<!-- 分类展示:first-two 风格 -->
|
||||
<template>
|
||||
<view>
|
||||
<view class="ss-flex flex-wrap">
|
||||
<view class="goods-box" v-for="item in pagination?.list" :key="item.id">
|
||||
<view @click="sheep.$router.go('/pages/goods/index', { id: item.id })">
|
||||
<view class="goods-img">
|
||||
<image class="goods-img" :src="item.picUrl" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="goods-content">
|
||||
<view class="goods-title ss-line-1 ss-m-b-28">{{ item.name }}</view>
|
||||
<view class="goods-price">¥{{ fen2yuan(item.price) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { fen2yuan } from '@/sheep/hooks/useGoods';
|
||||
|
||||
const props = defineProps({
|
||||
pagination: Object,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-box {
|
||||
width: calc((100% - 20rpx) / 2);
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.goods-img {
|
||||
width: 100%;
|
||||
height: 246rpx;
|
||||
border-radius: 10rpx 10rpx 0px 0px;
|
||||
}
|
||||
|
||||
.goods-content {
|
||||
width: 100%;
|
||||
background: #ffffff;
|
||||
box-shadow: 0px 0px 20rpx 4rpx rgba(199, 199, 199, 0.22);
|
||||
padding: 20rpx 0 32rpx 16rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 0 0 10rpx 10rpx;
|
||||
|
||||
.goods-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.goods-price {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #e1212b;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2n + 1) {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,80 @@
|
||||
<!-- 分类展示:second-one 风格 -->
|
||||
<template>
|
||||
<view>
|
||||
<!-- 一级分类的名字 -->
|
||||
<view class="title-box ss-flex ss-col-center ss-row-center ss-p-b-30">
|
||||
<view class="title-line-left" />
|
||||
<view class="title-text ss-p-x-20">{{ props.data[activeMenu].name }}</view>
|
||||
<view class="title-line-right" />
|
||||
</view>
|
||||
<!-- 二级分类的名字 -->
|
||||
<view class="goods-item-box ss-flex ss-flex-wrap ss-p-b-20">
|
||||
<view
|
||||
class="goods-item"
|
||||
v-for="item in props.data[activeMenu].children"
|
||||
:key="item.id"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/goods/list', {
|
||||
categoryId: item.id,
|
||||
})
|
||||
"
|
||||
>
|
||||
<image class="goods-img" :src="item.picUrl" mode="aspectFill" />
|
||||
<view class="ss-p-10">
|
||||
<view class="goods-title ss-line-1">{{ item.name }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
activeMenu: [Number, String],
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title-box {
|
||||
.title-line-left,
|
||||
.title-line-right {
|
||||
width: 15px;
|
||||
height: 1px;
|
||||
background: #d2d2d2;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-item {
|
||||
width: calc((100% - 20px) / 3);
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:nth-of-type(3n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.goods-img {
|
||||
width: calc((100vw - 140px) / 3);
|
||||
height: calc((100vw - 140px) / 3);
|
||||
}
|
||||
|
||||
.goods-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
line-height: 40rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.goods-price {
|
||||
color: $red;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,93 @@
|
||||
<!-- 首页,支持店铺装修 -->
|
||||
<template>
|
||||
<view v-if="template">
|
||||
<s-layout
|
||||
title="首页"
|
||||
navbar="custom"
|
||||
tabbar="/pages/index/index"
|
||||
:bgStyle="template.page"
|
||||
:navbarStyle="template.navigationBar"
|
||||
onShareAppMessage
|
||||
>
|
||||
<s-block
|
||||
v-for="(item, index) in template.components"
|
||||
:key="index"
|
||||
:styles="item.property.style"
|
||||
>
|
||||
<s-block-item :type="item.id" :data="item.property" :styles="item.property.style" />
|
||||
</s-block>
|
||||
</s-layout>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { onLoad, onPageScroll, onPullDownRefresh } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import $share from '@/sheep/platform/share';
|
||||
// 隐藏原生tabBar
|
||||
uni.hideTabBar({
|
||||
fail: () => {},
|
||||
});
|
||||
|
||||
const template = computed(() => sheep.$store('app').template?.home);
|
||||
// 在此处拦截改变一下首页轮播图 此处先写死后期复活 放到启动函数里
|
||||
// (async function() {
|
||||
// console.log('原代码首页定制化数据',template)
|
||||
// let {
|
||||
// data
|
||||
// } = await index2Api.decorate();
|
||||
// console.log('首页导航配置化过高无法兼容',JSON.parse(data[1].value))
|
||||
// 改变首页底部数据 但是没有通过数组id获取商品数据接口
|
||||
// let {
|
||||
// data: datas
|
||||
// } = await index2Api.spids();
|
||||
// template.value.data[9].data.goodsIds = datas.list.map(item => item.id);
|
||||
// template.value.data[0].data.list = JSON.parse(data[0].value).map(item => {
|
||||
// return {
|
||||
// src: item.picUrl,
|
||||
// url: item.url,
|
||||
// title: item.name,
|
||||
// type: "image"
|
||||
// }
|
||||
// })
|
||||
// }())
|
||||
|
||||
onLoad((options) => {
|
||||
// #ifdef MP
|
||||
// 小程序识别二维码
|
||||
if (options.scene) {
|
||||
const sceneParams = decodeURIComponent(options.scene).split('=');
|
||||
console.log('sceneParams=>', sceneParams);
|
||||
options[sceneParams[0]] = sceneParams[1];
|
||||
}
|
||||
// #endif
|
||||
|
||||
// 预览模板
|
||||
if (options.templateId) {
|
||||
sheep.$store('app').init(options.templateId);
|
||||
}
|
||||
|
||||
// 解析分享信息
|
||||
if (options.spm) {
|
||||
$share.decryptSpm(options.spm);
|
||||
}
|
||||
|
||||
// 进入指定页面(完整页面路径)
|
||||
if (options.page) {
|
||||
sheep.$router.go(decodeURIComponent(options.page));
|
||||
}
|
||||
});
|
||||
|
||||
// 下拉刷新
|
||||
onPullDownRefresh(() => {
|
||||
sheep.$store('app').init();
|
||||
setTimeout(function () {
|
||||
uni.stopPullDownRefresh();
|
||||
}, 800);
|
||||
});
|
||||
|
||||
onPageScroll(() => {});
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!-- 微信公众号的登录回调页 -->
|
||||
<template>
|
||||
<!-- 空登陆页 -->
|
||||
<view />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
|
||||
onLoad(async (options) => {
|
||||
// #ifdef H5
|
||||
// 将 search 参数赋值到 options 中,方便下面解析
|
||||
new URLSearchParams(location.search).forEach((value, key) => {
|
||||
options[key] = value;
|
||||
});
|
||||
// 执行登录 or 绑定,注意需要 await 绑定
|
||||
const event = options.event;
|
||||
const code = options.code;
|
||||
const state = options.state;
|
||||
if (event === 'login') { // 场景一:登录
|
||||
await sheep.$platform.useProvider().login(code, state);
|
||||
} else if (event === 'bind') { // 场景二:绑定
|
||||
await sheep.$platform.useProvider().bind(code, state);
|
||||
}
|
||||
|
||||
// 检测 H5 登录回调
|
||||
let returnUrl = uni.getStorageSync('returnUrl');
|
||||
if (returnUrl) {
|
||||
uni.removeStorage({key:'returnUrl'});
|
||||
location.replace(returnUrl);
|
||||
} else {
|
||||
uni.switchTab({
|
||||
url: '/',
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,51 @@
|
||||
<!-- 自定义页面:支持装修 -->
|
||||
<template>
|
||||
<s-layout
|
||||
:title="state.name"
|
||||
navbar="custom"
|
||||
:bgStyle="state.page"
|
||||
:navbarStyle="state.navigationBar"
|
||||
onShareAppMessage
|
||||
showLeftButton
|
||||
>
|
||||
<s-block v-for="(item, index) in state.components" :key="index" :styles="item.property.style">
|
||||
<s-block-item :type="item.id" :data="item.property" :styles="item.property.style" />
|
||||
</s-block>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import { onLoad, onPageScroll } from '@dcloudio/uni-app';
|
||||
import DiyApi from '@/sheep/api/promotion/diy';
|
||||
|
||||
const state = reactive({
|
||||
name: '',
|
||||
components: [],
|
||||
navigationBar: {},
|
||||
page: {},
|
||||
});
|
||||
onLoad(async (options) => {
|
||||
let id = options.id
|
||||
|
||||
// #ifdef MP
|
||||
// 小程序预览自定义页面
|
||||
if (options.scene) {
|
||||
const sceneParams = decodeURIComponent(options.scene).split('=');
|
||||
id = sceneParams[1];
|
||||
}
|
||||
// #endif
|
||||
|
||||
const { code, data } = await DiyApi.getDiyPage(id);
|
||||
if (code === 0) {
|
||||
state.name = data.name;
|
||||
state.components = data.property?.components;
|
||||
state.navigationBar = data.property?.navigationBar;
|
||||
state.page = data.property?.page;
|
||||
}
|
||||
});
|
||||
|
||||
onPageScroll(() => {});
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -0,0 +1,119 @@
|
||||
<!-- 搜索界面 -->
|
||||
<template>
|
||||
<s-layout :bgStyle="{ color: '#FFF' }" class="set-wrap" title="搜索">
|
||||
<view class="ss-p-x-24">
|
||||
<view class="ss-flex ss-col-center">
|
||||
<uni-search-bar
|
||||
class="ss-flex-1"
|
||||
radius="33"
|
||||
placeholder="请输入关键字"
|
||||
cancelButton="none"
|
||||
:focus="true"
|
||||
@confirm="onSearch($event.value)"
|
||||
/>
|
||||
</view>
|
||||
<view class="ss-flex ss-row-between ss-col-center">
|
||||
<view class="serach-history">搜索历史</view>
|
||||
<button class="clean-history ss-reset-button" @tap="onDelete"> 清除搜索历史 </button>
|
||||
</view>
|
||||
<view class="ss-flex ss-col-center ss-row-left ss-flex-wrap">
|
||||
<button
|
||||
class="history-btn ss-reset-button"
|
||||
@tap="onSearch(item)"
|
||||
v-for="(item, index) in state.historyList"
|
||||
:key="index"
|
||||
>
|
||||
{{ item }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
|
||||
const state = reactive({
|
||||
historyList: [],
|
||||
});
|
||||
|
||||
// 搜索
|
||||
function onSearch(keyword) {
|
||||
if (!keyword) {
|
||||
return;
|
||||
}
|
||||
saveSearchHistory(keyword);
|
||||
// 前往商品列表(带搜索条件)
|
||||
sheep.$router.go('/pages/goods/list', { keyword });
|
||||
}
|
||||
|
||||
// 保存搜索历史
|
||||
function saveSearchHistory(keyword) {
|
||||
// 如果关键词在搜索历史中,则把此关键词先移除
|
||||
if (state.historyList.includes(keyword)) {
|
||||
state.historyList.splice(state.historyList.indexOf(keyword), 1);
|
||||
}
|
||||
// 置顶关键词
|
||||
state.historyList.unshift(keyword);
|
||||
|
||||
// 最多保留 10 条记录
|
||||
if (state.historyList.length >= 10) {
|
||||
state.historyList.length = 10;
|
||||
}
|
||||
uni.setStorageSync('searchHistory', state.historyList);
|
||||
}
|
||||
|
||||
function onDelete() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确认清除搜索历史吗?',
|
||||
success: function (res) {
|
||||
if (res.confirm) {
|
||||
state.historyTag = [];
|
||||
uni.removeStorageSync('searchHistory');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
state.historyList = uni.getStorageSync('searchHistory') || [];
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.serach-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.uni-searchbar {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.serach-history {
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.clean-history {
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.history-btn {
|
||||
padding: 0 38rpx;
|
||||
height: 60rpx;
|
||||
background: #f5f6f8;
|
||||
border-radius: 30rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
max-width: 690rpx;
|
||||
margin: 0 20rpx 20rpx 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,47 @@
|
||||
<!-- 个人中心:支持装修 -->
|
||||
<template>
|
||||
<s-layout
|
||||
title="我的"
|
||||
tabbar="/pages/index/user"
|
||||
navbar="custom"
|
||||
:bgStyle="template.page"
|
||||
:navbarStyle="template.navigationBar"
|
||||
onShareAppMessage
|
||||
>
|
||||
<s-block
|
||||
v-for="(item, index) in template.components"
|
||||
:key="index"
|
||||
:styles="item.property.style"
|
||||
>
|
||||
<s-block-item :type="item.id" :data="item.property" :styles="item.property.style" />
|
||||
</s-block>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { onShow, onPageScroll, onPullDownRefresh } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
// 隐藏原生tabBar
|
||||
uni.hideTabBar({
|
||||
fail: () => {},
|
||||
});
|
||||
|
||||
const template = computed(() => sheep.$store('app').template.user);
|
||||
|
||||
onShow(() => {
|
||||
sheep.$store('user').updateUserData();
|
||||
});
|
||||
|
||||
onPullDownRefresh(() => {
|
||||
sheep.$store('user').updateUserData();
|
||||
setTimeout(function () {
|
||||
uni.stopPullDownRefresh();
|
||||
}, 800);
|
||||
});
|
||||
|
||||
onPageScroll(() => {});
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
Reference in New Issue
Block a user