Commit d0882d60 authored by GuanHua's avatar GuanHua

feat: 企业商城装修开发

parent ed6485b7
......@@ -22,7 +22,7 @@ const mockData = {
},
{
name: '한국어-KO',
key: 'ko',
key: 'ko',
icon: 'http://lingxi-frontend-test.oss-cn-hangzhou.aliyuncs.com/images/koren.png'
}
],
......@@ -41,4 +41,4 @@ exports.fetchConfig = async () => {
resolve(mockData.data)
}, 2* 1000)
})
}
\ No newline at end of file
}
......@@ -58,6 +58,15 @@ const serviceConfig = {
url: '/order/rule/configuration/all',
method: 'get'
}
},
site: {
siteInfo: {
url: '/manage/paas/site/details',
params: {
id: SITE_ID
},
method: 'get'
}
}
}
......
......@@ -41,6 +41,7 @@ export interface ShopInfo {
describe: string;
state: number;
url: string;
isDefault?: any;
}
export interface OrderMode {
......@@ -54,6 +55,18 @@ export interface Web {
orderMode: OrderMode[];
}
export interface SiteInfo {
id: number;
name: string;
logo: string;
siteUrl: string;
symbol?: any;
}
export interface Site {
siteInfo: SiteInfo;
}
export interface CountryList {
name: string;
key: string;
......@@ -78,5 +91,6 @@ export interface Global {
export interface RootObject {
payConfig: PayConfig;
web: Web;
site: Site;
global: Global;
}
\ No newline at end of file
......@@ -79,7 +79,7 @@
}
.template_info {
padding: 0 24px;
padding: 0 16px;
.template_info_name {
display: flex;
......@@ -116,6 +116,7 @@
line-height: 22px;
.template_info_content_text_line {
display: flex;
&:not(:last-child) {
margin-bottom: 8px;
}
......@@ -127,6 +128,10 @@
&>span {
color: #303133;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
......@@ -163,4 +168,4 @@
}
}
}
\ No newline at end of file
}
......@@ -3,7 +3,6 @@ import { PlayCircleOutlined } from '@ant-design/icons'
import cx from 'classnames'
import { Link } from 'umi'
import { TEMPLATE_TYPE_TEXT } from '@/constants'
import { GetTemplatePlatformFindAllTemplateByTypeResponse } from '@/services/TemplateApi'
import styles from './index.less'
interface TemplateItemPropsType {
......@@ -55,7 +54,7 @@ const TemplateItem: React.FC<TemplateItemPropsType> = (props) => {
type !== TEMPLATE_TYPE_TEXT.goods && (
<div className={styles.template_info_content_text_line}>
<label>使用商城:</label>
<span>{templateInfo.shopName}</span>
<span title={templateInfo.shopName}>{templateInfo.shopName}</span>
</div>
)
}
......
import { ComponentConfigTypes, PROPS_TYPES } from 'lingxi-editor-core';
const Category: ComponentConfigTypes = {
propsConfig: {
children: {
label: '文本内容',
type: PROPS_TYPES.string,
},
},
};
export default Category
import { ComponentConfigTypes, PROPS_TYPES } from 'lingxi-editor-core';
const MallMainNav: ComponentConfigTypes = {
propsConfig: {
componentType: {
label: '导航朗编辑',
type: PROPS_TYPES.mallNav
},
},
};
export default MallMainNav;
import { ComponentConfigTypes, PROPS_TYPES } from 'lingxi-editor-core';
import { ComponentConfigTypes, PROPS_TYPES, NODE_PROPS_TYPES } from 'lingxi-editor-core';
const QuickNav: ComponentConfigTypes = {
nodePropsConfig: {
overflowedIndicator: {
type: NODE_PROPS_TYPES.reactNode,
isOnlyNode: true,
},
children: {
type: NODE_PROPS_TYPES.reactNode,
childNodesRule: ['Advert',],
},
},
propsConfig: {
children: {
label: '文本内容',
......
......@@ -5,6 +5,7 @@ import Carousel from './Carousel'
import ShowCase from './ShowCase'
import MallLayout from './MallLayout'
import MainNav from './MainNav'
import MallMainNav from './MallMainNav'
import Advert from './Advert'
import QuickNav from './QuickNav'
import FloorLine from './FloorLine'
......@@ -24,6 +25,7 @@ import MobileQuality from './MobileQuality'
import MobileQuickNav from './MobileQuickNav'
import MobileRecommendShops from './MobileRecommendShops'
import MobileShowCase from './MobileShowCase'
import Category from './Category'
export default {
TopBar,
......@@ -32,6 +34,7 @@ export default {
Carousel,
MallLayout,
MainNav,
MallMainNav,
Advert,
QuickNav,
FindMore,
......@@ -52,4 +55,5 @@ export default {
MobileQuickNav,
MobileRecommendShops,
MobileShowCase,
Category,
}
......@@ -9,7 +9,7 @@ export const mallLayoutConfig = {
"minHeight": "100%"
}
},
"childNodes": ["1", "3", "4", "5", "6"]
"childNodes": ["1", "3", "4", "21"]
},
}
......@@ -48,105 +48,118 @@ export const headerConfig = {
export const mainNavConfig = {
key: "4",
"4": {
"componentName": "MainNav",
"componentName": "MallMainNav",
"props": {},
},
}
export const bannerAdvertConfig = {
key: "5",
"5": {
"componentName": "Advert",
// < div className = { styles.horizontalWrap } >
// <Category categoryList={ categoryList } />
// < div className = { styles.bannerWrap } >
// {
// useMemo(() => <Advert type="banner" advertList = { firstAdvertList } />, [firstAdvertList])
// }
// {
// useMemo(() => <Advert type="interact" advertList = { secondAdvertList } />, [secondAdvertList])
// }
// </div>
// < div className = { styles.quickNavWrap } >
// <QuickNav
// userInfo={ userInfo }
// userRoles = { userRoles }
// name = { mallInfo?.name }
// advertList = { secondAdvertList }
// />
// </div>
// < /div>
export const bannerContainer = {
key: "21",
"21": {
"componentName": "div",
"props": {
"type": "banner",
"hasQuickNav": true,
"linkdisable": true,
"advertList": []
}
},
"style": {
position: "relative",
display: "flex",
paddingTop: "16px",
width: "1200px",
margin: "0 auto",
}
},
"childNodes": ["22", "23", "24"]
}
}
export const interactAdvertConfig = {
key: "6",
"6": {
"componentName": "Advert",
export const categoryConfig = {
key: "22",
"22": {
"componentName": "Category",
"props": {
"type": "interact",
"linkdisable": true,
"advertList": []
},
},
categoryList: [],
canHide: false,
}
}
}
export const FindMoreConfig = {
key: "18",
"18": {
"componentName": "FindMore",
"props": {},
},
export const bannerWrap = {
key: "23",
"23": {
"componentName": "div",
"props": {
"style": {
margin: "0 16px",
flex: 1,
width: 0,
}
},
"childNodes": ["5", "6"]
}
}
export const InformationConfig = {
key: '19',
"19": {
"componentName": "Information",
export const quickNavConfigWrap = {
key: "24",
"24": {
"componentName": "div",
"props": {},
},
"childNodes": ["25", "26"]
}
}
export const FooterConfig = {
key: '20',
"20": {
"componentName": "Footer",
export const quickNavConfig = {
key: "25",
"25": {
"componentName": "QuickNav",
"props": {},
},
}
}
export default {
"0": {
"componentName": "MallLayout",
"props": {
"style": {
"width": "100%",
"minHeight": "100%"
}
},
"childNodes": ["1", "2", "3", "4", "5", "7", "8", "18", "19", "20"]
},
"1": {
"componentName": "TopBar",
"props": {
linkdisable: true
},
},
"2": {
export const navAdvertConfig = {
key: "26",
"26": {
"componentName": "Advert",
"props": {
"type": "top",
"type": "nav",
"linkdisable": true,
"advertList": []
},
},
"3": {
"componentName": "Header",
"props": {},
},
"4": {
"componentName": "MainNav",
"props": {},
},
}
export const bannerAdvertConfig = {
key: "5",
"5": {
"componentName": "Advert",
"props": {
"type": "banner",
"hasQuickNav": true,
"hasQuickNav": false,
"linkdisable": true,
"advertList": []
}
},
"7": {
}
export const interactAdvertConfig = {
key: "6",
"6": {
"componentName": "Advert",
"props": {
"type": "interact",
......@@ -154,61 +167,26 @@ export default {
"advertList": []
},
},
"8": {
"componentName": "FloorLine",
"props": {},
"childNodes": ["9", "10"]
},
"9": {
"componentName": "FloorLine.Horizontal",
"props": {},
"childNodes": ["11", "12"]
},
"10": {
"componentName": "FloorLine.Brand",
"props": {},
},
"11": {
"componentName": "FloorLine.Category",
"props": {},
},
"12": {
"componentName": "FloorLine.Vertical",
"props": {},
"childNodes": ["13", "15"]
},
"13": {
"componentName": "FloorLine.FloorHeader",
"props": {},
"childNodes": ["14"]
},
"14": {
"componentName": "FloorLine.Banner",
"props": {
"type": "category",
},
},
"15": {
"componentName": "FloorLine.Horizontal",
"props": {},
"childNodes": ["16", "17"]
},
"16": {
"componentName": "FloorLine.Goods",
"props": {},
},
"17": {
"componentName": "FloorLine.Shops",
"props": {},
},
}
export const FindMoreConfig = {
key: "18",
"18": {
"componentName": "FindMore",
"props": {},
},
}
export const InformationConfig = {
key: '19',
"19": {
"componentName": "Information",
"props": {},
},
}
export const FooterConfig = {
key: '20',
"20": {
"componentName": "Footer",
"props": {},
......
export const menuData = [
{
"path": "/shop",
"name": "首页",
"key": "shopHome",
},
{
"path": "/commodity",
"name": "商品商城",
"key": "shopCommodity",
},
{
"path": "/purchaseOnline",
"name": "在线求购",
"key": "purchaseOnline",
},
{
"path": "/pointsMall",
"name": "积分商城",
"key": "shopPointsMall",
},
{
"path": "/shops",
"name": "店铺",
"key": "shops",
},
{
"path": "/shop/infomation",
"name": "资讯",
"key": "shopInfomation",
},
]
\ No newline at end of file
import { GlobalConfig } from '@/global/config'
const siteUrl = GlobalConfig.site.siteInfo.siteUrl
/**
* 请求头
*/
export const REQUEST_HEADER = 'http://'
export const getTopDomainByHost = (url: string): string => {
if (!url) return ''
return url.split('.').slice(-2).join('.')
}
export const TOP_DOMAIN = getTopDomainByHost(siteUrl)
export const getMenuData = (shopId: number) => {
const shopInfo = GlobalConfig.web.shopInfo
const webMallList = shopInfo.filter((item: { id: number}) => item.id === shopId)
if (webMallList.length > 0) {
const SUB_DOMAIN = webMallList[0].url
return [
{
"id": 0,
"link": `${REQUEST_HEADER}${SUB_DOMAIN}.${TOP_DOMAIN}`,
"name": "商城首页",
"status": true,
"key": "shopHome",
},
{
"id": 1,
"link": `${REQUEST_HEADER}${SUB_DOMAIN}.${TOP_DOMAIN}/commodity`,
"name": "现货商品",
"status": true,
"key": "shopCommodity",
},
{
"id": 2,
"link": `${REQUEST_HEADER}${SUB_DOMAIN}.${TOP_DOMAIN}/inquiry`,
"name": "询价商品",
"status": true,
"key": "purchaseOnline",
},
{
"id": 3,
"link": `${REQUEST_HEADER}${SUB_DOMAIN}.${TOP_DOMAIN}/stores`,
"name": "优选店铺",
"status": true,
"key": "shopPointsMall",
},
{
"id": 4,
"link": `${REQUEST_HEADER}${SUB_DOMAIN}.${TOP_DOMAIN}/integral`,
"name": "积分商城",
"key": "shops",
"status": true,
},
{
"id": 5,
"link": `${REQUEST_HEADER}info.${TOP_DOMAIN}`,
"name": "行情资讯",
"status": true,
"key": "shopInfomation",
},
]
}
return []
}
......@@ -6,12 +6,13 @@ import SettingPanel from '../settingsPanel'
import config from '../configs'
import { Prompt } from 'umi'
import { GlobalConfig } from '@/global/config'
import { topBarConfig, topAdvertConfig, headerConfig, mainNavConfig, bannerAdvertConfig, interactAdvertConfig, mallLayoutConfig, InformationConfig, FooterConfig } from './defaultData'
import { topBarConfig, topAdvertConfig, headerConfig, mainNavConfig, bannerContainer, quickNavConfigWrap, categoryConfig, bannerWrap, quickNavConfig, bannerAdvertConfig, interactAdvertConfig, navAdvertConfig, mallLayoutConfig, InformationConfig, FooterConfig } from './defaultData'
import Loading from '../components/Loading'
import { LAYOUT_TYPE } from '@/constants'
import { menuData } from './defaultMenu'
import { getMenuData } from './defaultMenu'
import { PublicApi } from '@/services/api'
import styles from './index.less'
import { isEmpty } from 'lodash'
interface MallEditPropsType {
location: {
......@@ -24,6 +25,7 @@ interface MallEditPropsType {
* 模板名称
*/
template: string;
shopId: number;
}
}
}
......@@ -31,7 +33,7 @@ interface MallEditPropsType {
const TemplateList = ['science']
const MallEdit: React.FC<MallEditPropsType> = (props) => {
const { query: { id, template } } = props.location
const { query: { id, template, shopId } } = props.location
const [loading, setLoading] = useState<boolean>(true)
const [theme, setTheme] = useState<string>('theme-mall-science')
const [componentConfigs, setComponentConfigs] = useState({})
......@@ -45,28 +47,11 @@ const MallEdit: React.FC<MallEditPropsType> = (props) => {
getComponentsConfig()
}, [])
const findFirstAdvertsByType = () => {
const findAdvertsByType = (type: number) => {
return new Promise((resolve) => {
const params: any = {
templateId: id,
type: 1
}
PublicApi.getTemplateAdornWebEnterpriseFindAdvertsByType(params).then(res => {
if (res.code === 1000) {
resolve(res.data)
} else {
resolve([])
}
})
})
}
const findSecondAdvertsByType = () => {
return new Promise((resolve) => {
const params: any = {
templateId: id,
type: 2
type
}
PublicApi.getTemplateAdornWebEnterpriseFindAdvertsByType(params).then(res => {
......@@ -112,72 +97,74 @@ const MallEdit: React.FC<MallEditPropsType> = (props) => {
})
}
/**
* 获取头条新闻
*/
const fetchLeadNews = () => {
const fetchNewByLabel = (label: string) => {
// 1-头条文章 2-轮播新闻 3-图片新闻 4-推荐阅读
return new Promise((resolve, reject) => {
PublicApi.getManageContentInformationFindAllByRecommendLabel({ recommendLabel: label }).then((res: { code: number; data: unknown }) => {
if (res.code === 1000) {
resolve(res.data)
} else {
reject()
}
}).catch(() => {
reject()
})
})
}
/**
* 获取商品品类树
*/
const getCategoryTree = () => {
return new Promise((resolve) => {
const param: any = {
recommendLabel: 1
templateId: id
}
PublicApi.getManageContentInformationFindAllByRecommendLabel(param).then(res => {
PublicApi.getTemplateWebCategoryWebFindEnterpriseCategoryTree(param).then(res => {
if (res.code === 1000) {
const leadNewsList: any = res.data || []
if (leadNewsList.length >= 2) {
const leftList = leadNewsList.slice(0, Math.round(leadNewsList.length / 2))
const rightList = leadNewsList.slice(Math.round(leadNewsList.length / 2), leadNewsList.length)
resolve({
leadLeftNews: leftList,
leadRightNews: rightList
})
} else {
resolve({
leadLeftNews: leadNewsList,
leadRightNews: []
})
}
resolve(res.data)
}
})
})
}
/**
* 获取全部栏目
* 获取导航栏菜单数据
*/
const fetchAllColumn = () => {
const getMallNavData = () => {
return new Promise((resolve) => {
PublicApi.getManageContentColumnAll().then(res => {
const param: any = {
templateId: id
}
PublicApi.getTemplateAdornWebEnterpriseFindColumn(param).then(res => {
if (res.code === 1000) {
let result = ""
const allColumn = res.data
if (allColumn && allColumn.length > 0) {
let labelList = allColumn.map((item: any) => item.name)
const showCount = 4
if (labelList.length <= showCount) {
result = labelList.join(' | ')
} else {
labelList = labelList.slice(0, showCount)
result = labelList.join(' | ') + " ..."
}
}
resolve(result)
resolve(res.data)
}
})
})
}
const getComponentsConfig = async () => {
// 导航栏
mainNavConfig[mainNavConfig.key].props.menuData = menuData
mainNavConfig[mainNavConfig.key].props.type = LAYOUT_TYPE.mall
mainNavConfig[mainNavConfig.key].props.categoryList = []
const navData = await getMallNavData()
if(!isEmpty(navData)) {
mainNavConfig[mainNavConfig.key].props.menuData = navData
} else {
mainNavConfig[mainNavConfig.key].props.menuData = getMenuData(Number(shopId))
}
// mainNavConfig[mainNavConfig.key].props.type = LAYOUT_TYPE.mall
// mainNavConfig[mainNavConfig.key].props.categoryList = []
categoryConfig[categoryConfig.key].props.categoryList = await getCategoryTree()
// 一号位广告
bannerAdvertConfig[bannerAdvertConfig.key].props.advertList = await findFirstAdvertsByType()
bannerAdvertConfig[bannerAdvertConfig.key].props.advertList = await findAdvertsByType(1)
// 二号位广告
interactAdvertConfig[interactAdvertConfig.key].props.advertList = await findSecondAdvertsByType()
interactAdvertConfig[interactAdvertConfig.key].props.advertList = await findAdvertsByType(2)
// 三号位广告
navAdvertConfig[navAdvertConfig.key].props.advertList = await findAdvertsByType(4)
const shopList = GlobalConfig.web.shopInfo
const webMallInfo = shopList.filter(item => item.environment === 1 && item.type === 1)[0]
......@@ -185,8 +172,7 @@ const MallEdit: React.FC<MallEditPropsType> = (props) => {
topBarConfig[topBarConfig.key].props.shopname = webMallInfo.name
// 行情资讯
InformationConfig[InformationConfig.key].props.newsList = await fetchLeadNews()
InformationConfig[InformationConfig.key].props.allColumn = await fetchAllColumn()
InformationConfig[InformationConfig.key].props.newsList = await fetchNewByLabel('4')
let initIndex = 100
let floorLineConfig: any = {}
......@@ -195,7 +181,7 @@ const MallEdit: React.FC<MallEditPropsType> = (props) => {
const firstCategory: any = await fetchFirstCategory()
for (const item of firstCategory) {
const categoryDetail: any = await fetchCategoryById(item.id)
const categoryDetail: any = await fetchCategoryById(item.categoryId)
let floorLineConfigItem = {}
floorLineKeys.push(String(initIndex + 1))
......@@ -313,8 +299,14 @@ const MallEdit: React.FC<MallEditPropsType> = (props) => {
...topAdvertConfig,
...headerConfig,
...mainNavConfig,
...bannerContainer,
...categoryConfig,
...bannerWrap,
...quickNavConfigWrap,
...quickNavConfig,
...bannerAdvertConfig,
...interactAdvertConfig,
...navAdvertConfig,
...floorLineConfig,
...InformationConfig,
...FooterConfig
......
......@@ -57,20 +57,30 @@
text-overflow: ellipsis;
white-space: nowrap;
background-color: #FAFBFC;
padding-left: 12px;
&>.icon {
margin-left: auto;
cursor: pointer;
width: 32px;
height: 32px;
line-height: 32px;
margin-right: 12px;
}
}
&_operation {
margin-left: auto;
background-color: #FAFBFC;
&_btn {
margin-left: 16px;
width: 16px;
&_icon {
width: 16px;
height: 16px;
}
}
}
}
......@@ -79,4 +89,4 @@
width: 100%;
border: 1px dashed #DFE1E6;
}
}
\ No newline at end of file
}
import React, { forwardRef, useState, useCallback } from 'react'
import { Button, Input, Modal } from 'antd'
import UploadImage from '@/components/UploadImage'
import { ReactSortable } from "react-sortablejs"
import { filterPropsFunction, getAdvertType, addTempalteIdToList } from '../../../../utils'
import SettingPanel from '../../../../components/SettingPanel'
import { message } from 'antd'
import { isEmpty } from '@formily/antd/esm/shared'
import { PublicApi } from '@/services/api'
import { clearSelectedStatus, changeProps } from 'lingxi-editor-core';
import { ArrowUpOutlined, DeleteOutlined, PlusOutlined, ArrowDownOutlined, CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons'
import { clearSelectedStatus, changeProps } from 'lingxi-editor-core'
import cx from 'classnames'
import { PlusOutlined, CaretDownOutlined, CaretRightOutlined } from '@ant-design/icons'
import upIcon from '@/asserts/icons/up_icon.png'
import downIcon from '@/asserts/icons/down_icon.png'
import deleteIcon from '@/asserts/icons/delete_icon.png'
import sortIcon from '@/asserts/icons/sort_icon.png'
import styles from './index.less'
interface AdvertItemType {
......@@ -55,7 +61,7 @@ interface AdvertItemType {
interface AdvertSettingPropsType {
advertList: AdvertItemType[];
onChange: Function;
type: 'top' | 'banner' | 'interact' | 'category';
type: 'top' | 'banner' | 'interact' | 'category' | 'nav';
templateid: number;
categoryid?: number;
}
......@@ -76,6 +82,8 @@ const AdvertSetting: React.FC<AdvertSettingPropsType> = forwardRef((props, ref)
return "285*120"
case 'category':
return "500*90"
case 'nav':
return "320*96"
}
}
......@@ -233,7 +241,7 @@ const AdvertSetting: React.FC<AdvertSettingPropsType> = forwardRef((props, ref)
}
PublicApi.postTemplateAdornWebEnterpriseSaveAdvert(param).then(res => {
if (res.code === 1000) {
resolve()
resolve(true)
} else {
message.error(res.message)
reject()
......@@ -265,55 +273,69 @@ const AdvertSetting: React.FC<AdvertSettingPropsType> = forwardRef((props, ref)
onOK={handleConfirmSave}
>
<div className={styles.advert_setting}>
{
list && list.map((item, index) => (
<div className={styles.advert_setting_line} key={`advert_setting_${index}`}>
<div className={styles.advert_setting_line_sort}>{item.sort}</div>
<div className={styles.advert_setting_line_main}>
<div className={styles.advert_setting_line_name} onClick={() => handleExpand(item.sort, !item.expand)}>
<span>{item.name}</span>
<ReactSortable
list={list}
setList={(newList) => {
setList(newList)
if(!isEmpty(newList)) {
changeProps({
props: Object.assign({ ...props }, { dataList: newList })
})
}
}}
handle=".draghandle"
>
{
list && list.map((item, index) => (
<div className={styles.advert_setting_line} key={`advert_setting_${index}`}>
<div className={styles.advert_setting_line_sort}>{item.sort}</div>
<div className={styles.advert_setting_line_main}>
<div className={styles.advert_setting_line_name} onClick={() => handleExpand(item.sort, !item.expand)}>
{
item.expand ? <CaretDownOutlined className={styles.icon} /> : <CaretRightOutlined className={styles.icon} />
}
<span>{item.name}</span>
</div>
{
item.expand ? <CaretUpOutlined className={styles.icon} /> : <CaretDownOutlined className={styles.icon} />
}
</div>
{
!!item.expand && (
<div className={styles.advert_setting_line_addItem}>
<div className={styles.advert_setting_line_addItem_line}>
<div className={styles.advert_setting_line_addItem_line_label}>名称</div>
<div className={styles.advert_setting_line_addItem_line_brief}>
<Input className={styles.advert_setting_line_addItem_input} value={item.name} onChange={(e) => handleKeyChange(e.target.value, item.sort, 'name')} maxLength={15} />
!!item.expand && (
<div className={styles.advert_setting_line_addItem}>
<div className={styles.advert_setting_line_addItem_line}>
<div className={styles.advert_setting_line_addItem_line_label}>名称</div>
<div className={styles.advert_setting_line_addItem_line_brief}>
<Input className={styles.advert_setting_line_addItem_input} value={item.name} onChange={(e) => handleKeyChange(e.target.value, item.sort, 'name')} maxLength={15} />
</div>
</div>
</div>
<div className={styles.advert_setting_line_addItem_line}>
<div className={styles.advert_setting_line_addItem_line_label}>图片</div>
<div className={styles.advert_setting_line_addItem_line_brief}>
<UploadImage
imgUrl={item.picUrl}
size={getImgSize()}
fileMaxSize={500}
onChange={(val) => handleKeyChange(val, item.sort, 'picUrl')}
/>
<div className={styles.advert_setting_line_addItem_line}>
<div className={styles.advert_setting_line_addItem_line_label}>图片</div>
<div className={styles.advert_setting_line_addItem_line_brief}>
<UploadImage
imgUrl={item.picUrl}
size={getImgSize()}
fileMaxSize={500}
onChange={(val) => handleKeyChange(val, item.sort, 'picUrl')}
/>
</div>
</div>
</div>
<div className={styles.advert_setting_line_addItem_line}>
<div className={styles.advert_setting_line_addItem_line_label}>链接</div>
<div className={styles.advert_setting_line_addItem_line_brief}>
<Input className={styles.advert_setting_line_addItem_input} value={item.link} onChange={(e) => handleKeyChange(e.target.value, item.sort, 'link')} />
<div className={styles.advert_setting_line_addItem_line}>
<div className={styles.advert_setting_line_addItem_line_label}>链接</div>
<div className={styles.advert_setting_line_addItem_line_brief}>
<Input className={styles.advert_setting_line_addItem_input} value={item.link} onChange={(e) => handleKeyChange(e.target.value, item.sort, 'link')} />
</div>
</div>
</div>
</div>
)
}
</div>
<div className={styles.advert_setting_line_operation}>
<Button disabled={index === 0} onClick={() => sortUp(index, item)} className={styles.advert_setting_line_operation_btn} icon={<ArrowUpOutlined />}></Button>
<Button disabled={index === list.length - 1} onClick={() => sortDown(index, item)} className={styles.advert_setting_line_operation_btn} icon={<ArrowDownOutlined />}></Button>
<Button className={styles.advert_setting_line_operation_btn} onClick={() => handleDeleteItem(index)} icon={<DeleteOutlined />}></Button>
)
}
</div>
<div className={styles.advert_setting_line_operation}>
<Button type="link" onClick={() => handleDeleteItem(index)} className={styles.advert_setting_line_operation_btn} icon={<img className={styles.advert_setting_line_operation_btn_icon} src={deleteIcon} />}></Button>
<Button type="link" disabled={index === 0} onClick={() => sortUp(index, item)} className={styles.advert_setting_line_operation_btn} icon={<img className={styles.advert_setting_line_operation_btn_icon} src={upIcon} />}></Button>
<Button type="link" disabled={index === list.length - 1} onClick={() => sortDown(index, item)} className={styles.advert_setting_line_operation_btn} icon={<img className={styles.advert_setting_line_operation_btn_icon} src={downIcon} />}></Button>
<Button type="link" className={cx(styles.advert_setting_line_operation_btn, "draghandle")} icon={<img className={styles.advert_setting_line_operation_btn_icon} src={sortIcon} />}></Button>
</div>
</div>
</div>
))
}
))
}
</ReactSortable>
<Button onClick={addSliderItem} className={styles.add_btn} icon={<PlusOutlined />}>添加广告</Button>
</div>
</SettingPanel>
......
.mall_nav {
&_title {
background-color: #F7F8FA;
}
ul {
display: flex;
padding: 0;
margin: 0;
&.hasBorder {
border-bottom: 1px solid #F4F5F7;
&>li {
height: 48px;
line-height: 48px;
}
}
&>li {
list-style: none;
height: 40px;
line-height: 40px;
padding-left: 8px;
font-size: 12px;
color: #909399;
font-weight: 500;
}
}
.mall_nav_list_btn {
margin-left: 16px;
width: 16px;
&_icon {
width: 16px;
height: 16px;
}
}
.width160 {
width: 160px;
}
.width392 {
width: 392px;
}
.width96 {
width: 96px;
}
.width120 {
width: 120px;
}
}
import React, { useState, useCallback } from 'react'
import { Modal, Checkbox, Button, message } from 'antd'
import { clearSelectedStatus, changeProps } from 'lingxi-editor-core'
import SettingPanel from '../../../../components/SettingPanel'
import { ReactSortable } from "react-sortablejs"
import { useEffect } from 'react'
import { PublicApi } from '@/services/api'
import isEmpty from 'lodash/isEmpty'
import cx from 'classnames'
import upIcon from '@/asserts/icons/up_icon.png'
import downIcon from '@/asserts/icons/down_icon.png'
import sortIcon from '@/asserts/icons/sort_icon.png'
import styles from './index.less'
interface MenuItemType {
id: number,
name: string,
link: string,
status: boolean
}
interface MallNavProps {
templateid: number,
menuData: MenuItemType[]
}
const MallNav: React.FC<MallNavProps> = (props) => {
const { menuData, templateid } = props
const [list, setList] = useState<MenuItemType[]>([])
const [confirmLoading, setConfirmLoading] = useState<boolean>(false)
const [newProps, setNewProps] = useState(props)
useEffect(() => {
if (!isEmpty(menuData)) {
const newList: MenuItemType[] = [...menuData]
newList.shift()
if (newList) {
setList(newList.map(((item, index) => {
item.id = index
return item
})))
}
}
}, [menuData])
const changeNewProps = (key: string, data: any) => {
const newProps = { ...props }
newProps[key] = data
setNewProps(newProps)
}
/**
* 保存导航
*/
const saveNav = (data: any) => {
return new Promise((resolve, reject) => {
const { menuData } = data
const param: any = {
templateId: templateid,
columnBOList: menuData.map((item) => {
return {
name: item.name,
link: item.link,
status: item.status
}
})
}
PublicApi.postTemplateAdornWebEnterpriseSaveColumn(param).then(res => {
if (res.code === 1000) {
resolve(true)
} else {
reject()
}
})
})
}
/**
* 确认
*/
const handleConfirm = (e) => {
e.preventDefault();
if (JSON.stringify(props) === JSON.stringify(newProps)) {
return
}
setConfirmLoading(true)
saveNav(newProps).then(() => {
changeProps({
props: newProps
})
clearSelectedStatus()
setConfirmLoading(false)
}).catch(() => {
setConfirmLoading(false)
})
}
const sortUp = (index: number, item: MenuItemType) => {
const newList = JSON.parse(JSON.stringify(list))
const tempItem = JSON.parse(JSON.stringify(item))
const temp = newList[index - 1]
newList[index - 1] = item
newList[index - 1].id = temp.id
newList[index] = temp
newList[index].id = tempItem.id
setList(newList)
changeNewProps('menuData', [menuData[0], ...newList])
}
const sortDown = (index: number, item: MenuItemType) => {
const newList = JSON.parse(JSON.stringify(list))
const temp = newList[index + 1]
const tempItem = JSON.parse(JSON.stringify(item))
newList[index + 1] = item
newList[index + 1].id = temp.id
newList[index] = temp
newList[index].id = tempItem.id
setList(newList)
changeNewProps('menuData', [menuData[0], ...newList])
}
const handleCancel = useCallback(() => {
if (JSON.stringify(props) !== JSON.stringify(newProps)) {
Modal.confirm({
content: "您还没有保存修改的内容,是否确认关闭?",
okText: "确认",
cancelText: "取消",
onOk: () => {
clearSelectedStatus()
}
})
} else {
clearSelectedStatus()
}
}, [newProps])
const handleStatusChange = (e, item: MenuItemType) => {
const newList = JSON.parse(JSON.stringify(list))
newList.map(listItem => {
if (listItem.id === item.id) {
listItem.status = e.target.checked
}
})
setList(newList)
changeNewProps('menuData', [menuData[0], ...newList])
}
return (
<SettingPanel
confirmLoading={confirmLoading}
onCancel={handleCancel}
onOK={handleConfirm}
>
<div className={styles.mall_nav}>
<div className={styles.mall_nav_title}>
<ul>
<li className={styles.width160}>栏目名称</li>
<li className={styles.width392}>链接地址</li>
<li className={styles.width96}>显示在导航上</li>
<li className={styles.width120}>位置排序</li>
</ul>
</div>
<div className={styles.mall_nav_body}>
<div className={styles.mall_nav_list}>
<div>
{
menuData && menuData.map((item, index) => index === 0 && (
<ul key={`menu_item_${index}`} className={styles.hasBorder}>
<li className={styles.width160}>{item.name}</li>
<li className={styles.width392}>{item.link}</li>
<li className={styles.width96}><Checkbox checked={item.status} disabled /></li>
<li className={styles.width120}></li>
</ul>
))
}
</div>
<ReactSortable
list={list}
setList={(newList) => {
setList(newList)
if(!isEmpty(newList)) {
changeProps({
props: Object.assign({ ...props }, { menuData: [menuData[0], ...newList] })
})
}
}}
handle=".draghandle"
>
{
list && list.map((item, index) => (
<ul key={`menu_item_${index}`} className={styles.hasBorder}>
<li className={styles.width160}>{item.name}</li>
<li className={styles.width392}>{item.link}</li>
<li className={styles.width96}><Checkbox checked={item.status} onChange={(e) => handleStatusChange(e, item)} /></li>
<li className={styles.width120}>
<Button type="link" disabled={index === 0} onClick={() => sortUp(index, item)} className={styles.mall_nav_list_btn} icon={<img className={styles.mall_nav_list_btn_icon} src={upIcon} />}></Button>
<Button type="link" disabled={index === list.length - 1} onClick={() => sortDown(index, item)} className={styles.mall_nav_list_btn} icon={<img className={styles.mall_nav_list_btn_icon} src={downIcon} />}></Button>
<Button type="link" className={cx(styles.mall_nav_list_btn, "draghandle")} icon={<img className={styles.mall_nav_list_btn_icon} src={sortIcon} />}></Button>
</li>
</ul>
))
}
</ReactSortable>
</div>
</div>
</div>
</SettingPanel>
)
}
export default MallNav
......@@ -4,6 +4,7 @@ import GoodsSetting from './components/GoodsSetting'
import BrandSetting from './components/BrandSetting'
import ShopSetting from './components/ShopSetting'
import AdvertSetting from './components/AdvertSetting'
import MallNav from './components/MallNav'
import CategoryRecommendSetting from './components/CategoryreCommendSetting'
import { SelectedInfoType, clearSelectedStatus, PROPS_TYPES } from 'lingxi-editor-core';
import './index.less'
......@@ -40,6 +41,8 @@ const PropsSettings: React.FC<PropsSettingsPropsType> = (props) => {
case PROPS_TYPES.categoryBanner:
case PROPS_TYPES.advert:
return <AdvertSetting templateid={templateId} {...initProps} />
case PROPS_TYPES.mallNav:
return <MallNav templateid={templateId} {...initProps} />
}
}
return null
......
......@@ -153,7 +153,7 @@ const TemplateDetail: React.FC<TemplateDetailPropsType> = (props) => {
<Fragment>
<div className={cx(styles.btn, styles.renovation)} onClick={() => {
if (detailInfo?.environment === 1) {
window.location.href = `/pageCustomized/mall/template/edit?id=${id}&template=${detailInfo?.fileName}&environment=${detailInfo?.environment}`
window.location.href = `/pageCustomized/mall/template/edit?id=${id}&template=${detailInfo?.fileName}&environment=${detailInfo?.environment}&shopId=${detailInfo?.shopId}`
} else {
// message.info(`暂仅支持web环境模板装修`)
window.location.href = `/pageCustomized/mobile/template/edit?id=${id}&template=${detailInfo?.fileName}&environment=${detailInfo?.environment}`
......
......@@ -28,8 +28,8 @@ export const formatSpecialProps = (props: any, propsConfig: any) => {
if (funcTemplate) {
nextProps[k] = () => eval(funcTemplate);
} else {
nextProps[k] = () => {
};
// nextProps[k] = () => {
// };
}
}
} else if (isObject(v) && !isEmpty(propsConfig[k].childPropsConfig) && isEqual(keys(v), keys(propsConfig[k].childPropsConfig))) {
......@@ -157,16 +157,18 @@ export const getAdvertType = (type) => {
return 2
case 'category':
return 3
case 'nav':
return 4
}
}
/**
* 往数组添加模板id字段
* @param list
* @param list
*/
export const addTempalteIdToList = (list, templateId) => {
return list.map(item => {
item.templateId = templateId
return item
})
}
\ No newline at end of file
}
......@@ -4,6 +4,7 @@ import * as WarehouseApi from './WarehouseApi'
import * as MemberApi from './MemberApi'
import * as ProductApi from './ProductApi'
import * as TemplateApi from './TemplateApi'
import * as Template2Api from './Template2Api'
import * as PayApi from './PayApi'
import * as SearchApi from './SearchApi'
import * as OrderApi from './OrderApi';
......@@ -29,6 +30,7 @@ export const PublicApi = {
...MemberApi,
...ProductApi,
...TemplateApi,
...Template2Api,
...PayApi,
...SearchApi,
...OrderApi,
......
......@@ -5,6 +5,7 @@ const tokenList = [
{ name: 'Member', token: '3a46198c5b97ac7147e5b07ad2dff5ac5c93c1afed47e1911961db87149e6ebf', categoryIds: [0] }, // 商户会员管理服务
{ name: 'Product', token: 'efe99e20ed1375dc0db3e809e4fc7692f42ecebaf60cd77e65c50ed65d6ba6c4', categoryIds: [0] }, // 商品服务
{ name: 'Template', token: '7ec923520215c7e2f771867cb4d29cafbf823daf0fb2d3d9fa70b57a523c8bfb', categoryIds: [0] }, // 店铺模板服务
{ name: 'Template2', token: '7bb5578bc50e4e7935cbc146a2192e978053d5380cc96fcfdc0a754cfe885ec5', categoryIds: [0] }, // 店铺模板服务
{ name: 'Pay', token: 'c789e0e56ee8a8cc2fbd85f930eb2928c58fc1014583c6643acf29cff954da49', categoryIds: [0] }, // 支付服务
{ name: 'Search', token: 'ca19f532efba91f7773cbfbd526b798c6ac83df670071e97d72c50dca1d53a48', categoryIds: [0] }, // 搜索服务
{ name: 'Order', token: '5de0aaeaac12c8d911d86dada6cd128993e34cd6e13135fa79246aa5979a2bcd', categoryIds: [0] }, //订单服务,
......
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