Commit d1825df3 authored by GuanHua's avatar GuanHua

feat:对接店铺能力的接口

parent e52412f2
......@@ -77,9 +77,9 @@ const config: any = {
theme,
}
if (OPEN_THEME_BUILD) {
config.plugins = [...config.plugins, 'umi-plugin-antd-theme']
delete config.theme
}
// if (OPEN_THEME_BUILD) {
// config.plugins = [...config.plugins, 'umi-plugin-antd-theme']
// delete config.theme
// }
export default defineConfig(config);
......@@ -32,6 +32,7 @@
"@umijs/preset-react": "1.x",
"@umijs/test": "^3.2.0",
"bizcharts": "^4.0.7",
"copy-to-clipboard": "^3.3.1",
"god": "^0.1.17",
"lint-staged": "^10.0.7",
"mobx": "^5.15.4",
......
.common_detail_page {
margin: -24px;
.common_header {
display: flex;
height: 64px;
align-items: center;
background: #ffffff;
.title {
margin-left: 16px;
font-size: 20px;
color: #172B4D;
font-weight: 500;
}
.back_btn {
color: #6B778C;
margin-left: 27px;
font-size: 16px;
cursor: pointer;
&>span {
font-size: 14px;
margin-left: 6px;
}
}
}
.detail_page_contaner {
margin: 24px;
border-radius: 8px;
background-color: #ffffff;
}
}
\ No newline at end of file
import React, { useEffect } from 'react'
import { history } from 'umi'
import { ArrowLeftOutlined } from '@ant-design/icons'
import styles from './index.less'
interface DetailPagePropsType {
children?: React.ReactNode;
title: string;
}
const DetailPage: React.FC<DetailPagePropsType> = (props) => {
const { children, title } = props
useEffect(() => {
window.scrollTo(0, 0)
}, [])
return (
<div className={styles.common_detail_page}>
<div className={styles.common_header}>
<div className={styles.back_btn} onClick={() => history.goBack()}>
<ArrowLeftOutlined />
<span>返回</span>
</div>
<div className={styles.title}>{title}</div>
</div>
<div className={styles.detail_page_contaner}>
{children}
</div>
</div>
)
}
export default DetailPage
......@@ -3,6 +3,10 @@
width: 180px;
color: #6B778C;
&>span {
margin-right: 6px;
}
&.require {
&::after {
display: inline-block;
......
......@@ -3,14 +3,18 @@ import cx from 'classnames'
import styles from './index.less'
interface RequireItemPropsType {
label: string;
label: string | React.ReactNode;
brief?: string | React.ReactNode;
isRequire?: boolean;
}
const RequireItem: React.FC<RequireItemPropsType> = (props) => {
const { label, isRequire = false } = props
const { label, isRequire = false, brief } = props
return <label className={cx(styles.require_item, isRequire ? styles.require : '')}>{label}</label>
return <label className={cx(styles.require_item, isRequire ? styles.require : '')}>
<span>{label}</span>
{brief && brief}
</label>
}
export default RequireItem
\ No newline at end of file
......@@ -26,6 +26,11 @@
border: 1px dashed rgba(223, 225, 230, 1);
}
&.large {
width: 175px;
height: 120px;
}
&>img {
height: 100%;
width: auto;
......
......@@ -10,21 +10,23 @@ interface UploadImagePorpsType {
size?: string;
onChange: Function;
disabled?: boolean;
large?: boolean;
fileMaxSize?: number;
}
const UploadImage: React.FC<UploadImagePorpsType> = forwardRef((props, ref) => {
const { imgUrl, onChange, size = "386x256", disabled = false } = props
const { imgUrl, onChange, size = "386x256", disabled = false, large = false, fileMaxSize = 200 } = props
const [loading, setLoading] = useState<boolean>(false)
const beforeUpload = (file: UploadFile) => {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg';
if (!isJpgOrPng) {
message.error('仅支持上传JPEG/JPG/PNG文件!');
}
const isLt200k = file.size / 1024 < 200;
if (!isLt200k) {
message.error('上传图片不超过200K!');
const isSizeLimit = file.size / 1024 < fileMaxSize;
if (!isSizeLimit) {
message.error(`上传图片不超过${fileMaxSize}K!`);
}
return isJpgOrPng && isLt200k;
return isJpgOrPng && isSizeLimit;
}
const uploadProps = {
......@@ -64,14 +66,14 @@ const UploadImage: React.FC<UploadImagePorpsType> = forwardRef((props, ref) => {
return (
<div className={styles.upload_image_wrap}>
<Upload {...uploadProps}>
{<div className={cx(styles.upload_btn, !imgUrl ? styles.isAdd : "")}>
{<div className={cx(styles.upload_btn, !imgUrl ? styles.isAdd : "", large ? styles.large : '')}>
{
imgUrl ? <img src={imgUrl} /> : uploadButton
}
</div>}
</Upload>
<div className={styles.size_require}>
<p>支持JPG/PNG/JPEG, <br />最大不超过 200K, <br />尺寸:{size}</p>
<p>支持JPG/PNG/JPEG, <br />最大不超过 {fileMaxSize}K, <br />尺寸:{size}</p>
</div>
</div>
)
......
export const NOT_CHANGE_VALUE = 'hello, world'
\ No newline at end of file
export const NOT_CHANGE_VALUE = 'hello, world'
export const MALL_TYPE = {
1: '企业商城',
2: '积分商城',
3: '渠道商城',
4: '渠道自有商城',
5: '渠道积分商城'
}
export const Environment_Status = {
0: "所有",
1: "PC",
2: "H5",
3: "APP"
}
import React, { memo, useState, Fragment, forwardRef } from 'react'
import React, { memo, useState, Fragment, forwardRef, useEffect } from 'react'
import { Select } from 'antd'
import { PlusOutlined, MinusOutlined } from '@ant-design/icons'
import { PublicApi } from '@/services/api'
import cx from 'classnames'
import styles from './index.less'
interface CitySelectPropsType {
selectData: Array<{
provinceId: number;
cityId: number;
provinceCode: number;
province: string;
cityCode: number;
city: string;
}>;
onAdd: Function;
onReduce: Function;
......@@ -21,61 +24,84 @@ interface selectItemType {
}
const { Option } = Select;
const provinceData = [
{
lable: '广东省',
value: 1,
},
{
lable: '浙江省',
value: 2
},
]
const cityData: any = {
1: [
{
lable: '广州市',
value: 11
},
{
lable: '深圳市',
value: 12
}
],
2: [
{
lable: '杭州市',
value: 21
},
{
lable: '温州市',
value: 22
}
],
};
const getProviceById = (id: number) => {
let result: number = 0
provinceData && provinceData.map(item => {
if (item.value === id) {
result = item.value
}
})
return result
}
const CityCascader: React.FC<CitySelectPropsType> = (props) => {
const { selectData, onAdd, onReduce, onChange } = props
const [provinceData, setProvinceData] = useState([])
const [cityData, setCityData] = useState({})
const [cities, setCities] = useState([])
useEffect(() => {
fetchAreaList()
}, [])
const getProviceById = (id: number) => {
let result: number = 0
provinceData && provinceData.map(item => {
if (item.value === id) {
result = item
}
})
return result
}
const getCityById = (id: number) => {
let result: number = 0
cities && cities.map(item => {
if (item.value === id) {
result = item
}
})
return result
}
const fetchAreaList = () => {
PublicApi.getManageAreaAll().then(res => {
initProvinceAndCityData(res.data)
})
}
const initProvinceAndCityData = (areaList: any) => {
if (!areaList) {
return
}
let tempProvinceData = []
let tempCityData = {}
for (let item of areaList) {
tempProvinceData.push({
lable: item.name,
value: item.code
})
if (!!item.areaResponses) {
let tempCityList = []
for (let cityItem of item.areaResponses) {
tempCityList.push({
lable: cityItem.name,
value: cityItem.code
})
}
tempCityData[item.code] = tempCityList
}
}
setProvinceData(tempProvinceData)
setCityData(tempCityData)
}
const handleProvinceChange = (value: number, index: number) => {
let newData = JSON.parse(JSON.stringify(selectData))
let proviceById = getProviceById(value)
setCities(cityData[proviceById])
let proviceById: any = getProviceById(value)
console.log(cityData[proviceById.value], "cityData[proviceById.value]")
setCities(cityData[proviceById.value])
newData.map((item: any) => {
if (item.index === index) {
item.provinceId = value
item.provinceCode = value
item.province = proviceById.lable
item.cityCode = cityData[proviceById.value][0].value
item.city = cityData[proviceById.value][0].lable
}
return item
})
......@@ -84,9 +110,12 @@ const CityCascader: React.FC<CitySelectPropsType> = (props) => {
const onSecondCityChange = (value: number, index: number) => {
let newData = JSON.parse(JSON.stringify(selectData))
const cityById: any = getCityById(value)
newData.map((item: any) => {
if (item.index === index) {
item.cityId = value
item.cityCode = cityById.value
item.city = cityById.lable
}
return item
})
......@@ -94,13 +123,15 @@ const CityCascader: React.FC<CitySelectPropsType> = (props) => {
}
const handleAddNewSelect = () => {
onAdd({ index: selectData.length + 1, provinceId: 0, cityId: 0 })
onAdd({ index: selectData.length + 1, provinceCode: 0, province: '', cityCode: 0, city: '' })
}
const handleReduceSelect = (index: number) => {
if (selectData.length > 1) {
onReduce(index)
}
}
return (
<Fragment>
{
......@@ -108,7 +139,7 @@ const CityCascader: React.FC<CitySelectPropsType> = (props) => {
<div className={styles.city_select_line} key={`selectDataItem-${index}`}>
<Select
style={{ width: 278 }}
value={item.provinceId ? item.provinceId : undefined}
value={item.provinceCode ? item.provinceCode : undefined}
onChange={(value) => handleProvinceChange(value, item.index)}
placeholder="请选择"
>
......@@ -118,7 +149,7 @@ const CityCascader: React.FC<CitySelectPropsType> = (props) => {
</Select>
<Select
style={{ width: 278, marginLeft: 16, marginRight: 24 }}
value={item.cityId ? item.cityId : undefined}
value={item.cityCode ? item.cityCode : undefined}
onChange={(value) => onSecondCityChange(value, item.index)}
placeholder="请选择"
>
......
......@@ -10,20 +10,35 @@ interface TemplateItemPropsType {
type: string;
}
const Environment_Status = {
0: {
name: "所有"
},
1: {
name: "PC "
},
2: {
name: "H5 "
},
3: {
name: "APP "
}
}
const TemplateItem: React.FC<TemplateItemPropsType> = (props) => {
const { templateInfo, type } = props
return (
<div className={styles.template_item}>
<div className={styles.img_box} style={{ backgroundImage: `url(${default_img})` }}>
<div className={styles.img_box} style={{ backgroundImage: `url(${templateInfo.templatePicUrl})` }}>
<div className={styles.img_box_mask}>
<Link to={`/memberCenter/shopAbility/template/detail?type=${type}`} className={styles.detail_btn}>查看详情</Link>
<Link to={`/memberCenter/shopAbility/template/detail?type=${type}&id=${templateInfo.id}`} className={styles.detail_btn}>查看详情</Link>
</div>
<div className={cx(styles.type_tag, templateInfo.platform === 'H5' ? styles.h5 : '')}>{templateInfo.platform}</div>
<div className={cx(styles.type_tag, templateInfo.environment === 2 ? styles.h5 : '')}>{Environment_Status[templateInfo.environment].name}</div>
</div>
<div className={styles.template_info}>
<div className={styles.template_info_name}>
<span>{templateInfo.name}</span>
<span>{templateInfo.templateName}</span>
{
templateInfo.isDefault && <div className={styles.tag}>默认模板</div>
}
......@@ -32,17 +47,17 @@ const TemplateItem: React.FC<TemplateItemPropsType> = (props) => {
<div className={styles.template_info_content_text_wrap}>
<div className={styles.template_info_content_text_line}>
<label>使用站点:</label>
<span>{templateInfo.station}</span>
<span>{templateInfo.siteName}</span>
</div>
<div className={styles.template_info_content_text_line}>
<label>使用商城:</label>
<span>{templateInfo.useShopCenter}</span>
<span>{templateInfo.shopName}</span>
</div>
</div>
<div className={cx(styles.template_item_btn, templateInfo.isUse ? styles.active : '')}>
<PlayCircleOutlined />
<label>{templateInfo.isUse ? '启用中' : '启用'}</label>
<label>{templateInfo.use === 1 ? '启用中' : '启用'}</label>
</div>
</div>
</div>
......
import React from 'react'
import { Modal, Form, Select, Checkbox } from 'antd'
import styles from './index.less'
import detailInfo from '@/pages/logistics/components/orderSearchDetail'
interface UseModalPropsType {
visible: boolean;
onOk: Function;
onCancel: Function;
title: string;
dataInfo: any;
confirmLoading?: boolean;
}
const UseModal: React.FC<UseModalPropsType> = (props) => {
const { visible, onOk, onCancel, title } = props
const { visible, onOk, onCancel, title, dataInfo = {}, confirmLoading = false } = props
return (
......@@ -18,22 +21,36 @@ const UseModal: React.FC<UseModalPropsType> = (props) => {
width={576}
title={title}
visible={visible}
confirmLoading={confirmLoading}
onOk={() => onOk()}
centered
onCancel={() => onCancel()}
>
<div className={styles.text_line}>
<span>您选择的站点</span>
<label>美国站-WEB商城</label>
<span>现在使用的模板是</span>
<label>“模板003-清新类”</label>
<span>模板,</span>
</div>
<div className={styles.text_line}>
<span>您是否使用</span>
<label>“模板001-清新类”</label>
<span>模板,来替换您正在使用的模板</span>
</div>
{
dataInfo.inUseTemplateName ? (
<>
<div className={styles.text_line}>
<span>您选择的站点</span>
<label>{dataInfo.siteName}-{dataInfo.shopName}</label>
<span>现在使用的模板是</span>
<label>{dataInfo.inUseTemplateName}</label>
<span>模板,</span>
</div>
<div className={styles.text_line}>
<span>您是否使用</span>
<label>{dataInfo.templateName}</label>
<span>模板,来替换您正在使用的模板</span>
</div>
</>
) : (
<div className={styles.text_line}>
<span>您是否使用</span>
<label>{dataInfo.templateName}</label>
<span>模板?</span>
</div>
)
}
</Modal>
)
}
......
......@@ -31,6 +31,22 @@
}
.shop_url {
color: #6B778C;
margin-top: 16px;
font-size: 14px;
&>label {
color: #172B4D;
margin-left: 8px;
margin-right: 15px;
}
.copy_icon {
cursor: pointer;
}
}
.upload_btn {
position: relative;
width: 104px;
......@@ -45,6 +61,7 @@
border: 1px dashed rgba(223, 225, 230, 1);
cursor: pointer;
margin-right: 24px;
overflow: hidden;
&.upload {
border: 1px solid rgba(223, 225, 230, 1);
......@@ -53,9 +70,12 @@
.upload_img {
height: 100%;
width: auto;
width: 100%;
display: block;
margin: 0 auto;
background-size: auto 100%;
background-position: center center;
background-repeat: no-repeat;
}
&>img {
......
This diff is collapsed.
import React from 'react'
import React, { useEffect, useState } from 'react'
import { Row, Col } from 'antd'
import { PageHeaderWrapper } from '@ant-design/pro-layout'
import TemplateItem from '../components/templateItem'
import { PublicApi } from '@/services/api'
import { GetTemplateShopFindAllShelfShopTemplateResponse } from '@/services'
import styles from './index.less'
const ShopTemplate: React.FC = () => {
const template_data_mock = [
{
id: 1,
name: '模板001-清新类',
platform: 'PC',
station: '中国站',
useShopCenter: 'WEB用户商城',
isUse: true,
isDefault: true,
},
{
id: 2,
name: '模板002-科技类',
platform: 'H5',
station: '美国站',
useShopCenter: 'H5用户商城',
isUse: true,
isDefault: true,
},
{
id: 3,
name: '模板003-科技类',
platform: 'PC',
station: '美国站',
useShopCenter: 'WEB用户商城',
isUse: false,
isDefault: false,
}
]
const [templateList, setTemplateList] = useState<GetTemplateShopFindAllShelfShopTemplateResponse>([])
useEffect(() => {
fetchAllShelfShopTemplate()
}, [])
const fetchAllShelfShopTemplate = () => {
PublicApi.getTemplateShopFindAllShelfShopTemplate().then(res => {
setTemplateList(res.data)
})
}
return (
<PageHeaderWrapper>
<div className={styles.shop_center_template}>
<Row gutter={24} className={styles.template_list}>
{
template_data_mock.map(item => (
templateList.map(item => (
<Col span={6} key={item.id}>
<TemplateItem templateInfo={item} type="shop" />
</Col>
......
......@@ -2,7 +2,7 @@
height: 224px;
background-color: #ffffff;
display: flex;
margin: -24px;
border-radius: 8px;
align-items: center;
.back_btn {
......@@ -89,6 +89,12 @@
&>label {
margin-left: 5px;
cursor: pointer;
}
&.fit {
background: #6B778C;
color: #ffffff;
}
&.use {
......
import React, { useState } from 'react'
import React, { useState, useEffect } from 'react'
import { history } from 'umi';
import { ArrowLeftOutlined, EyeOutlined, PushpinOutlined } from '@ant-design/icons'
import { LayoutOutlined, EyeOutlined, PushpinOutlined } from '@ant-design/icons'
import cx from 'classnames'
import DetailPage from '@/components/DetailPage'
import UseModal from '../components/useModal'
import { PublicApi } from '@/services/api'
import { Environment_Status } from '@/constants'
import { GetTemplateShopFindShopTemplateDetailsResponse } from '@/services'
import default_img from '@/assets/imgs/template_default_img.png'
import styles from './index.less'
interface TemplateDetailPropsType {
location: {
query: {
id: number;
type: string; // 商城模板:shopCenter ;店铺模板:shop; 商品描述模板: goods
}
}
}
const TemplateDetail: React.FC<TemplateDetailPropsType> = (props) => {
const { location: { query } } = props
const { query: { type, id } } = props.location
const [detailInfo, setDetailInfo] = useState<GetTemplateShopFindShopTemplateDetailsResponse>()
const [useModalVisible, setUseModalVisible] = useState<boolean>(false)
const [confirmLoading, setConfirmLoading] = useState(false)
useEffect(() => {
fetchDetail()
}, [])
const fetchDetail = () => {
//@ts-ignore
PublicApi.getTemplateShopFindShopTemplateDetails({ id }).then(res => {
setDetailInfo(res.data)
})
}
/**
* 使用模板
*/
const handleConfirmUse = () => {
setConfirmLoading(true)
//@ts-ignore
PublicApi.postTemplateShopUseShopTemplate({ id }).then(res => {
if (res.code === 1000) {
setUseModalVisible(false)
fetchDetail()
}
setConfirmLoading(false)
}).catch(() => {
setConfirmLoading(false)
})
}
return (
<div className={styles.template_detail}>
<div className={styles.back_btn} onClick={() => history.goBack()}>
<ArrowLeftOutlined />
</div>
<div className={styles.template_info_wrap}>
<div className={styles.template_img_box}>
<div className={cx(styles.type_tag)}>PC</div>
<img src={default_img} />
</div>
<div className={styles.template_info}>
<div className={styles.template_info_line}>
<label>模板名称:</label>
<span>模板001-清新类</span>
</div>
<div className={styles.template_info_line}>
<label>适用环境:</label>
<span>PC</span>
<DetailPage
title="查看模板"
>
<div className={styles.template_detail}>
<div className={styles.template_info_wrap}>
<div className={styles.template_img_box}>
<div className={cx(styles.type_tag)}>{Environment_Status[detailInfo?.environment]}</div>
<img src={default_img} />
</div>
<div className={styles.template_info_line}>
<label>模板描述:</label>
<span>此模板只适用于PC端</span>
</div>
<div className={styles.template_info_line}>
<label>使用站点:</label>
<span>中国站</span>
</div>
<div className={styles.template_info_line}>
<label>使用商城:</label>
<span>WEB用户商城</span>
<div className={styles.template_info}>
<div className={styles.template_info_line}>
<label>模板名称:</label>
<span>{detailInfo?.templateName}</span>
</div>
<div className={styles.template_info_line}>
<label>适用环境:</label>
<span>{Environment_Status[detailInfo?.environment]}</span>
</div>
<div className={styles.template_info_line}>
<label>模板描述:</label>
<span>{detailInfo?.templateDescribe}</span>
</div>
<div className={styles.template_info_line}>
<label>使用站点:</label>
<span>{detailInfo?.siteName}</span>
</div>
<div className={styles.template_info_line}>
<label>使用商城:</label>
<span>{detailInfo?.shopName}</span>
</div>
</div>
</div>
</div>
<div className={styles.btn}>
<EyeOutlined />
<label>预览</label>
</div>
<div className={cx(styles.btn, styles.use)}>
<PushpinOutlined />
<label>店铺装修</label>
</div>
<div className={cx(styles.btn, styles.use)} onClick={() => setUseModalVisible(true)}>
<PushpinOutlined />
<label>使用</label>
</div>
<div className={styles.btn}>
<EyeOutlined />
<label>预览</label>
</div>
<div className={cx(styles.btn, styles.fit)}>
<LayoutOutlined />
<label>店铺装修</label>
</div>
<div className={cx(styles.btn, detailInfo?.use === 1 ? styles.use : '')} onClick={() => setUseModalVisible(true)}>
<PushpinOutlined />
<label>{detailInfo?.use === 1 ? '使用中' : '使用'}</label>
</div>
<UseModal
title="使用商场模板"
visible={useModalVisible}
onOk={() => { }}
onCancel={() => setUseModalVisible(false)}
/>
</div>
<UseModal
title="使用店铺模板"
visible={useModalVisible}
dataInfo={detailInfo}
confirmLoading={confirmLoading}
onOk={() => handleConfirmUse()}
onCancel={() => setUseModalVisible(false)}
/>
</div>
</DetailPage>
)
}
......
import UserStore from './user'
import ThemeStore from './theme'
import ProductStroe from './product'
import SiteStore from './site'
import React from 'react';
import { Provider } from 'mobx-react'
import { ILoginModule } from '@/module/userModule';
import { IProductModule } from '@/module/productModule'
// import { ProductContext } from '@/pages/commodity/products/addProducts';
/**
......@@ -30,7 +32,8 @@ export interface IStore {
export const store = {
userStore: new UserStore,
ThemeStore: new ThemeStore,
ProductStore: new ProductStroe
ProductStore: new ProductStroe,
SiteStore: new SiteStore
}
const MobxProvider: React.FC = (props) => {
......
import {action, computed, observable, runInAction} from 'mobx'
import { action, computed, observable, runInAction } from 'mobx'
import { ISiteModule } from '@/module/siteModule';
class SiteStore implements ISiteModule {
@observable public siteId: number = 255;
@observable public siteId: number = 255; // 站点id
@observable public siteUrl: string = 'https://127.0.0.1:4379'; // 站点域名
}
export default SiteStore
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