Commit 0b7c7d58 authored by Bill's avatar Bill

Merge branch 'dev' of 10.0.0.22:lingxi/lingxi-business-paltform into dev

parents f1b87090 9104204b
/*
* @Author: ghua
* @Date: 2021-02-26 11:00:24
* @LastEditTime: 2021-03-01 10:15:04
* @LastEditors: Please set LastEditors
* @Description: app渠道商城行业资讯组件
* @FilePath: /lingxi-business-paltform/src/pages/editor/configs/componentConfigs/LingXiUI/MobileChannelHeaderNav.ts
*/
import { ComponentConfigTypes, PROPS_TYPES } from 'lingxi-editor-core';
const MobileChannelCategory: ComponentConfigTypes = {
propsConfig: {
componentType: {
label: '编辑',
type: PROPS_TYPES.moibileChannelCategory
},
},
};
export default MobileChannelCategory;
......@@ -32,6 +32,7 @@ import MobileBottomNavigation from './MobileBottomNavigation'
import MobileChannelHeaderNav from './MobileChannelHeaderNav'
import MobileChannelGoodsCard from './MobileChannelGoodsCard'
import MobileChannelInformation from './MobileChannelInformation'
import MobileChannelCategory from './MobileChannelCategory'
export default {
TopBar,
......@@ -57,6 +58,7 @@ export default {
MobileChannelGoodsCard,
MobileChannelInformation,
MobileBottomNavigation,
MobileChannelCategory,
...FloorLine,
...ShopFloorLine,
...ShowCase,
......
@import '../style/common.less';
.@{prefixCls}-category-list {
background-color: #FFF;
min-height: 78px;
padding-top: 12px;
margin: 0 8px;
border-radius: 8px;
&.hide {
display: none;
}
&-carousel {
position: relative;
}
&-list {
display: flex;
flex-wrap: wrap;
&-item {
width: 20%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-bottom: 12px;
color: #606266;
&-navIcon {
width: 48px;
height: 48px;
background-color: #F9F9F9;
border-radius: 50%;
margin-bottom: 6px;
}
}
&-pagination {
padding-bottom: 12px;
&-wrap {
display: flex;
justify-content: center;
}
&-item {
width: 24px;
height: 3px;
background-color: #EEF0F3;
border-radius: 2px;
&.active {
background-color: @headerNavScienceColor;
}
}
}
}
}
import React, { useEffect, useState, useRef } from 'react'
import { ConfigConsumer } from '../Generator'
import { Carousel } from 'antd'
import { isEmpty, isArray } from 'lodash'
import cx from 'classnames'
import { arrayGroupsByCount } from '@/utils'
import defaultImg from './default_img.png'
import styles from './index.less'
interface DataItemType {
title: string,
id: number,
imageUrl: string,
}
export interface MobileQuickNavPropsType {
className?: string,
style?: React.CSSProperties,
visible?: boolean,
dataList: DataItemType[],
}
const MobileChannelCategory: React.FC<MobileQuickNavPropsType> = (props) => {
const { children, className, style, dataList, visible, ...others } = props
const [navList, setNavList] = useState<any>([])
const [currentIndex, setCurrentIndex] = useState<number>(0)
const carouselRef: any = useRef()
useEffect(() => {
console.log(dataList, "dataList")
if(isArray(dataList)) {
setNavList(arrayGroupsByCount(dataList, 10))
}
}, [dataList])
const renderChildren = ({ getPrefixCls }: any) => {
const prefixCls = getPrefixCls("category-list");
const classNameString = cx(styles[`${prefixCls}`], !visible ? styles.hide: null)
const handleCarouselChange = (current: number) => {
setCurrentIndex(current)
}
const handleGoSlide = (e, index: number) => {
e.stopPropagation()
carouselRef.current.goTo(index, false)
}
return !isEmpty(navList) ? (
<div className={classNameString} style={style} {...others}>
<Carousel
ref={carouselRef}
dots={false}
afterChange={handleCarouselChange}
className={styles["lingxi-category-list-carousel"]}
>
{
!isEmpty(navList) && navList.map((listItem, listIndex) => (
<div key={`lingxi-category-list-list-${listIndex}`} >
<div className={styles["lingxi-category-list-list"]}>
{
listItem.map((item: DataItemType, index: number) => (
<div className={styles["lingxi-category-list-list-item"]} key={`lingxi-category-list-list-item-${index}`}>
{
item.imageUrl ? <img className={styles["lingxi-category-list-list-item-navIcon"]} src={item.imageUrl} /> :
<img className={styles["lingxi-category-list-list-item-navIcon"]} src={defaultImg} />
}
<span className={styles["lingxi-category-list-list-item-navText"]}>{item.title}</span>
</div>
))
}
</div>
</div>
))
}
</Carousel>
{
navList.length > 1 && (
<div className={styles["lingxi-category-list-list-pagination"]} style={{ justifyContent: 'center' }}>
<div className={styles["lingxi-category-list-list-pagination-wrap"]} style={styles.paginationStyle}>
{
navList.map((_: any, listIndex: number) => (
<div
key={`lingxi-category-list-list-pagination-item-${listIndex}`}
className={cx(styles["lingxi-category-list-list-pagination-item"], currentIndex === listIndex ? styles.active : {})}
onClick={(e) => handleGoSlide(e, listIndex)}
/>
))
}
</div>
</div>
)
}
</div>
) : null
}
return <ConfigConsumer>{renderChildren}</ConfigConsumer>
}
MobileChannelCategory.defaultProps = {
dataList: [],
visible: true,
}
export default MobileChannelCategory
......@@ -9,3 +9,4 @@
export { default as MobileChannelHeaderNav } from './ChannelHeaderNav'
export { default as MobileChannelGoodsCard } from './ChannelGoodsCard'
export { default as MobileChannelInformation } from './ChannelInformation'
export { default as MobileChannelCategory } from './ChannelCategory'
import React from 'react'
import { numFormat, priceFormat } from '@/utils/numberFomat'
import ImageBox from '@/components/ImageBox'
const showMainPic = (mainPic: string) => <ImageBox width={32} height={32} imgUrl={mainPic} />
export const promptCommodityColumn = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
width: 80,
},
{
title: "商品图片",
dataIndex: "mainPic",
render: (mainPic: string) => showMainPic(mainPic)
},
{
title: "商品名称",
dataIndex: "name",
width: 280,
ellipsis: true,
},
{
title: "品类",
render: (_, record) => record.customerCategory ? record.customerCategory.name : "",
ellipsis: true,
},
{
title: "品牌",
render: (_, record) => record.brand ? record.brand.name : "",
ellipsis: true,
},
{
title: "价格",
dataIndex: "min",
render: (_, record) => ${priceFormat(record.min)}`
},
]
export default promptCommodityColumn
import { ISchema } from '@formily/antd'
import { FORM_FILTER_PATH } from '@/formSchema/const'
import { PublicApi } from '@/services/api'
export const formProduct: ISchema = {
type: 'object',
properties: {
name: {
type: 'string',
'x-component': 'ModalSearch',
'x-component-props': {
placeholder: '搜索',
align: 'flex-left',
},
},
[FORM_FILTER_PATH]: {
type: 'object',
'x-component': 'flex-layout',
'x-component-props': {
rowStyle: {
flexWrap: 'nowrap',
style: {
marginRight: 0
}
},
colStyle: {
marginTop: 20,
},
},
properties: {
customerCategoryId: {
type: 'string',
"x-component": 'SearchSelect',
"x-component-props": {
placeholder: '请选择品类',
className: 'fixed-ant-selected-down', // 该类强制将显示的下拉框出现在select下, 只有这里出现问题, ??
queryParams: {
storeId: 2,
},
fetchSearch: PublicApi.getProductSelectGetSelectCategory,
style: {
width: 160
}
}
},
brandId: {
type: 'string',
"x-component": 'SearchSelect',
"x-component-props": {
placeholder: '请选择品牌',
queryParams: {
storeId: 2,
},
fetchSearch: PublicApi.getSearchMobileShopStoreGetBrand,
style: {
width: 160
}
}
},
submit: {
"x-component": 'Submit',
"x-mega-props": {
span: 1
},
"x-component-props": {
children: '查询'
}
}
}
}
}
}
export const basicSchema: ISchema = {
type: 'object',
properties: {
name: {
type: 'string',
'x-component': 'Search',
'x-component-props': {
placeholder: '搜索',
align: 'flex-left',
},
},
}
}
@import "../../../../../../global//styles/utils.less";
@import "../../common.less";
.selectBtn {
display: block;
width: 100%;
background-color: #FAFBFC;
border: 1px dashed #D8DDE6;
}
.uploadPreview {
border: 1px solid #EBECF0;
}
import React, { useState } from 'react'
import { Button, Input, message } from 'antd'
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'
import { changeProps } from 'lingxi-editor-core'
import ImageBox from '@/components/ImageBox'
import isEmpty from 'lodash/isEmpty'
import ModalTable from '@/components/ModalTable'
import { PublicApi } from '@/services/api'
import { FORM_FILTER_PATH } from '@/formSchema/const'
import { priceFormat } from '@/utils/numberFomat'
import { useRowSelectionTable } from '@/hooks/useRowSelectionTable'
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch'
import SearchSelect from '@/components/NiceForm/components/SearchSelect'
import Search from '@/components/NiceForm/components/Search'
import Submit from '@/components/NiceForm/components/Submit'
import { basicSchema } from './contant/schema'
import promptCommodityColumn from './contant/column'
import arrowRightIcon from '@/assets/icons/arrow_right.png'
import arrowLeftIcon from '@/assets/icons/arrow_left.png'
import styles from './index.less'
interface DataItemType {
sort: number,
id: number,
name: string,
img: string,
type: number,
expand: boolean,
selectInfo?: any
}
interface BannerPropsType {
dataList: DataItemType[],
selectInfo?: any,
productIdList: number[],
storeId: number,
title: string,
}
const RecommendCommodity: React.FC<BannerPropsType> = (props) => {
const { title, storeId, productIdList, dataList } = props
const [expandState, setExpandState] = useState<boolean>(true)
const [list, setList] = useState<DataItemType[]>([])
const [modalVisible, setModalVisible] = useState<boolean>(false)
const [recommendTitle, setRecommendTitle] = useState<string>(title)
const [productRowSelection, productRowCtl] = useRowSelectionTable()
/**
* 修改名称
* @param value
* @param id
*/
const handleNameChange = (value: string) => {
setRecommendTitle(value)
changeProps({
props:Object.assign({ ...props }, { title: value })
})
}
const getDataListByList = (list) => {
let result: any = []
if(list) {
list.forEach((listItem) => {
if (listItem.productList && listItem.productList.length > 0) {
result = [...result, ...listItem.productList]
}
})
}
return result
}
/**
* 删除已选商品
*/
const handleDeleteItem = (id: number) => {
let productIds = [...productIdList]
productIds = productIds.filter(item => item !== id)
const commodityList = []
dataList.forEach((listItem: any) => {
if (listItem.productList && listItem.productList.length > 0) {
const temp = listItem
const tempProductList = []
listItem.productList.forEach((productItem) => {
if(productItem.id !== id) {
tempProductList.push(productItem)
}
})
if(tempProductList.length > 0) {
temp.productList = tempProductList
commodityList.push(temp)
}
}
})
changeProps({
props:Object.assign({ ...props }, {
productIdList: productIds,
dataList: commodityList,
})
})
}
/**
* 根据类型显示选择的信息
* @param type 1-商品详情 2-积分详情 3-店铺主页 4-资讯详情 5-不跳转
*/
const renderSelectItemByType = (list: any[]) => {
const selectList = getDataListByList(list)
return (
<div>
{
selectList && selectList.map(selectItem => (
<div className={styles.setting_line_addItem_line} key={selectItem.id}>
<div className={styles.setting_line_addItem_line_label}></div>
<div className={styles.setting_line_addItem_line_brief}>
<div className={styles.selectInfoBox}>
<ImageBox direction="column" width={60} height={60} imgUrl={selectItem.mainPic}/>
<div className={styles.selectInfo}>
<div className={styles.selectInfo_name}>{selectItem.name}</div>
<div className={styles.selectInfo_price}>{priceFormat(selectItem.min)}</div>
</div>
<div className={styles.selectInfoBox_delete} onClick={() => handleDeleteItem(selectItem.id)}>
<DeleteOutlined />
</div>
</div>
</div>
</div>
))
}
</div>
)
}
/**
* 打开选择模态框
* @param sort
* @param type
*/
const handleOpenSelectModal = () => {
setModalVisible(true)
}
const fetchCategoryByCommodityId = (idList: number[]) => {
return new Promise((resolve) => {
const param = {
idList
}
PublicApi.postSearchMobileShopStoreGetCategoryByCommodityId(param).then((res) => {
message.destroy()
if (res.code === 1000) {
resolve(changeData(res.data))
} else {
resolve([])
}
}).catch(() => {
resolve([])
})
})
}
const changeData = (dataList) => {
if(dataList) {
return dataList.map((dataItem) => {
return {
categoryId: dataItem.id,
categoryName: dataItem.name,
categoryImage: dataItem.imageUrl || "",
productList: dataItem.commodityResponseList ? dataItem.commodityResponseList.map((commodityItem) => {
return {
id: commodityItem.id,
name: commodityItem.name,
sellPoints: commodityItem.sellingPoint,
min: commodityItem.min,
unitName: commodityItem.unitName,
sold: commodityItem.sold,
mainPic: commodityItem.mainPic,
}
}) : []
}
})
}
return []
}
const handleModalOk = async () => {
let selectedRowKeys = productRowCtl.selectedRowKeys
if (!selectedRowKeys || isEmpty(selectedRowKeys)) {
message.info("请选择")
return null
}
selectedRowKeys = [...productIdList, ...selectedRowKeys]
const commodityList = await fetchCategoryByCommodityId(selectedRowKeys)
changeProps({
props:Object.assign({ ...props }, {
productIdList: selectedRowKeys,
dataList: commodityList,
})
})
setModalVisible(false)
}
const handleModalCancel = async () => {
setModalVisible(false)
}
/**
* 获取模态框数据
* @param param
*/
const fetchTableList = async (param: any) => {
const params: any = {
...param,
priceTypeList: [1],
idNotInList: productIdList || [],
storeId
}
const res = await PublicApi.postSearchMobileShopStoreGetCommodityList(params, {ctlType: 'none'})
message.destroy()
return res.data
}
return (
<div className={styles.setting}>
{/* <div className={styles.hideModule}>
<Checkbox checked={!visible} onChange={handleHideChange}>隐藏整个模块</Checkbox>
</div> */}
<div className={styles.setting_line}>
<div className={styles.setting_line_main}>
<div className={styles.setting_line_name} >
<div style={{ flex: 1 }} onClick={() => setExpandState(!expandState)}>
{
expandState ? <img className={styles.icon} src={arrowLeftIcon} /> : <img className={styles.icon} src={arrowRightIcon} />
}
<span>{title}</span>
</div>
</div>
{
expandState && (
<div className={styles.setting_line_addItem}>
<div className={styles.setting_line_addItem_line}>
<div className={styles.setting_line_addItem_line_label}>名称:</div>
<div className={styles.setting_line_addItem_line_brief}>
<Input value={recommendTitle} onChange={(e) => handleNameChange(e.target.value)} />
</div>
</div>
<div className={styles.setting_line_addItem_line}>
<div className={styles.setting_line_addItem_line_label}>推荐商品:</div>
<div className={styles.setting_line_addItem_line_brief}>
<Button className={styles.selectBtn} icon={<PlusOutlined />} onClick={() => handleOpenSelectModal()}>选择商品</Button>
</div>
</div>
{
(dataList && dataList.length > 0) ? renderSelectItemByType(dataList) : null
}
</div>
)
}
</div>
</div>
<ModalTable
modalTitle="选择商品"
width={960}
confirm={handleModalOk}
cancel={handleModalCancel}
scroll={{ y: 400 }}
visible={modalVisible}
columns={promptCommodityColumn}
rowSelection={productRowSelection}
fetchTableData={params => fetchTableList(params)}
formilyProps={
{
ctx: {
schema: basicSchema,
components: { ModalSearch: Search, SearchSelect, Submit } ,
effects: ($, actions) => {
actions.reset()
useStateFilterSearchLinkageEffect(
$,
actions,
'name',
FORM_FILTER_PATH,
);
}
}
}
}
resetModal={{
destroyOnClose: true
}}
tableProps={{
rowKey: 'id',
onRow: (record) => ({
onClick: () => {
if(!productRowCtl.selectRow.includes(record)) {
productRowCtl.setSelectRow([...productRowCtl.selectRow, record]);
productRowCtl.setSelectedRowKeys([...productRowCtl.selectRow.map(item => item.id), record.id]);
} else {
productRowCtl.setSelectRow(productRowCtl.selectRow.filter(selectRowItem => selectRowItem.id !== record.id));
productRowCtl.setSelectedRowKeys(productRowCtl.selectedRowKeys.filter(selectedRowKeysItem => selectedRowKeysItem !== record.id))
}
},
})
}}
/>
</div>
)
}
export default RecommendCommodity
......@@ -6,6 +6,7 @@ import QuickNav from './components/quickNav'
import HeadBackground from './components/headBackground'
import RecommendCommodity from './components/recommendCommodity'
import BottomNavigation from './components/bottomNavigation'
import MobileChannelGoods from './components/channelGoods'
import styles from './index.less'
import { ConsoleSqlOutlined } from '@ant-design/icons';
......@@ -32,6 +33,8 @@ const PropsSettings: React.FC<PropsSettingsPropsType> = (props) => {
return <Banner {...initProps} />
case PROPS_TYPES.mobileQuickNav:
return <QuickNav {...initProps} />
case PROPS_TYPES.mobileChannelGoodsCard:
return <MobileChannelGoods {...initProps} />
case PROPS_TYPES.mobileBottomNavigation:
return <BottomNavigation {...initProps} />
default:
......
......@@ -97,63 +97,16 @@ export const mobileBanner = {
}
}
export const mobileQuickNav = {
export const mobileChannelCategory = {
key: "4",
"4": {
"componentName": "MobileQuickNav",
"componentType": PROPS_TYPES.mobileQuickNav,
"title": "导航模块",
"componentName": "MobileChannelCategory",
"componentType": PROPS_TYPES.moibileChannelCategory,
"title": "商品品类",
"canEdit": false,
"canHide": false,
"props": {
"canEdit": false,
"style": {
paddingTop: '12px',
margin: '0 8px',
borderRadius: '8px',
},
dataList: [
{
"id":1,
"expand":false,
"icon":"https://shushangyun-lingxi.oss-cn-shenzhen.aliyuncs.com/c5d66f1488cc47d0a73279ce1ef11c991610677462848.png",
"type":1,
"name":"商城",
"url":""
},
{
"id":2,
"expand":false,
"icon":"https://shushangyun-lingxi.oss-cn-shenzhen.aliyuncs.com/9f105bdebcfb4010b5827f7b64fb53281610696444606.png",
"type":2,
"name":"分类",
"url":""
},
{
"id":3,
"expand":false,
"icon":"https://shushangyun-lingxi.oss-cn-shenzhen.aliyuncs.com/d4383c684c6e4707b405f46f281796d71610696469970.png",
"type":3,
"name":"积分兑换",
"url":""
},
{
"id":4,
"expand":false,
"icon":"https://shushangyun-lingxi.oss-cn-shenzhen.aliyuncs.com/d4383c684c6e4707b405f46f281796d71610696469970.png",
"type":4,
"name":"公司介绍",
"url":""
},
{
"id":5,
"expand":false,
"icon":"https://shushangyun-lingxi.oss-cn-shenzhen.aliyuncs.com/441a66ebeb3b45e6a64ecfa9977f411c1610696489991.png",
"type":5,
"name":"成为会员",
"url":""
},
]
dataList: []
}
}
}
......
......@@ -18,7 +18,7 @@ import {
divWrap,
mobileChannelHeaderNav,
mobileBanner,
mobileQuickNav,
mobileChannelCategory,
mobileChannelGoodsCard,
mobileChannelInformation,
mobileBottomNavigation,
......@@ -186,6 +186,28 @@ const mobileShopTempleteEdit: React.FC<ShopPreviewPropsType> = (props) => {
})
}
/**
* 获取品类树
*/
const getCustomerCategoryTreeById = (channelMemberId: number) => {
return new Promise((resolve) => {
const param: any = {
channelMemberId,
}
PublicApi.getSearchMobileShopChannelGetCustomerCategoryTree(param).then((res) => {
if (res.code === 1000) {
resolve(res.data)
} else {
resolve([])
}
}).catch(() => {
resolve([])
})
})
}
const getComponentsConfig = async () => {
try {
const appConfig = await getAppChannelConfig()
......@@ -195,6 +217,11 @@ const mobileShopTempleteEdit: React.FC<ShopPreviewPropsType> = (props) => {
const channelInfo = await fetchChannelInfo()
mobileChannelHeaderNav[mobileChannelHeaderNav.key].props.name = channelInfo.memberName
if(channelInfo.memberId) {
const categoryList = await getCustomerCategoryTreeById(channelInfo.memberId)
mobileChannelCategory[mobileChannelCategory.key].props.dataList = categoryList
}
if(appConfig?.advertBO) {
mobileBanner[mobileBanner.key].props.dataList = appConfig?.advertBO.advertDetailsBOList
}
......@@ -204,7 +231,7 @@ const mobileShopTempleteEdit: React.FC<ShopPreviewPropsType> = (props) => {
...mobileChannelHeaderNav,
...divWrap,
...mobileBanner,
...mobileQuickNav,
...mobileChannelCategory,
...mobileChannelGoodsCard,
...mobileChannelInformation,
...mobileBottomNavigation,
......
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