提交 aaa6e234 作者: ZhangLingKun

功能:商城页面

上级 6e3d2a91
流水线 #7380 已通过 于阶段
in 6 分 20 秒
...@@ -91,7 +91,7 @@ export type QueryGoodsInfoByCategorySub = InterFunction< ...@@ -91,7 +91,7 @@ export type QueryGoodsInfoByCategorySub = InterFunction<
tradeName: string; tradeName: string;
userAccountId: number; userAccountId: number;
recommend: number; recommend: number;
}[][] }[]
>; >;
// 一级行业列表 // 一级行业列表
export type IndustryListPagesType = InterListFunction< export type IndustryListPagesType = InterListFunction<
......
import { InterDataType, InterFunction } from '@/api/interface';
// 小程序分类信息--含一二级分类
export type GetAppCategoryInfo = InterFunction<
{},
{
id: number;
name: string;
description?: string;
icon?: string;
createTime?: string;
updateTime?: null;
subDTOList?: Array<{
id: number;
name: string;
description?: string;
categoryPrimaryId?: number;
createTime?: string;
updateTime?: string;
subDTOList?: InterDataType<QueryGoodsInfoByCategorySub>;
}>;
sort?: number;
}[]
>;
export type QueryGoodsInfoByCategorySub = InterFunction<
number[],
{
categoryPrimaryId: number;
categorySubId: number;
createTime: string;
description: string;
goodsDetails: string;
goodsLabel: string;
goodsSpecList: Array<{
chooseType: number;
goodsSpecValuesList: Array<{
channelPrice: number;
goodsSpecId: number;
id: number;
partNo: string;
salePrice: number;
showPrice: number;
specValueImage: string;
specValueName: string;
stock: number;
}>;
id: number;
mallGoodsId: number;
must: number;
skuUnitId: number;
specName: string;
}>;
id: number;
labelShow: number;
resourcesList: Array<{
id: number;
type: number;
url: string;
}>;
shelfStatus: number;
tradeName: string;
userAccountId: number;
recommend: number;
}[]
>;
import {
QueryGoodsInfoByCategorySub,
GetAppCategoryInfo,
} from '@/api/interface/mall';
import request from '../request';
export class MallAPI {
// 小程序分类信息--含一二级分类
static getAppCategoryInfo: GetAppCategoryInfo = (params) =>
request.get('/pms/category/appCategoryInfo', { params });
// 根据子分类查询商品信息
static queryGoodsInfoByCategorySub: QueryGoodsInfoByCategorySub = (params) =>
request.post('/pms/app/goods/queryGoodsInfoByCategorySub', params);
}
import React from 'react';
import { Breadcrumb } from 'antd';
import { useRouter } from 'next/router';
import styled from 'styled-components';
const BreadcrumbWrap = styled.div`
position: relative;
width: 100%;
display: flex;
align-content: center;
justify-content: flex-start;
.title {
font-size: 13px;
font-weight: 400;
color: #666666;
margin-bottom: 0.75rem;
}
`;
const BreadcrumbView: React.FC = () => {
// 路由钩子
const router = useRouter();
// 路由对应列表
const routerList = [
{ name: '云享商城', path: '/mall' },
{ name: '行业服务', path: '/service' },
{ name: '设备租赁', path: '/rent' },
{ name: '执照培训', path: '/train' },
{ name: '飞手约单', path: '/flyer' },
];
return (
<BreadcrumbWrap>
<div className="title">您的位置:</div>
<Breadcrumb
separator=">"
items={[
{
title: '首页',
href: '/',
},
{
title: routerList.find((i) => i.path === router?.pathname)?.name,
href: router?.pathname,
},
]}
/>
</BreadcrumbWrap>
);
};
export default BreadcrumbView;
import React, { useEffect, useState } from 'react';
import { DownOutlined, UpOutlined } from '@ant-design/icons';
import styled from 'styled-components';
const CategorySelectWrap = styled.div`
position: relative;
width: 100%;
min-height: 2rem;
border: 1px solid #ebebeb;
margin-bottom: 1rem;
.category-select {
position: relative;
width: 100%;
height: 2rem;
flex-wrap: wrap;
border-bottom: 1px solid #ebebeb;
.select-item {
position: relative;
box-sizing: border-box;
color: #333333;
padding: 0 1.25rem;
min-height: 2rem;
line-height: 2rem;
display: flex;
align-items: center;
justify-content: center;
&:first-child {
color: #999999;
}
&:not(:first-child):active,
&:not(:first-child):hover {
background: #ececec;
}
&:not(:first-child) {
cursor: pointer;
}
.text {
margin-right: 0.25rem;
}
}
.item-active {
background: #ececec;
}
}
.category-list {
position: relative;
width: 100%;
min-height: 3rem;
flex-wrap: wrap;
box-sizing: border-box;
padding: 0 0.5rem;
.list-item {
position: relative;
min-height: 1.68rem;
line-height: 1.68rem;
padding: 0 0.5rem;
cursor: pointer;
margin-right: 0.5rem;
}
.item-active {
color: #fff;
background: #ff552d;
border-radius: 4px;
}
}
`;
// 分类列表类型
export type CategoryType = {
value: number;
label: string;
children?: CategoryType;
}[];
const CategorySelectView: React.FC<{
list: CategoryType;
onMain?: (arr: number) => void;
onSecond?: (arr: number[]) => void;
}> = ({ list, onMain, onSecond }) => {
// 图标样式
const IconStyle = {
fontSize: '8px',
color: 'rgba(153,153,153,0.5)',
transform: 'scaleX(1.2)',
};
// 当前选择索引
const [currentIndex, setCurrentIndex] = useState<number>(0);
// 二级索引列表
const [secondIndex, setSecondIndex] = useState<number[]>([]);
// 二级索引选择
const handleSecondary = (index: number) => {
const has = secondIndex?.some((i) => i === index);
const arr = has
? secondIndex?.filter((i) => i !== index)
: [...secondIndex, index];
setSecondIndex(arr);
onSecond?.(
arr?.length !== 0
? arr?.map((i) => list[currentIndex]?.children?.[i]?.value || 0)
: list[currentIndex]?.children?.map((i) => i?.value) || [],
);
};
// 主索引选择
const handleMain = (index: number) => {
setCurrentIndex(index);
setSecondIndex([]);
// 切换分类
onMain?.(list[index]?.value);
// 清空二级分类,并返回全部二级分类
onSecond?.(list[index]?.children?.map((i) => i?.value) || []);
};
// 组件挂载
useEffect(() => {
if (!list?.length) return;
// 初始化返回一级分类
onMain?.(list[currentIndex]?.value);
// 初始化全部二级分类
onSecond?.(list[currentIndex]?.children?.map((i) => i?.value) || []);
}, [list]);
return (
<CategorySelectWrap>
<div className="category-select flex-start">
<div className="select-item">类别:</div>
{list?.map((i, j) => (
<div
className={`select-item ${currentIndex === j && 'item-active'}`}
key={j}
onClick={() => handleMain(j)}
>
<div className="text">{i.label}</div>
{currentIndex === j ? (
<UpOutlined style={IconStyle} />
) : (
<DownOutlined style={IconStyle} />
)}
</div>
))}
</div>
<div className="category-list flex-start">
{list[currentIndex]?.children?.map((i, j) => (
<div
className={`list-item ${secondIndex?.includes(j) && 'item-active'}`}
key={j}
onClick={() => handleSecondary(j)}
>
{i.label}
</div>
))}
</div>
</CategorySelectWrap>
);
};
export default CategorySelectView;
import React from 'react';
import { PropertySafetyFilled, ShoppingCartOutlined } from '@ant-design/icons';
import styled from 'styled-components';
import { InterDataType } from '@/api/interface';
import { QueryGoodsInfoByCategorySub } from '@/api/interface/home';
const ProductItemWrap = styled.div`
position: relative;
box-sizing: border-box;
width: calc((100% - (0.83rem * 5)) / 6);
height: 15rem;
border: 0.02rem solid #e3e3e3;
margin: 0 0.83rem 0.83rem 0;
padding: 0.67rem;
cursor: pointer;
&:nth-child(6n) {
margin-right: 0;
}
&:active,
&:hover {
background: #fff9f6;
border: 0.04rem solid #ff7a3e;
}
.product-image {
position: relative;
width: 100%;
height: 8rem;
box-sizing: border-box;
.image {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.product-title {
width: 100%;
font-size: 13px;
font-weight: 500;
color: #333333;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
margin-bottom: 0.33rem;
}
.product-desc {
position: relative;
width: 100%;
font-size: 12px;
font-weight: 400;
color: #999999;
.label {
width: 60%;
}
.text {
width: 40%;
text-align: right;
}
}
.product-store {
position: absolute;
left: 0.67rem;
bottom: 0.67rem;
.title {
width: 6rem;
font-size: 12px;
font-weight: 400;
color: #666666;
margin-left: 0.25rem;
}
}
.product-cart {
position: absolute;
right: 0.67rem;
bottom: 0.67rem;
width: 2rem;
height: 2rem;
background: #ff8b2e;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
&:active,
&:hover {
filter: brightness(0.9);
}
}
`;
// 商品详情类型
type GoodsInfoListType = InterDataType<QueryGoodsInfoByCategorySub>[0];
const ProductItemView: React.FC<{
detail: GoodsInfoListType;
}> = ({ detail }) => {
return (
<ProductItemWrap>
<div className="product-image">
<img
className="image"
src={`${detail?.resourcesList?.at(0)
?.url}?x-oss-process=image/quality,q_20`}
alt={detail?.tradeName}
/>
</div>
<div className="product-title">{detail?.tradeName}</div>
<div className="product-desc flex-between">
<div className="label text-ellipsis">{detail?.description}</div>
<div className="text text-ellipsis">成交{detail?.id}</div>
</div>
<div className="product-store flex-start">
<PropertySafetyFilled style={{ color: '#FF552D' }} />
<div className="title text-ellipsis">{detail?.tradeName}</div>
</div>
<div className="product-cart">
<ShoppingCartOutlined style={{ color: '#ffffff', fontSize: '16px' }} />
</div>
</ProductItemWrap>
);
};
export default ProductItemView;
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Button } from 'antd'; import { Button } from 'antd';
import { useRouter } from 'next/router';
import styled from 'styled-components'; import styled from 'styled-components';
import { HomeAPI } from '@/api'; import { HomeAPI } from '@/api';
import { InterDataType } from '@/api/interface'; import { InterDataType } from '@/api/interface';
...@@ -9,6 +10,8 @@ import { AppCategoryInfoType } from '@/api/interface/home'; ...@@ -9,6 +10,8 @@ import { AppCategoryInfoType } from '@/api/interface/home';
type ListType = InterDataType<AppCategoryInfoType>; type ListType = InterDataType<AppCategoryInfoType>;
const TabView01 = () => { const TabView01 = () => {
// 导航钩子
const router = useRouter();
// 列表数据 // 列表数据
const [tabList, setTabList] = useState<ListType>([]); const [tabList, setTabList] = useState<ListType>([]);
// 获取云享商城分类 // 获取云享商城分类
...@@ -21,12 +24,19 @@ const TabView01 = () => { ...@@ -21,12 +24,19 @@ const TabView01 = () => {
// console.log('获取云享商城分类 --->', tabList); // console.log('获取云享商城分类 --->', tabList);
} }
}; };
// 跳转详情
const handleDetail = () => {
router.push('/mall');
};
// 组件挂载 // 组件挂载
useEffect(() => { useEffect(() => {
getAppCategoryInfo().then(); getAppCategoryInfo().then();
}, []); }, []);
return ( return (
<TabViewWrap className="animate__animated animate__fast animate__fadeIn"> <TabViewWrap
className="animate__animated animate__fast animate__fadeIn"
onClick={() => handleDetail()}
>
{tabList.map((i, j) => ( {tabList.map((i, j) => (
<div key={j}> <div key={j}>
<div className={'tab-little flex-start'}> <div className={'tab-little flex-start'}>
......
import React, { useEffect } from 'react'; import React, { useEffect, useState } from 'react';
import { Button } from 'antd';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { CommonAPI } from '@/api'; import { InterDataType } from '@/api/interface';
import {
GetAppCategoryInfo,
QueryGoodsInfoByCategorySub,
} from '@/api/interface/mall';
import { MallAPI } from '@/api/modules/mall';
import BreadcrumbView from '@/components/breadcrumb';
import CategorySelectView, { CategoryType } from '@/components/categorySelect';
import LayoutView from '@/components/layout'; import LayoutView from '@/components/layout';
import ProductItemView from '@/components/productItem';
import { MallWrap } from '@/pages/mall/styled';
// 分类列表类型
type CategoryListType = InterDataType<GetAppCategoryInfo>;
// 商品列表类型
type GoodsInfoListType = InterDataType<QueryGoodsInfoByCategorySub>;
// 每次加载页面都会执行 // 每次加载页面都会执行
export async function getServerSideProps() { export async function getServerSideProps() {
// 分类数据 // 分类数据
let categoryData = {}; let categoryList: CategoryListType = [];
// 获取分类数据 // 获取各个目录及分类信息
const getPageHomeCategories = async () => { const getAppCategoryInfo = async () => {
const res = await CommonAPI.getPageHomeCategories({ const res = await MallAPI.getAppCategoryInfo({
type: 4, type: 4,
}); });
if (res && res.code === '200') { if (res && res.code === '200') {
categoryData = res; categoryList = res?.result || [];
} }
}; };
// 依次获取接口数据 // 依次获取接口数据
await (async () => { await (async () => {
await getPageHomeCategories(); await getAppCategoryInfo();
})(); })();
return { props: { categoryData } }; return { props: { categoryList } };
} }
const MallView = (props: any) => { const MallView: React.FC<{
categoryList: CategoryListType;
}> = (props) => {
// 路由钩子 // 路由钩子
const router = useRouter(); const router = useRouter();
// 获取接口数据 // 分类列表
const getCooperationListTag = async () => { const [categoryList, setCategoryList] = useState<CategoryType>([]);
const res = await CommonAPI.cooperationListTag(); // 商品列表
const [goodsInfoList, setGoodsInfoList] = useState<GoodsInfoListType>([]);
// 转换分类列表
const getCategoryList = () => {
setCategoryList(
props?.categoryList?.map((i) => ({
value: i.id,
label: i.name,
children: i.subDTOList?.map((n) => ({ value: n.id, label: n.name })),
})),
);
};
// 获取商品列表
const getGoodsInfoByCategorySub = async (list: number[]) => {
const res = await MallAPI.queryGoodsInfoByCategorySub(list);
if (res && res.code === '200') { if (res && res.code === '200') {
console.log('获取接口数据 --->', res); setGoodsInfoList(res.result || []);
console.log('商品列表 --->', res.result);
} }
}; };
// 组件挂载
useEffect(() => { useEffect(() => {
getCooperationListTag().then(); if (!props) return;
console.log('执行到此处12312 --->', props); getCategoryList();
// console.log('执行到此处12312 --->', props);
}, [props]); }, [props]);
// 路由变化
useEffect(() => {
console.log('路由变化 --->', router);
}, [router]);
return ( return (
<> <>
<LayoutView placeholder={true}> <LayoutView placeholder={true}>
<div>测试</div> <MallWrap>
<Button <BreadcrumbView />
onClick={() => { <CategorySelectView
router.back(); list={categoryList}
}} onSecond={getGoodsInfoByCategorySub}
> />
返回 <div className="mall-list flex-start">
</Button> {goodsInfoList?.map((i, j) => (
<ProductItemView key={j} detail={i} />
))}
</div>
</MallWrap>
</LayoutView> </LayoutView>
</> </>
); );
......
import styled from 'styled-components';
export default function Style() {
return <></>;
}
export const MallWrap = styled.div`
position: relative;
max-width: 1190px;
box-sizing: border-box;
padding: 2rem 0 0 0;
margin: 0 auto;
.mall-list {
position: relative;
width: 100%;
flex-wrap: wrap;
}
`;
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论