提交 590a0325 作者: ZhangLingKun

功能:商城页面重构

上级 0f330754
流水线 #8788 已失败 于阶段
in 33 秒
......@@ -25,8 +25,8 @@ export class MallAPI {
request.get('/pms/app/goods/appMallGoodsDetails', { params });
// pc-后台用户id单位查询
static getCompanyInfoByBUId: GetCompanyInfoByBUId = (params) =>
request.get('/userapp/company/getCompanyInfoByBUId', { params });
static getCompanyInfoByBUId: GetCompanyInfoByBUId = (params, config) =>
request.get('/userapp/company/getCompanyInfoByBUId', { params, ...config });
// 租赁商品详情
static leaseGoodsDetails: LeaseGoodsDetailsType = (params) =>
......
......@@ -5,112 +5,112 @@ import styled from 'styled-components';
import { InterDataType } from '@/api/interface';
import { QueryGoodsInfoByCategorySub } from '@/api/interface/mall';
const ProductItemWrap = styled.div`
position: relative;
box-sizing: border-box;
width: calc((100% - (0.83rem * 5)) / 6);
height: 16rem;
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 {
// 商品详情类型
type GoodsInfoListType = InterDataType<QueryGoodsInfoByCategorySub>[0];
const ProductItemView: React.FC<{
detail: GoodsInfoListType;
row?: number;
}> = ({ detail, row = 6 }) => {
const ProductItemWrap = styled.div`
position: relative;
width: 100%;
height: 8rem;
box-sizing: border-box;
margin-bottom: 0.5rem;
.image {
width: 100%;
height: 100%;
object-fit: contain;
width: calc((100% - (0.83rem * ${row - 1})) / ${row});
height: 16rem;
border: 0.02rem solid #e3e3e3;
margin: 0 0.83rem 0.83rem 0;
padding: 0.67rem;
cursor: pointer;
&:nth-child(${row}n) {
margin-right: 0;
}
}
.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;
min-height: 2rem;
}
.product-desc {
position: relative;
width: 100%;
font-size: 12px;
font-weight: 400;
color: #999999;
margin-bottom: 0.1rem;
.label {
width: 60%;
&:active,
&:hover {
background: #fff9f6;
border: 0.04rem solid #ff7a3e;
}
.text {
width: 40%;
text-align: right;
.product-image {
position: relative;
width: 100%;
height: 8rem;
box-sizing: border-box;
margin-bottom: 0.5rem;
.image {
width: 100%;
height: 100%;
object-fit: contain;
}
}
}
.product-money {
font-size: 16px;
font-weight: 500;
color: #ff1b1b;
.label {
font-size: 12px;
font-weight: bold;
.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;
min-height: 2rem;
}
}
.product-store {
position: absolute;
left: 0.67rem;
bottom: 0.67rem;
.title {
width: 6rem;
.product-desc {
position: relative;
width: 100%;
font-size: 12px;
font-weight: 400;
color: #666666;
margin-left: 0.25rem;
color: #999999;
margin-bottom: 0.1rem;
.label {
width: 60%;
}
.text {
width: 40%;
text-align: right;
}
}
}
.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);
.product-money {
font-size: 16px;
font-weight: 500;
color: #ff1b1b;
.label {
font-size: 12px;
font-weight: bold;
}
}
}
@media (prefers-color-scheme: dark) {
.product-title {
color: #fff;
.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;
}
}
}
`;
// 商品详情类型
type GoodsInfoListType = InterDataType<QueryGoodsInfoByCategorySub>[0];
const ProductItemView: React.FC<{
detail: GoodsInfoListType;
}> = ({ detail }) => {
.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);
}
}
@media (prefers-color-scheme: dark) {
.product-title {
color: #fff;
}
}
`;
// 路由钩子
const router = useRouter();
// 获取最低价格
......
import React, { useEffect, useState } from 'react';
import { App } from 'antd';
import { CommonAPI } from '@/api';
const QrcodeView: React.FC<{
path?: string;
scene?: string;
}> = ({ path = 'pages/welcome/index', scene = 'type=share' }) => {
// 消息钩子
const { message } = App.useApp();
// 二维码的地址
const [qrCodeData, setQrCodeData] = useState<string>();
// 获取二维码
const getQrcodeLogin = async () => {
// 获取二维码
const res = await CommonAPI.getAppletQRCode({
page: path || 'pages/welcome/index',
scene: scene || 'type=share',
});
if (res && res.code === '200') {
if (!res.result) {
message.warning('获取登录二维码失败');
return;
}
// 设置当前登录的二维码
setQrCodeData(`data:image/png;base64,${res.result}`);
}
};
// 组件挂载
useEffect(() => {
if (!path) return;
getQrcodeLogin().then();
}, [path]);
return (
<img
className="animate__animated animate__faster animate__fadeIn h-full w-full"
src={qrCodeData}
alt="云享飞小程序"
/>
);
};
export default QrcodeView;
import React, { useEffect, useState } from 'react';
import { Empty, Rate } from 'antd';
import { useRouter } from 'next/router';
import styled from 'styled-components';
import { HomeAPI } from '@/api';
import { InterDataType } from '@/api/interface';
import {
GetCompanyInfoByBUId,
QueryBrandGoodsType,
} 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 ProductItemView from '@/components/productItem';
import ProductListView from '@/components/productList';
// 详情类型
type DetailType = InterDataType<GetCompanyInfoByBUId>;
// 商品列表类型
type GoodsInfoListType = InterDataType<QueryBrandGoodsType>;
const StoreProductDetailPage = () => {
// 路由钩子
const router = useRouter();
// 分类列表
const [categoryList, setCategoryList] = useState<CategoryType>([]);
// 店铺详情
const [storeDetail, setStoreDetail] = useState<DetailType>();
// 获取店铺详情
const getCompanyInfoByBUId = async () => {
const res = await MallAPI.getCompanyInfoByBUId({
backUserAccountId: Number(router?.query?.id),
});
if (res && res.code === '200') {
setStoreDetail(res.result);
// console.log('获取店铺详情 ===>', res);
}
};
// 商品列表
const [goodsInfoList, setGoodsInfoList] = useState<GoodsInfoListType>();
// 筛选过后的商品列表
const [goodsSelectList, setGoodsSelectList] = useState<GoodsInfoListType>();
// 分页数据
const [pagination, setPagination] = useState({
pageNo: 1,
pageSize: 18,
totalCount: 0,
});
// 获取品牌商的商品
const getQueryBrandGoods = async () => {
const res = await MallAPI.queryBrandGoods({
userAccountId: Number(router?.query?.id),
});
if (res && res.code === '200') {
setGoodsInfoList(res.result);
// console.log('商品列表 ===>', res.result);
}
};
// 获取各个目录及分类信息
const getAppCategoryInfo = async () => {
const res = await HomeAPI.appCategoryInfo({
type: 4,
});
if (res && res.code === '200') {
const mainIds = goodsInfoList?.map((i) => i?.categoryPrimaryId);
setCategoryList(
res.result
?.map((i) => ({
value: i.id,
label: i.name,
children: i.subDTOList?.map((n) => ({
value: n.id,
label: n.name,
})),
}))
?.filter((i) => mainIds?.includes(i?.value)),
);
// console.log('获取各个目录及分类信息 ===>', res.result);
}
};
// 选择监听
const handleSelect = (e: { main?: number; second?: number[] }) => {
const list = goodsInfoList?.filter(
(i) => e.second?.includes(i?.categorySubId),
);
setGoodsSelectList(list);
setPagination({
...pagination,
pageNo: 1,
totalCount: list?.length || 0,
});
// console.log('选择监听 --->', e, list);
};
// 翻页回调
const handlePageChange = (pageNo: number, pageSize: number) => {
setPagination({ ...pagination, pageNo, pageSize });
};
// 组件挂载
useEffect(() => {
if (!router?.query?.id) return;
getCompanyInfoByBUId().then();
getQueryBrandGoods().then();
}, [router?.query]);
// 商品列表监听
useEffect(() => {
if (!goodsInfoList) return;
getAppCategoryInfo().then();
}, [goodsInfoList]);
return (
<LayoutView>
<StoreProductDetailWrap>
{/* 面包屑 */}
<BreadcrumbView />
<div className="detail-head flex w-full justify-start align-middle">
<div className="head-image">
<img
className="image"
src={storeDetail?.brandLogo}
alt={storeDetail?.companyName}
/>
</div>
<div className="head-content">
<div className="content-title">{storeDetail?.companyName}</div>
<div className="content-star flex">
<div className="label">星级服务</div>
<div className="star">
<Rate
disabled
defaultValue={Number(storeDetail?.score) || 4}
className="text-xs text-primary"
/>
</div>
</div>
<div className="content-type">{storeDetail?.content}</div>
</div>
</div>
{/* 类型筛选 */}
<CategorySelectView list={categoryList} onSelect={handleSelect} />
{/* 产品列表 */}
<ProductListView pagination={pagination} onChange={handlePageChange}>
{goodsSelectList?.length ? (
goodsSelectList?.map((i, j) => (
<ProductItemView key={j} detail={i as any} />
))
) : (
<div className="list-empty flex-center">
<Empty />
</div>
)}
</ProductListView>
</StoreProductDetailWrap>
</LayoutView>
);
};
export default StoreProductDetailPage;
// 样式
const StoreProductDetailWrap = styled.div`
position: relative;
max-width: 1190px;
box-sizing: border-box;
padding: 1rem 0 0 0;
margin: 0 auto;
.detail-head {
margin-bottom: 1rem;
.head-content {
max-width: calc(100% - 10rem);
}
.head-image {
position: relative;
width: 3.5rem;
height: 3.5rem;
margin-right: 0.75rem;
.image {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.head-content {
.content-title {
font-weight: bold;
margin-bottom: 0.25rem;
}
.content-star {
margin-bottom: 0.25rem;
.label {
margin-right: 0.5rem;
background: #fff2ee;
border-radius: 0.1rem;
border: 0.02rem solid #ff6242;
font-weight: 400;
font-size: 0.65rem;
color: #ff522f;
box-sizing: border-box;
padding: 0 0.25rem;
}
}
.content-type {
font-weight: 400;
font-size: 0.75rem;
color: #666666;
}
}
}
`;
import React from 'react';
import styled from 'styled-components';
import { InterDataType } from '@/api/interface';
import { AppCategoryInfoType } from '@/api/interface/home';
// 分类类型
type CategoryType = InterDataType<AppCategoryInfoType>;
const StoreProductCategoryView: React.FC<{
category: CategoryType;
current: number;
onSelect: (index: number) => void;
}> = ({ category, current, onSelect }) => {
return (
<StoreProductCategoryWrap className="flex-center">
<div className="category-label">所有分类:</div>
{category?.map((i, j) => (
<div
key={j}
className={`category-item ${current === j && 'item-active'}`}
onClick={() => onSelect?.(j)}
>
{i.name}
</div>
))}
</StoreProductCategoryWrap>
);
};
export default StoreProductCategoryView;
// 样式
const StoreProductCategoryWrap = styled.div`
position: relative;
width: 100%;
min-height: 3rem;
background: #2f2f2f;
.category-label {
font-weight: 400;
color: #ffffff;
line-height: 1.11rem;
user-select: none;
}
.category-item {
position: relative;
font-weight: 400;
color: #ffffff;
line-height: 3rem;
box-sizing: border-box;
padding: 0 1.11rem;
cursor: pointer;
&:hover {
background: #9f9f9f;
}
}
.item-active {
background: #9f9f9f;
}
`;
import React from 'react';
import { Input, Rate } from 'antd';
import styled from 'styled-components';
import { InterDataType } from '@/api/interface';
import { GetCompanyInfoByBUId } from '@/api/interface/mall';
import QrcodeView from '@/components/qrcodeView';
// 详情类型
type DetailType = InterDataType<GetCompanyInfoByBUId>;
const StoreProductHeadView: React.FC<{ detail: DetailType }> = ({ detail }) => {
return (
<StoreProductHeadWrap className="flex-center select-none">
<div className="head-image">
<img
className="image"
src={detail?.brandLogo}
alt={detail?.companyName}
/>
</div>
<div className="head-content">
<div className="title mb-1">
<span className="text-777">店铺:</span>
{detail?.fullName || detail?.companyName || detail?.brandName}
</div>
<div className="star flex-start mb-1">
<div className="label">服务星级</div>
<Rate
disabled
defaultValue={Number(detail?.score) || 4}
className="text-xs text-primary"
/>
</div>
<div className="text text-666 text-ellipsis">{detail?.content}</div>
</div>
<div className="head-search">
<Input.Search
className="search-box"
placeholder="科比特航空"
enterButton="搜索"
size={'large'}
bordered={true}
/>
</div>
<div className="head-qrcode">
<QrcodeView
path={`page-product/product-store/index`}
scene={`id=${detail?.id}`}
/>
</div>
<div className="head-text text-777">扫码进入小程序店铺</div>
</StoreProductHeadWrap>
);
};
export default StoreProductHeadView;
// 样式
const StoreProductHeadWrap = styled.div`
position: relative;
width: 100%;
box-sizing: border-box;
background: white;
padding: 0.5rem 0;
//min-height: 4.78rem;
.head-image {
position: relative;
width: 3.5rem;
height: 3.5rem;
margin-right: 0.75rem;
.image {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.head-content {
margin-right: 10rem;
.star {
.label {
margin-right: 0.5rem;
background: #fff2ee;
border-radius: 0.1rem;
border: 0.02rem solid #ff6242;
font-weight: 400;
font-size: 0.65rem;
color: #ff522f;
box-sizing: border-box;
padding: 0 0.25rem;
}
}
}
.head-search {
position: relative;
width: 24rem;
box-sizing: border-box;
margin-right: 10rem;
}
.head-qrcode {
position: relative;
width: 3.78rem;
height: 3.78rem;
margin-right: 1rem;
}
`;
import React, { useEffect, useState } from 'react';
import { Menu, MenuProps } from 'antd';
import styled from 'styled-components';
import { InterDataType } from '@/api/interface';
import { AppCategoryInfoType } from '@/api/interface/home';
// 分类类型
type CategoryType = InterDataType<AppCategoryInfoType>;
const StoreProductMenuView: React.FC<{
category: CategoryType;
current: number;
onCurrent: (index: number) => void;
onSelect: ({ main, second }: { main?: number; second?: number[] }) => void;
}> = ({ category, current, onSelect, onCurrent }) => {
// 菜单数据
const [items, setItems] = useState<MenuProps['items']>();
// 当前选项
const [selectedKeys, setSelectedKeys] = useState<MenuProps['selectedKeys']>();
// 转换菜单数据
const transMenuList = () => {
setItems(
category?.map((i, _j) => ({
label: i?.name,
key: String(i?.id),
children: i?.subDTOList?.map((n, _m) => ({
label: n?.name,
key: n?.id,
})),
})),
);
};
// 获取当前默认选择的选项
const getSelectedKeys = () => {
const index = category?.[current]?.id;
setSelectedKeys([String(index)]);
onSelect?.({
main: Number(index),
second: category
?.find((i) => i?.id === index)
?.subDTOList?.map((i) => i?.id),
});
};
// 选项展开
const handleOpen: MenuProps['onOpenChange'] = (e) => {
const select = e?.at(-1) || selectedKeys?.[0];
const index = category?.findIndex((i) => String(i?.id) === select);
setSelectedKeys([String(select)]);
onCurrent?.(index);
};
// 筛选事件
const handleSelect: MenuProps['onSelect'] = (e) => {
setSelectedKeys(e.keyPath);
onSelect?.({
main: Number(e?.keyPath?.[0]),
second: [Number(e?.key)],
});
};
// 组件挂载
useEffect(() => {
transMenuList();
}, []);
// 副作用
useEffect(() => {
getSelectedKeys();
}, [current]);
return (
<StoreProductMenuWrap>
<div className="menu-head">商品分类</div>
{items && (
<Menu
selectedKeys={selectedKeys}
openKeys={selectedKeys}
mode={'inline'}
items={items}
onSelect={handleSelect}
onOpenChange={handleOpen}
/>
)}
</StoreProductMenuWrap>
);
};
export default StoreProductMenuView;
// 样式
const StoreProductMenuWrap = styled.div`
position: relative;
width: 20%;
box-sizing: border-box;
border: 1px solid #e5e5e5;
border-top: none;
margin-bottom: 10rem;
//background: lightpink;
.menu-head {
width: 100%;
height: 2rem;
background: #2f2f2f;
font-weight: 400;
color: #ffffff;
box-sizing: border-box;
line-height: 2rem;
padding-left: 0.5rem;
}
`;
import React from 'react';
import styled from 'styled-components';
import { InterDataType } from '@/api/interface';
import { GetCompanyInfoByBUId } from '@/api/interface/mall';
// 详情类型
type DetailType = InterDataType<GetCompanyInfoByBUId>;
const StoreProductSwiperView: React.FC<{ detail: DetailType }> = ({
detail,
}) => {
return (
<StoreProductSwiperWrap>
<div>StoreProductSwiperView</div>
</StoreProductSwiperWrap>
);
};
export default StoreProductSwiperView;
// 样式
const StoreProductSwiperWrap = styled.div`
position: relative;
width: 100%;
height: 28.6rem;
box-sizing: border-box;
background: #9f9f9f;
display: none;
`;
......@@ -17,10 +17,11 @@ module.exports = {
xl: '1190px',
},
colors: {
333: '#333',
777: '#777',
999: '#999',
aaa: '#aaa',
333: '#333333',
666: '#666666',
777: '#777777',
999: '#999999',
aaa: '#aaaaaa',
tag: '#001D85',
primary: '#ff552d',
},
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论