Commit 3fcdf329 authored by GuanHua's avatar GuanHua

feat:店铺商城页面和分类商品加载的骨架屏组件

parent 11004e1e
......@@ -19,6 +19,20 @@ const shopRoute = {
component: '@/pages/lxMall/commodity',
},
{
// 积分商城
path: `/shop/pointsMall`,
name: 'shopPointsMall',
key: 'shopPointsMall',
component: '@/pages/lxMall/pointsMall',
},
{
// 资讯
path: `/shop/infomation`,
name: 'shopInfomation',
key: 'shopInfomation',
component: '@/pages/lxMall/information',
},
{
// 关于我们
path: `/shop/about`,
name: 'shopAbout',
......
......@@ -20,22 +20,28 @@ const userLoginLists = [
'/user/login',
'/user/register',
]
// let routeAuthUrls: any[] = []
// 路由白名单
const whiteLists = [
...userLoginLists,
// 商城相关路由
const mallLists = [
'/',
'/channelmall',
'/shop',
'/purchaseOnline',
'/pointsMall',
// '/memberCenter',
'/memberCenter/noAuth',
'/commodity',
'/shops',
'/shop/commodity/detail',
'/shop/commodity',
'/infomation',
]
// let routeAuthUrls: any[] = []
// 路由白名单
const whiteLists = [
...userLoginLists,
...mallLists,
// '/memberCenter',
'/memberCenter/noAuth',
'/403',
'/404',
'/500',
......
......@@ -14,9 +14,11 @@ export default {
'menu.shopCommodity': '商品',
'menu.purchaseOnline': '在线求购',
'menu.pointsMall': '积分商城',
'menu.shopPointsMall': '积分兑换',
'menu.shops': '店铺',
'menu.shopAbout': '关于我们',
'menu.infomation': '资讯',
'menu.shopInfomation': '资讯',
'menu.admin': '管理页',
'menu.admin.sub-page': '二级管理页',
'menu.login': '登录',
......
......@@ -52,7 +52,7 @@ const Commodity: React.FC<CommodityPropsType> = (props) => {
const [totalCount, setTotalCount] = useState<number>(0)
// const [filterParam, setFilterParam] = useState<filterQuery | {}>({})
const filterConfig = [FILTER_TYPE.commonlyUsed, FILTER_TYPE.category, FILTER_TYPE.style, FILTER_TYPE.brand, FILTER_TYPE.price, FILTER_TYPE.useArea, FILTER_TYPE.commodityType]
console.log(layoutType, "layoutType")
useEffect(() => {
fetchCommodityList()
}, [filterParam, current])
......
import React from 'react'
import cx from 'classnames'
import { Skeleton } from 'antd'
import { GetSearchShopEnterpriseGetCommodityListResponseDetail } from '@/services'
import creditIcon from '@/assets/imgs/credit_icon.png'
import styles from './list.less'
......@@ -50,7 +51,9 @@ const CommodityList: React.FC<CommodityListPropsType> = (props) => {
<div key={item.id} className={cx(styles.commodity_list_item, styles.row)}>
<a href={`/${layoutType === 'channel' ? 'channelmall' : 'shop'}/commodity/detail?id=${item.id}&type=${item.priceType}`} target="_blank">
<div className={styles.goods_img}>
<img src={item.commodityPic || "https://img.alicdn.com/bao/uploaded/i1/691602756/O1CN013mdkHl1WEI92iLR75_!!691602756.jpg_400x400q60.jpg"} />
{
item.commodityPic ? <img src={item.commodityPic} /> : <Skeleton.Image style={{ width: 220, height: 220 }} />
}
</div>
<div className={styles.info_box}>
{
......
......@@ -54,7 +54,7 @@
margin-left: 55px;
flex: 1;
width: 0;
padding-right: 68px;
// padding-right: 68px;
&_tabs {
display: flex;
......@@ -74,6 +74,23 @@
}
}
.honors_list {
display: flex;
flex-wrap: wrap;
margin: 0 -16px;
&_item {
width: 160px;
height: 107px;
margin: 30px 16px 0 16px;
&>img {
width: 160px;
height: 107px;
}
}
}
&_title {
font-size: 24px;
font-weight: 500;
......@@ -83,6 +100,7 @@
&_brief {
color: #666666;
padding-right: 68px;
}
&_more {
......
import React, { useState } from 'react'
import React, { useState, Fragment } from 'react'
import cx from 'classnames'
import { ArrowRightOutlined } from '@ant-design/icons'
import { Carousel } from 'antd'
import { Carousel, Skeleton } from 'antd'
import styles from './index.less'
enum TAB_TYPE {
introduction = 1,
honors = 2
}
const AboutUs: React.FC = () => {
const [aboutTab, setAboutTab] = useState<number>(1) //1:公司简介 2:资质荣誉
const [aboutTab, setAboutTab] = useState<number>(TAB_TYPE.introduction) //1:公司简介 2:资质荣誉
const handleChangeAboutTab = (tab: number) => {
if (aboutTab !== tab) {
......@@ -14,8 +19,10 @@ const AboutUs: React.FC = () => {
}
}
const list = Array.apply({}, new Array(6))
return (
<div className={styles.channel_info_about}>
<div className={styles.channel_info_about} id="about_us">
<div className={styles.channel_info_about_container}>
<div className={styles.channel_info_about_img}>
<Carousel className={styles.channel_info_about_img_list} pauseOnDotsHover>
......@@ -40,14 +47,34 @@ const AboutUs: React.FC = () => {
<div className={cx(styles.channel_info_about_info_tabs_item, aboutTab === 1 ? styles.active : '')} onClick={() => handleChangeAboutTab(1)}>公司简介</div>
<div className={cx(styles.channel_info_about_info_tabs_item, aboutTab === 2 ? styles.active : '')} onClick={() => handleChangeAboutTab(2)}>资质荣誉</div>
</div>
<div className={styles.channel_info_about_info_title}>温州市龙昌皮具有限公司</div>
<p className={styles.channel_info_about_info_brief}>
公司位于温州,多年行业经验,专业经营各种成品真皮,包括全粒面牛皮,修面皮,漆色皮,打腊皮,水腊皮……二层皮胚,硅胶二层等,产品主要用于时尚女鞋、男鞋、箱包、皮带、服装、汽车内饰等高端皮具制品行业。
本着质量保证,品种多样,现货充足,款式新颖的经营特色和薄利多销的原则,为客户提供长期优质的服务!
</p>
<p>
同时公司长期寻找各皮厂合作,要求皮厂有自已的生产线及高性价比特色产品,可作为普通供应商合作,也可作为其核心经销商战略合作。欢迎来人来样,当面洽谈。
</p>
{
aboutTab === TAB_TYPE.introduction ? (
<Fragment>
<div className={styles.channel_info_about_info_title}>温州市龙昌皮具有限公司</div>
<p className={styles.channel_info_about_info_brief}>
公司位于温州,多年行业经验,专业经营各种成品真皮,包括全粒面牛皮,修面皮,漆色皮,打腊皮,水腊皮……二层皮胚,硅胶二层等,产品主要用于时尚女鞋、男鞋、箱包、皮带、服装、汽车内饰等高端皮具制品行业。
本着质量保证,品种多样,现货充足,款式新颖的经营特色和薄利多销的原则,为客户提供长期优质的服务!
</p>
<p className={styles.channel_info_about_info_brief}>
同时公司长期寻找各皮厂合作,要求皮厂有自已的生产线及高性价比特色产品,可作为普通供应商合作,也可作为其核心经销商战略合作。欢迎来人来样,当面洽谈。
</p>
</Fragment>
) : (
<Fragment>
<div className={styles.honors_list}>
{
list.map((_, index) => (
<div key={`honors_list_item_${index}`} className={styles.honors_list_item}>
<Skeleton.Image style={{ width: 160, height: 107 }} />
</div>
))
}
</div>
</Fragment>
)
}
<div className={styles.channel_info_about_info_more}>
<span>查看更多</span>
<ArrowRightOutlined />
......
......@@ -76,10 +76,6 @@ const UseArea: React.FC<UseAreaPropsType> = (props) => {
}
}, [layoutType])
useEffect(() => {
console.log(selectCity, "selectCity")
}, [selectCity])
const initAreaData = (data: GetSearchShopEnterpriseGetAreaResponse) => {
if (!!data) {
return data.map(item => {
......
......@@ -17,6 +17,9 @@ const FloorAnchor: React.FC<FloorAnchorPropsType> = (props) => {
<div className="anchor_wrap">
<Anchor className="anchor" offsetTop={120} >
{
type === 'shop' && <Anchor.Link href={`#about_us`} title="关于我们" />
}
{
anchorList.map(item => (
<Anchor.Link key={item.id} href={`#floorline_${item.id}`} title={item.name} />
))
......
This diff is collapsed.
import React from 'react'
import { RightOutlined, CaretRightOutlined } from '@ant-design/icons'
import { Skeleton } from 'antd'
import cx from 'classnames'
import { LAYOUT_TYPE } from '@/constants'
import styles from './index.less'
interface FloorSkeletonPropsType {
type: LAYOUT_TYPE.mall | LAYOUT_TYPE.shop | LAYOUT_TYPE.channel
}
const FloorSkeleton: React.FC<FloorSkeletonPropsType> = (props) => {
const { type } = props
let dataList = Array.apply({}, new Array(9))
let goodsList = Array.apply({}, new Array(10))
let brandList = Array.apply({}, new Array(6))
let shopList = Array.apply({}, new Array(5))
const renderSkeleton = () => {
switch (type) {
case LAYOUT_TYPE.mall:
return (
<div className={styles.floor_line} >
<div className={styles.floor_line_container}>
<div className={styles.floor_line_name}>
<span className={styles.floor_line_name_text}><Skeleton.Button style={{ width: 200 }} active /></span>
<span className={styles.floor_line_more}><Skeleton.Button style={{ width: 200 }} active /></span>
</div>
<div className={styles.floor_line_body}>
<div className={styles.floor_line_horizontal}>
<section className={styles["floor-line-category"]} >
<Skeleton.Button active className={styles.skeleton_image} />
</section>
<div className={styles.floor_line_vertical}>
<section className={styles["floor-line-header"]} >
<div className={styles["floor-line-header-count-info"]}>
<div className={styles["floor-line-header-count-info-item"]}>
<Skeleton.Button style={{ width: 150 }} active />
</div>
<div className={styles["floor-line-header-count-info-item"]}>
<Skeleton.Button style={{ width: 150 }} active />
</div>
<section className={styles["floor-line-banner"]}>
<Skeleton.Button active className={styles.skeleton_image} />
</section>
</div>
</section>
<div className={styles.floor_line_horizontal}>
<section className={styles["floor-line-goods"]} >
<div className={styles.goods_list}>
{
goodsList.map((_, index) => (
<div key={`goods_list_item_${index}`} className={styles.goods_list_item}>
<div className={styles.goods_img}>
<Skeleton.Button active className={styles.skeleton_image} />
</div>
<div className={styles.goods_name}>
<Skeleton.Button active style={{ width: 120, height: 24 }} />
</div>
</div>
))
}
</div>
</section>
<section className={styles["floor-line-shop"]}>
<div className={styles.shop_title}><Skeleton.Button active style={{ width: 120, height: 24 }} /></div>
<div className={styles.shop_list}>
{
shopList.map((_, index) => (
<div key={`shop_list_item-${index}`} className={styles.shop_list_item}>
<div className={styles.shop_logo}><Skeleton.Avatar active /></div>
<div className={styles.shop_name}>
<Skeleton.Button active style={{ width: 160, height: 24 }} />
</div>
</div>
))
}
</div>
</section>
</div>
</div>
</div>
<section className={styles["floor-line-brand"]} >
<div className={styles.brand_list}>
{
brandList.map((_, index) => (
<div className={styles.brand_list_item} key={`brand_list_item_${index}`}>
<div className={styles.brand_img_box}>
<Skeleton.Button active className={styles.skeleton_image} />
</div>
</div>
))
}
</div>
</section>
</div>
</div>
</div>
)
case LAYOUT_TYPE.shop:
case LAYOUT_TYPE.channel:
return (
<div className={styles.shop_floor_line} >
<div className={styles.floor_line_container}>
<div className={styles.floor_line_name}>
<span className={styles.floor_line_name_text}><Skeleton.Button style={{ width: 200 }} active /></span>
<span className={styles.floor_line_more}><Skeleton.Button style={{ width: 200 }} active /></span>
</div>
<div className={styles.floor_line_body}>
<section className={styles['shop-floor-line-category']} >
<Skeleton.Button active className={styles.skeleton_image} />
</section>
<section className={styles['shop-floor-line-goods']}>
<div className={styles.goods_list}>
<div className={cx(styles.goods_list_item, styles.empty)}>
</div>
{
dataList && dataList.map((_, index) => (
<div key={`goods_list_item_${index}`} className={styles.goods_list_item}>
<div className={styles.goods_img}>
<Skeleton.Button active className={styles.skeleton_image} />
</div>
<div className={styles.goods_name}>
<Skeleton.Input active style={{ width: 200 }} />
</div>
</div>
))
}
</div>
</section>
</div>
</div>
</div >
)
}
}
return renderSkeleton()
}
export default FloorSkeleton
import React from 'react'
import { Link } from 'umi'
import cx from 'classnames'
import { LAYOUT_TYPE } from '@/constants'
import Category from '../Category'
import styles from './index.less'
interface MainNavPropsType {
menuData: any;
pathname: string;
type: "shop" | "mall" | "channel"
type: "shop" | "mall" | "channel",
shopId?: string
}
const MainNav: React.FC<MainNavPropsType> = (props) => {
const { menuData, pathname, type } = props
const { menuData, pathname, type, shopId = "" } = props
console.log(type, "type")
return (
......@@ -22,7 +24,7 @@ const MainNav: React.FC<MainNavPropsType> = (props) => {
{
menuData && menuData.map(item => !item.hide && (
<li className={cx(styles.nav_item, item.path === pathname ? styles.active : '')} key={item.key}>
<Link to={item.path}>{item.name}</Link>
<Link to={type === LAYOUT_TYPE.shop ? `${item.path}?shopId=${shopId}` : item.path}>{item.name}</Link>
</li>
))
}
......
......@@ -6,8 +6,9 @@ import { inject, observer } from 'mobx-react'
import FloorAnchor from '../components/FloorAnchor'
import { PublicApi } from '@/services/api'
import { Advert, FloorLine } from 'lingxi-design-ui'
import FloorSkeleton from '../components/FloorSkeleton'
import { GetTemplatePlatformFindAllFirstCategoryResponse } from '@/services'
import Loading from '../components/Loading'
import { LAYOUT_TYPE } from '@/constants'
import './index.less'
interface MallIndexPropsType {
......@@ -105,7 +106,7 @@ const MallIndex: React.FC<MallIndexPropsType> = (props) => {
</FloorLine.FloorHeader>
<FloorLine.Horizontal>
<FloorLine.Goods goodsList={categoryDetail.goodsBOList} />
<FloorLine.Shops shopsList={categoryDetail.shopBOList} />
<FloorLine.Shops shopsList={categoryDetail.shopBOList} linkUrl={`/shop?shopId=${btoa(JSON.stringify({ shopId: 2, meberId: 6 }))}`} />
</FloorLine.Horizontal>
</FloorLine.Vertical>
</FloorLine.Horizontal>
......@@ -119,6 +120,7 @@ const MallIndex: React.FC<MallIndexPropsType> = (props) => {
return (
<div className="mall_index">
<a href={`/shop?shopId=${btoa(JSON.stringify({ shopId: 2, memberId: 6 }))}`}>店铺链接测试</a>
{
useMemo(() => <Advert type="banner" advertList={firstAdvertList} hasQuickNav={true} ><QuickNav /></Advert>, [firstAdvertList])
}
......@@ -127,7 +129,7 @@ const MallIndex: React.FC<MallIndexPropsType> = (props) => {
useMemo(() => <Advert type="interact" advertList={secondAdvertList} />, [secondAdvertList])
}
{
categoryComponents ? categoryComponents : <Loading />
categoryComponents ? categoryComponents : <FloorSkeleton type={LAYOUT_TYPE.mall} />
}
<FindMore />
<Information />
......
......@@ -3,7 +3,7 @@ import {
BasicLayoutProps as ProLayoutProps,
getMenuData
} from '@ant-design/pro-layout';
import { useIntl } from 'umi';
import { useIntl, history } from 'umi';
import TopBar from '../components/TopBar'
import { inject, observer } from 'mobx-react'
import ShopHeader from '../components/ShopHeader'
......@@ -21,12 +21,22 @@ interface LXMallLayoutPropsType {
SiteStore?: any;
}
interface queryType {
shopId: number;
memberId: number;
}
const LXShopLayout: React.FC<LXMallLayoutPropsType> = (props) => {
const { children, location } = props
const [templateName] = useState<string>('theme-shop-science')
const { shopId } = location.query
const [query, setQuery] = useState<any>({})
useEffect(() => {
console.log('当前使用店铺模板')
let queryParam = shopId ? atob(shopId) : undefined
queryParam = queryParam ? JSON.parse(queryParam) : {}
setQuery(queryParam)
let body = document.getElementsByTagName('body')[0];
body.className = templateName;
}, [])
......@@ -40,12 +50,14 @@ const LXShopLayout: React.FC<LXMallLayoutPropsType> = (props) => {
<TopBar />
<div className={styles.content}>
<ShopHeader />
<MainNav menuData={menuData} pathname={location.pathname} type="shop" />
<MainNav menuData={menuData} pathname={location.pathname} type="shop" shopId={shopId} />
{
children && React.Children.map(children, (child: any) => {
return React.cloneElement(child,
{
layoutType: 'shop'
layoutType: 'shop',
shopId: query.shopId,
memberId: query.memberId
},
);
})
......
......@@ -2,20 +2,25 @@ import React, { useEffect, useMemo, useState } from 'react'
import Information from '../components/Information'
import FloorAnchor from '../components/FloorAnchor'
import CommonTitle from '../components/CommonTitle'
import FloorSkeleton from '../components/FloorSkeleton'
import { Advert, ShopFloorLine } from 'lingxi-design-ui'
import AboutUs from '../components/AboutUs'
import { inject, observer } from 'mobx-react'
import { PublicApi } from '@/services/api'
import Loading from '../components/Loading'
import { LAYOUT_TYPE } from '@/constants'
import { GetTemplatePlatformFindAllFirstCategoryResponse } from '@/services'
import styles from './index.less'
interface ChannelIndexPropsType {
SiteStore?: any;
location: any
memberId: number;
shopId: number;
}
const ShopIndex: React.FC<ChannelIndexPropsType> = (props) => {
const { shopTemplateId } = props.SiteStore
const { shopId, memberId } = props
const [categoryList, setCategoryList] = useState<GetTemplatePlatformFindAllFirstCategoryResponse>([])
const [firstAdvertList, setFirstAdvertList] = useState([])
const [secondAdvertList, setSecondAdvertList] = useState([])
......@@ -30,7 +35,8 @@ const ShopIndex: React.FC<ChannelIndexPropsType> = (props) => {
const findFirstAdvertsByType = () => {
let params = {
templateId: shopTemplateId,
type: 1
type: 1,
memberId
}
//@ts-ignore
PublicApi.getTemplateShopFindAdvertsByType(params).then(res => {
......@@ -43,7 +49,8 @@ const ShopIndex: React.FC<ChannelIndexPropsType> = (props) => {
const findSecondAdvertsByType = () => {
let params = {
templateId: shopTemplateId,
type: 2
type: 2,
memberId
}
//@ts-ignore
PublicApi.getTemplateShopFindAdvertsByType(params).then(res => {
......@@ -58,7 +65,7 @@ const ShopIndex: React.FC<ChannelIndexPropsType> = (props) => {
*/
const fetchFirstCategory = () => {
return new Promise((resolve) => {
PublicApi.getTemplateShopFindAllFirstCategory().then(res => {
PublicApi.getTemplateShopFindAllFirstCategory({ memberId }).then(res => {
if (res.code === 1000) {
setCategoryList(res.data)
resolve(res.data)
......@@ -68,13 +75,14 @@ const ShopIndex: React.FC<ChannelIndexPropsType> = (props) => {
}
/**
* 获取一级品类详细信息
*/
* 获取一级品类详细信息
*/
const fetchCategoryById = (categoryId) => {
return new Promise((resolve) => {
let param = {
templateId: shopTemplateId,
categoryId
categoryId,
memberId
}
// @ts-ignore
......@@ -109,17 +117,17 @@ const ShopIndex: React.FC<ChannelIndexPropsType> = (props) => {
<div className={styles.shop_index}>
<div className={styles.banner}>
{
useMemo(() => <Advert type="banner" advertList={firstAdvertList} hasQuickNav={false} />, [firstAdvertList])
useMemo(() => <Advert type="banner" visible={firstAdvertList.length > 0} advertList={firstAdvertList} hasQuickNav={false} />, [firstAdvertList])
}
</div>
<FloorAnchor anchorList={categoryList} type="shop" />
<CommonTitle title="关于我们" type="primary" />
<AboutUs />
<CommonTitle title="热销商品" type="primary" />
{
categoryComponents ? categoryComponents : <Loading />
categoryComponents ? categoryComponents : <FloorSkeleton type={LAYOUT_TYPE.shop} />
}
<Advert type="service" advertList={secondAdvertList} />
<CommonTitle title="关于我们" type="primary" />
<AboutUs />
<Advert visible={secondAdvertList.length > 0} type="service" advertList={secondAdvertList} />
<Information />
</div >
)
......
......@@ -37,7 +37,6 @@ const CityCascader: React.FC<CitySelectPropsType> = (props) => {
fetchAreaList()
}, [])
const getProviceById = (id: number) => {
let result: number = 0
provinceData && provinceData.map(item => {
......@@ -89,6 +88,7 @@ const CityCascader: React.FC<CitySelectPropsType> = (props) => {
}
setProvinceData(tempProvinceData)
setCityData(tempCityData)
}
......@@ -154,7 +154,7 @@ const CityCascader: React.FC<CitySelectPropsType> = (props) => {
onChange={(value) => onSecondCityChange(value, item.provinceCode, item.index)}
placeholder="请选择"
>
{(item.provinceCode && !isEmpty(cityData)) ? cityData[item.provinceCode].map((item: any) => (
{(item.provinceCode && !isEmpty(cityData)) ? cityData[item.provinceCode] && cityData[item.provinceCode].map((item: any) => (
<Option value={item.value} key={item.value}>{item.lable}</Option>
)) : null}
</Select>
......
......@@ -109,6 +109,7 @@ class ApiRequest {
if (res.code === 1101) {
removeAuth()
history.replace('/user/login')
message.destroy()
message.error(res.message)
return false
}
......@@ -119,6 +120,7 @@ class ApiRequest {
} else {
// 使用resolve将数据返回, 请求时需手动处理data为null的情况
resolve(res)
message.destroy()
message.error(res.message)
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment