Commit 2c0fcf86 authored by LeeJiancong's avatar LeeJiancong

'修改支付'

parents 5bca989b c86a0aec
......@@ -27,3 +27,6 @@
/src/services/index.ts
.vscode
config/base.config.json
src/global/config/global.d.ts
src/services/*Api.ts
\ No newline at end of file
......@@ -18,6 +18,14 @@ const mallRoute = {
component: '@/pages/lxMall/commodity',
},
{
// 商品搜索
path: `/commodity/search`,
name: 'mallCommoditySearch',
key: 'mallCommoditySearch',
hide: true,
component: '@/pages/lxMall/commodity/search',
},
{
// 订单
path: `/order`,
name: 'order',
......
const AuthConfigRoute = {
path: '/memberCenter/systemSetting',
name: 'systemSetting',
key: 'systemSetting',
icon: 'smile',
routes: [
{
path: '/memberCenter/systemSetting/authConfig',
key: 'authConfig',
name: 'authConfig',
routes: [
{
path: '/memberCenter/systemSetting/authConfig/organ',
name: 'organ',
component: '@/pages/authConfig/organ',
},
{
path: '/memberCenter/systemSetting/authConfig/memberSystem',
name: 'memberSystem',
component: '@/pages/authConfig/memberSystem',
},
{
path: '/memberCenter/systemSetting/authConfig/memberSystem/memberDetail',
name: 'memberDetail',
component: '@/pages/authConfig/memberSystem/memberDetail',
hideInMenu: true,
hidePageHeader: true
},
{
path: '/memberCenter/systemSetting/authConfig/userSystem',
name: 'userSystem',
component: '@/pages/authConfig/userSystem',
},
{
path: '/memberCenter/systemSetting/authConfig/userSystem/userDetail',
name: 'userSystem',
component: '@/pages/authConfig/userSystem/userDetail',
hideInMenu: true,
hidePageHeader: true
},
]
}
],
}
export default AuthConfigRoute
\ No newline at end of file
......@@ -11,8 +11,9 @@ import ChannelRoute from './channelRoute' // 渠道能力路由
import TranactionRoute from './tranactionRoute' // 交易能力路由
import LogisticsRoute from './logisticsRoutes' // 物流能力路由
import PayandSettleRoute from './payandSettle' //支付与结算
import AuthConfigRoute from './authConfigRoute'
const routes = [CommodityRoute, MemberRoute, ShopRoute, ChannelRoute, TranactionRoute,PayandSettleRoute ,LogisticsRoute]
const routes = [CommodityRoute, MemberRoute, ShopRoute, ChannelRoute, TranactionRoute,PayandSettleRoute ,LogisticsRoute, AuthConfigRoute]
const memberCenterRoute = {
path: '/memberCenter',
......
......@@ -50,7 +50,18 @@ const serviceConfig = {
// // }
// // }
// }
<<<<<<< HEAD
}
=======
},
//初始化会员支付策略配置
// payConfig:{
// paymemberConfig:{
// url:'/pay/member/pay/config',
// method: 'get'
// }
// }
>>>>>>> c86a0aec36df53e09e3699378805531daf993223
}
......@@ -75,19 +86,47 @@ async function batchAxiosHttps() {
// }
// }
// serverFn(asyncHttpQueue)
const httpErrorQueue = []
const serverErrorQueue = []
console.log('\n')
for (const item in serviceConfig) {
if(JSON.stringify(item) !== '{}'){
for (const subItem in serviceConfig[item]) {
try {
const data = await axios(serviceConfig[item][subItem])
asyncHttpQueue[item][subItem] = data.data.data
// 当接口出错时 不写入json文件
if (data.data.code === 1000) {
asyncHttpQueue[item][subItem] = data.data.data
} else {
serverErrorQueue.push({ ...asyncHttpQueue[item][subItem], ...data.data})
// 默认置为null
asyncHttpQueue[item][subItem] = null
}
} catch(err) {
console.log(serviceConfig[item][subItem].url)
console.log(err.response.data)
httpErrorQueue.push({...serviceConfig[item][subItem], ...err.response.data})
}
}
}
}
if (httpErrorQueue.length > 0) {
console.log('\n网络错误\n')
// 可在此做日志收集
console.log(httpErrorQueue)
}
if (serverErrorQueue.length > 0) {
console.log('\n接口服务错误\n')
// 可在此做日志收集
console.log(serverErrorQueue)
}
if (httpErrorQueue.length > 0 || serverErrorQueue.length > 0) {
// 退出构建
console.log('\n脚本构建失败!!!!!!')
// 终止当前进程
process.exit(1)
}
return isDemo ? Object.assign(asyncHttpQueue, await demoFetch()) : asyncHttpQueue
}
......
......@@ -26,15 +26,15 @@ const whiteLists = [
...userLoginLists,
'/',
'/channelmall',
'/shop',
'/purchaseOnline',
'/pointsMall',
'/memberCenter',
// '/memberCenter',
'/memberCenter/noAuth',
'/commodity',
'/shops',
'/shop/commodity/detail',
'/shop/commodity',
'/shop',
'/infomation',
'/403',
'/404',
......@@ -70,22 +70,23 @@ export function patchRoutes({ routes }: IRoutes) {
* @export
*/
export function render(oldRender: Function) {
const { pathname } = history.location
// 白名单页面不进行权限校验
if (whiteLists.includes(pathname)) {
oldRender()
return;
}
const authInfo = getAuth()
if (authInfo) {
PublicApi.getMemberLoginReget().then(res => {
const { data, code } = res
if (code === 1000) {
setAuth({
memberId: data.memberId,
userId: data.userId,
token: data.token
})
setAuth(data)
setRouters(data.urls)
} else {
removeAuth()
removeRouters()
history.push('/user/login')
history.replace('/user/login')
}
oldRender()
......@@ -117,7 +118,7 @@ export function onRouteChange({ routes, matchedRoutes, location, action }) {
if (userLoginLists.includes(pathname)) {
// 当登录过, 并且尝试访问登录相关页面, 需重定向到首页
history.replace('/memberCenter/home')
return ;
return;
}
// 固定配置, 出现此参数说明需携带参数校验权限路由
if (query.page_type && routeAuthUrls.find(authPath => {
......
.common_detail_page {
margin: -24px;
// margin: -24px;
.common_header {
display: flex;
height: 64px;
min-height: 64px;
align-items: center;
background: #ffffff;
justify-content: space-between;
padding: 0 24px;
.title {
margin-left: 16px;
......
import React, { useEffect } from 'react'
import React, { useEffect, useContext } from 'react'
import { history } from 'umi'
import { ArrowLeftOutlined } from '@ant-design/icons'
import styles from './index.less'
import { historyContainer } from '@/hooks/useHistoryContainer'
import { Row } from 'antd'
interface DetailPagePropsType {
children?: React.ReactNode;
title: string;
title?: React.ReactNode;
extra?: React.ReactNode
}
const DetailPage: React.FC<DetailPagePropsType> = (props) => {
const { children, title } = props
const { children, title, extra } = props
const routerInfo = useContext(historyContainer)
const defaultTitle = routerInfo ? routerInfo.name : ''
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>
<Row>
<div className={styles.back_btn} onClick={() => history.goBack()}>
<ArrowLeftOutlined />
<span>返回</span>
</div>
<div className={styles.title}>{title || defaultTitle}</div>
</Row>
<div>{extra}</div>
</div>
<div className={styles.detail_page_contaner}>
{children}
......
......@@ -38,7 +38,7 @@ const ModalTable:React.FC<ModalTableProps> = (props) => {
tableType='small'
currentRef={selfRef}
formRender={(child, ps) => <Row justify='space-between'>
<Col>{child}</Col>
<Col span={18}>{child}</Col>
<Col style={{marginTop: 4}}>{ps}</Col>
</Row>}
{...resetTable}
......
......@@ -116,6 +116,10 @@ const SearchSelect = (props: ISchemaFieldComponentProps) => {
// }
// const { run } = useDebounceFn(dispatchSearch, 500)
const multipleProps = multiple ? {
open: openSelect,
onFocus: () => setOpenSelect(true),
} : {}
return (
<Select
ref={ref}
......@@ -124,10 +128,12 @@ const SearchSelect = (props: ISchemaFieldComponentProps) => {
filterOption={false}
loading={loading}
options={dataSource}
onFocus={() => setOpenSelect(true)}
getPopupContainer={triggerNode => {
return triggerNode
}}
value={props.value}
open={multiple ? openSelect : null}
dropdownRender={originNode => <SelectContent confirm={confirm} resetField={resetField} parentRef={ref} handleChange={handleChange} multiple={multiple} value={props.value}>{originNode}</SelectContent>}
{...multipleProps}
{...resetProps}
>
</Select>
......
import React, { useState } from 'react'
import { Tag, Row } from 'antd'
import { useFormEffects } from '@formily/antd'
const TableTagList = (props) => {
const { value = [], mutators, editable } = props
const { extra = null, callback = null } = props.props['x-component-props'] ? props.props['x-component-props'] : {}
const handleClose = (id) => {
callback && callback(id)
mutators.remove(value.findIndex(v => v.id === id))
}
return (
<div className="table-tag-list" style={{width: '100%'}}>
<Row style={{flexWrap: 'wrap'}}>
{
value.map(v => <Tag closable={editable} onClose={() => handleClose(v.id)} color="#4279DF" key={v.id} style={{marginBottom: 8}}>{v.roleName}</Tag>)
}
</Row>
{extra}
</div>
)
}
TableTagList.defaultProps = {}
TableTagList.isFieldComponent = true;
export default TableTagList
\ No newline at end of file
......@@ -22,6 +22,7 @@ import CircleBox from './components/CircleBox';
import Phone from './components/Phone';
import CustomRadio from './components/CustomRadio';
import SearchSelect from './components/SearchSelect';
import TableTagList from './components/TableTagList';
import './index.less'
import { Checkbox } from '@formily/antd-components';
import cx from 'classnames'
......@@ -141,6 +142,7 @@ export const componentExport = {
Phone,
SearchSelect,
Input,
TableTagList,
}
const NiceForm: React.FC<NiceFormProps> = props => {
const { children, components, ...reset } = props;
......
......@@ -4,14 +4,14 @@ import { PlayCircleOutlined,PauseCircleOutlined } from '@ant-design/icons'
export interface StatusSwitchProps {
record: any,
fieldNames?: string, // 自定义字段名称 默认'state'
fieldNames?: string, // 自定义字段名称 默认'status'
expectTrueValue?: boolean|number|string, //期望为ture(有效)的值 默认1
handleConfirm?(),
handleCancel?()
}
const StatusSwitch:React.FC<StatusSwitchProps> = (props) => {
const { record, fieldNames = 'state', expectTrueValue = 1 } = props
const { record, fieldNames = 'status', expectTrueValue = 1 } = props
return (
<Popconfirm
title="确定要执行这个操作?"
......
......@@ -51,7 +51,7 @@ export interface RenderIconsProps {
export const useTreeActions = (action?): TabTreeActions => {
const actionRef = useRef<any>(null);
actionRef.current = actionRef.current || {};
actionRef.current = actionRef.current || action || createTreeActions();
return actionRef.current;
};
......
......@@ -8,6 +8,12 @@ export const MALL_TYPE = {
5: '渠道积分商城'
}
export enum LAYOUT_TYPE {
mall = 'mall',
shop = 'shop',
channel = 'channel'
}
// 本地环境跳过权限校验
export const isDev = process.env.NODE_ENV === "development"
// export const isDev = false
......@@ -19,6 +25,53 @@ export const Environment_Status = {
3: "APP"
}
export enum FILTE_RTYPE {
/**
* 常用筛选
*/
commonlyUsed = 'commonlyUsed',
/**
* 分类
*/
category = 'category',
/**
* 风格
*/
style = 'style',
/**
* 品牌
*/
brand = 'brand',
/**
* 价格
*/
price = 'price',
/**
* 发货地
*/
useArea = 'useArea',
/**
* 商品类型
*/
commodityType = 'commodityType',
/**
* 活跃店铺
*/
activeStores = 'activeStores',
/**
* 最新加入
*/
newJoin = 'newJoin',
/**
* 所需积分
*/
points = 'points',
/**
* 商品名称
*/
name = 'name'
}
// 商城类型
export const SHOP_TYPES = [
{
......@@ -43,6 +96,21 @@ export const SHOP_TYPES = [
},
]
export const STATUS_ENUM = [
{
label: '全部',
value: null
},
{
label: '有效',
value: 1
},
{
label: '无效',
value: 0
}
]
// 1是阿里云oss服务器, 2是本地文件服务器
export const UPLOAD_TYPE = isDev ? 2 : 1
......
export interface MemberType {
id: number;
typeName: string;
}
export interface BusinessType {
id: number;
typeName: string;
}
export interface UseType {
memberType: MemberType[];
businessType: BusinessType[];
}
export interface UserRegister {
useType: UseType;
}
export interface ShopInfo {
id: number;
name: string;
type: number;
environment: number;
logoUrl: string;
describe: string;
state: number;
url: string;
}
export interface Web {
shopInfo: ShopInfo[];
}
export interface PayConfig {
paymemberConfig?: any;
}
export interface CountryList {
name: string;
key: string;
icon: string;
}
export interface Global {
siteId: number;
siteUrl: string;
logo: string;
countryList: CountryList[];
}
export interface RootObject {
userRegister: UserRegister;
web: Web;
payConfig: PayConfig;
global: Global;
}
\ No newline at end of file
......@@ -149,6 +149,9 @@ h6 {
background: none;
}
}
.white-wrapper {
background-color: #fff;
}
//addonAfter 有选择弹窗时
.input_addonAfter{
......@@ -203,6 +206,8 @@ h6 {
}
}
.tree-node-circle {
width: 4px;
height: 4px;
......@@ -263,4 +268,11 @@ h6 {
position: absolute;
top: 100%;
font-size: 12px;
}
// 强制覆盖下拉框固定位置
.fixed-ant-selected-down {
.ant-select-dropdown {
top: 36px !important;
}
}
\ No newline at end of file
import React, { createContext } from 'react'
export const historyContainer = createContext<any>(null);
......@@ -12,6 +12,7 @@ import RightContent from './components/RightContent';
// import logo from '../assets/logo.svg';
import logo from '../../mockStatic/logo.png'
import MenuSlider from './components/MenuSlider'
import { getMatchMenu } from '@umijs/route-utils';
export interface BasicLayoutProps extends ProLayoutProps {
breadcrumbNameMap: {
......@@ -31,6 +32,23 @@ export type BasicLayoutContext = { [K in 'location']: BasicLayoutProps[K] } & {
};
};
export const getSelectedMenuKeys = (
pathname: string,
menuData: MenuDataItem[],
): string[] => {
const menus = getMatchMenu(pathname, menuData);
const dispatchMenu = menus.map(v => {
if (v.hideInMenu) {
const flag = v.key || v.path || ''
const substr = flag.substr(0, flag.lastIndexOf('/'))
v.path = substr
v.key = substr
}
return v
})
return dispatchMenu.map(item => item.key || item.path || '');
};
const defaultFooterDom = (
<DefaultFooter
copyright={`${new Date().getFullYear()} 技术组体验出品`}
......@@ -63,7 +81,7 @@ const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
};
const [openKeys, setOpenKeys] = useState<string[]>([])
const [collapsed, setCollapsed] = useState(false)
const [selectedKeys, setSelectedKeys] = useState<string[] | undefined>([]);
const handleMenuCollapse = (payload: boolean): void => {
setCollapsed(payload)
if (payload) {
......@@ -90,6 +108,19 @@ const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
const menuRouter = getMenuRouter(menuData, location.pathname)
useEffect(() => {
// if pathname can't match, use the nearest parent's key
const keys = getSelectedMenuKeys(
location.pathname || '/',
basicInfo.menuData || [],
);
const animationFrameId = requestAnimationFrame(() => {
setSelectedKeys(keys);
});
return () =>
window.cancelAnimationFrame &&
window.cancelAnimationFrame(animationFrameId);
}, [location.pathname]);
useEffect(() => {
if (menuRouter && menuRouter.children) {
if (collapsed) {
setOpenKeys([])
......@@ -118,7 +149,7 @@ const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
]}
menuRender={() => <MenuSlider
currentSelectKey={[location.pathname]}
currentSelectKey={selectedKeys}
openKeys={openKeys}
menuData={menuData}
pathname={location.pathname}
......
......@@ -5,7 +5,7 @@ import React from 'react';
import { history } from 'umi';
import PersonDropdown from './PersonDropdown'
import styles from '../styles/RightContent.less';
import { removeAuth, removeRouters } from '@/utils/auth';
import { removeAuth, removeRouters, getAuth } from '@/utils/auth';
const AvatarDropdown = () => {
......@@ -16,7 +16,7 @@ const AvatarDropdown = () => {
}
const currentUser = {
name: 'Serati Ma',
name: getAuth().name || '未知用户',
avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
}
......
......@@ -123,4 +123,13 @@ export default {
'menu.payandSettle': '支付',
'menu.payandSettle.paySetting': '支付方式管理',
'menu.payandSettle.paySetting.payParamsSetting': '会员支付参数配置',
// 权限管理
'menu.systemSetting': '系统',
'menu.systemSetting.authConfig': '平台权限',
'menu.systemSetting.authConfig.organ': '组织机构',
'menu.systemSetting.authConfig.memberSystem': '角色管理',
'menu.systemSetting.authConfig.memberDetail': '角色详情',
'menu.systemSetting.authConfig.userSystem': '用户管理',
'menu.systemSetting.authConfig.userDetail': '用户详情',
};
\ No newline at end of file
.menu-role-tree {
padding: 24px;
border: 1px solid #F4F5F7;
}
\ No newline at end of file
import React, { ReactNode, useRef } from 'react';
import { history, Link } from 'umi'
import { Button, Popconfirm, Card } from 'antd';
import {
PlusCircleOutlined,
PlayCircleOutlined,
PauseCircleOutlined,
PlusOutlined,
EyeOutlined
} from '@ant-design/icons';
import {StandardTable} from 'god'
import {ColumnType} from 'antd/lib/table/interface'
import { IButtonFilter, IFormFilter } from 'god/dist/src/standard-table/TableController';
import { PublicApi } from '@/services/api';
import StatusSwitch from '@/components/StatusSwitch';
import EyePreview from '@/components/EyePreview';
import { STATUS_ENUM } from '@/constants';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
const fetchData = async (params) => {
const { data } = await PublicApi.getMemberRolePage(params)
return data
}
const MemberSystem: React.FC<{}> = () => {
const ref = useRef<any>({})
const deleteItem = async (record) => {
// 删除该项
await PublicApi.postMemberRoleDelete({
memberRoleId: record.id
})
ref.current.reload()
}
const updateItem = (record) => {
history.push(`/memberCenter/systemSetting/authConfig/memberSystem/memberDetail?id=${record.id}&preview=0`)
}
const handleStatus = async (record) => {
await PublicApi.postMemberRoleUpdatestatus({
id: record.id,
status: record.status === 1 ? 0 : 1
})
ref.current.reload()
}
const columns: ColumnType<any>[] = [
{
title: 'ID',
dataIndex: 'id',
align: 'center',
key: 'id',
},
{
title: '角色名称',
dataIndex: 'roleName',
align: 'center',
key: 'roleName',
className: 'commonPickColor',
render: (text, record) => <EyePreview url={`/memberCenter/systemSetting/authConfig/memberSystem/memberDetail?id=${record.id}&preview=1`}>{text}</EyePreview>
},
{
title: '描述',
align: 'center',
dataIndex: 'remark',
key: 'remark',
},
{
title: '状态',
align: 'center',
dataIndex: 'status',
key: 'status',
render: (text: any, record:any) => <StatusSwitch record={record} handleConfirm={() => handleStatus(record)}/>
},
{
title: '操作',
dataIndex: 'option',
align: 'center',
render: (text:any, record:any) => {
return record.status === 0 ? (
<>
<Popconfirm
title="确定要执行这个操作?"
onConfirm={() => deleteItem(record)}
okText="是"
cancelText="否"
>
<Button type='link'>删除</Button>
</Popconfirm>
<Button type='link' onClick={()=>updateItem(record)}>修改</Button>
</>
) : null
}
}
];
return (
<PageHeaderWrapper>
<Card className="common-wrapper">
<StandardTable
columns={columns}
currentRef={ref}
fetchTableData={(params:any) => fetchData(params)}
formilyLayouts={{
justify: 'space-between'
}}
formilyChilds={{
layouts: {
order: 2
},
children: <Button style={{width: 140}} icon={<PlusOutlined/>} onClick={() => history.push('/memberCenter/systemSetting/authConfig/memberSystem/memberDetail')} type='primary'>新建</Button>
}}
formilyProps={{
layouts: {
order: 3
},
ctx: {
effects: ($) => {
$('onFieldInputChange', 'status').subscribe(() => {
ref.current.reload()
})
},
schema: {
type: 'object',
properties: {
roleName: {
type: 'Search',
"x-component-props": {
placeholder: '请输入角色名称'
}
},
status: {
type: 'string',
enum: STATUS_ENUM,
"x-component-props": {
placeholder: '请选择状态'
}
}
}
}
}
}}
/>
</Card>
</PageHeaderWrapper>
)
}
export default MemberSystem
import React, { useContext, useState, useEffect, useRef, useLayoutEffect, useCallback } from 'react';
import { Row, Col, Button, Form, Input, Space, Tabs, message, Badge, Card } from 'antd';
import {IntegrateTree} from 'god'
import { history } from 'umi';
import { historyContainer } from '@/hooks/useHistoryContainer';
import NiceForm from '@/components/NiceForm';
import TabTree, { useTreeActions, createTreeActions } from '@/components/TabTree';
import { PublicApi } from '@/services/api';
import styled from './index.less'
import CheckboxTree from '@/components/CheckBoxTree';
import { useTreeTabs } from '@/hooks/useTreeTabs';
import { createFormActions } from '@formily/antd';
import { usePageStatus, PageStatus } from '@/hooks/usePageStatus';
import DetailPage from '@/components/DetailPage';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ReutrnEle from '@/components/ReturnEle';
const pageTitles = [
'新增',
'编辑',
'预览'
]
const TabFormErrors = (props) => {
return (
<Badge dot={props.dot} offset={[5,-5]}>
{props.children}
</Badge>
)
}
const fetchMenuData = async () => {
const res = await PublicApi.getMemberRoleAuthTree()
return res
}
const TabsItem = Tabs.TabPane
const menuActions = createFormActions()
const treeActions = createTreeActions()
const MemberDetail: React.FC<{}> = () => {
const [form] = Form.useForm();
const value = useContext(historyContainer)
const {
treeData,
handleSelect,
nodeRecord,
isEditForm,
setIsEditForm,
getTreeMaps,
} = useTreeTabs({
fetchMenuData,
fetchItemDetailData: ({id}) => PublicApi.getMemberRoleAuthButton({
menuId: id
})
})
const actionRef = useRef<any>({})
const formInitValue = nodeRecord ? getTreeMaps(nodeRecord.key) : {}
const [errors, setErrors] = useState<boolean>(false)
const [formValue, setFormValue] = useState<any>(null)
const {
pageStatus,
id
} = usePageStatus()
// 编辑和预览模式下需回显数据
const fetchRoleMenuDetail = async (id) => {
// 10秒缓存
const res = await PublicApi.getMemberRoleGet({
memberRoleId: id
}, { useCache: true, ttl: 10 * 1000 })
return res
}
// 储存的按钮数据
const [buttonInfos, setButtonInfos] = useState<any>([])
useEffect(() => {
if (!id) return ;
fetchRoleMenuDetail(id).then(res => {
const { data } = res
setFormValue(data)
})
}, [])
useEffect(() => {
if (formInitValue) {
// 显示右侧checkbox
setButtonInfos(formInitValue.buttons || [])
// 回显右侧checkbox的值
if (actionRef.current.setSelected) {
actionRef.current.setSelected()
}
}
}, [getTreeMaps])
const handleSubmit = () => {
menuActions.submit().then(async ({values}) => {
setErrors(false)
// 如果未点击过操作权限tab, 则无法获取到actionRef实例, 需补充手动补充回显的ids, 新增的时候如果未设置按钮,则返回空数组
const buttonCheckIds = actionRef.current.selected || (formValue && formValue.ids) || []
const treeCheckIds = treeActions.getSelectKeys()
const menuIds = [...buttonCheckIds, ...treeCheckIds]
if (pageStatus === PageStatus.EDIT) {
await PublicApi.postMemberRoleUpdate({
...values,
memberRoleId: id,
menuIds
})
} else {
await PublicApi.postMemberRoleAdd({
...values,
menuIds
})
}
history.goBack(-1)
}).catch(err => {
console.log(err)
if (Array.isArray(err)) {
setErrors(true)
}
})
}
const changeTabs = (key) => {
if (key === '2' && id) {
fetchRoleMenuDetail(id).then(res => {
const { data } = res
// 获取菜单id选中的集合
const {
checkIds,
...reset
} = data
treeActions.setSelectKeys(checkIds)
})
}
}
const extraButtons = (
<Space>
<Button type='primary' disabled={pageStatus === PageStatus.PREVIEW} onClick={handleSubmit}>保存</Button>
</Space>
)
return (
<PageHeaderWrapper
onBack={() => history.goBack()}
backIcon={<ReutrnEle description="返回"/>}
className='addRepository'
title={pageTitles[pageStatus]}
extra={[
extraButtons
]}
>
<Card>
<Tabs type='card' className='black-tabs' onChange={changeTabs}>
<TabsItem tab={<TabFormErrors dot={errors}>基本信息</TabFormErrors>} key='1'>
<NiceForm
labelCol={4}
wrapperCol={12}
initialValues={formValue}
labelAlign='left'
actions={menuActions}
editable={pageStatus !== PageStatus.PREVIEW}
previewPlaceholder=' '
schema={{
type: 'object',
properties: {
roleName: {
type: 'string',
title: '角色名称',
required: true
},
remark: {
type: 'textarea',
title: '备注',
"x-rules": [
{
limitByte: true,
maxByte: 120
}
],
"x-component-props": {
rows: 4
}
},
state: {
type: 'number',
title: '状态',
"x-component": 'CustomStatus',
default: 1
}
}
}}
>
</NiceForm>
</TabsItem>
<TabsItem tab='操作权限' key='2'>
<Row justify='space-between'>
<Col span={17} className={styled['menu-role-tree']}>
<TabTree
title='菜单访问权限'
fetchData={params => fetchMenuData()}
checkable
actions={treeActions}
treeData={treeData}
handleSelect={handleSelect}
customKey='id'
disabled={pageStatus === PageStatus.PREVIEW}
/>
</Col>
<Col span={6} className={styled['menu-role-tree']}>
<CheckboxTree
actions={actionRef}
disabled={pageStatus === PageStatus.PREVIEW}
checkedNodes={buttonInfos}
title='菜单接口访问权限'
/>
</Col>
</Row>
</TabsItem>
</Tabs>
</Card>
</PageHeaderWrapper>
)
}
export default MemberDetail
import React, {useState, ReactText, useEffect} from 'react';
import { Row, Col, Tree, Form, Table, InputNumber, Popconfirm, Button, Input, Modal, Card } from 'antd';
import {
CarryOutOutlined,
FormOutlined,
PlusOutlined
} from '@ant-design/icons';
import TabTree, { useTreeActions, createTreeActions } from '@/components/TabTree';
import SchemaForm, { createFormActions, LifeCycleTypes, FormEffectHooks } from '@formily/antd';
import { menuSchema } from './schema';
import { PublicApi } from '@/services/api';
import { omit } from '@/utils';
import { useMap, useBoolean } from '@umijs/hooks';
import { useTreeTabs } from '@/hooks/useTreeTabs';
import { isObject } from '@antv/util';
// import "./index.less"
const { ON_FORM_INPUT_CHANGE } = LifeCycleTypes
const { onFieldInputChange$ } = FormEffectHooks
enum FormState {
FREE, // 空闲状态
EDIT, // 编辑状态
ADD, // 新增状态
}
const formActions = createFormActions()
const treeActions = createTreeActions()
const fetchMenuData = async (params?) => {
const res = await PublicApi.getMemberOrgTree()
return res
}
const Organ: React.FC<{}> = () => {
const {
treeStatus,
setTreeStatus,
treeData,
setIsEditForm,
nodeRecord,
setNodeRecord,
handleSelect,
getTreeMaps,
setTreeMaps,
resetMenu,
toolsRender,
handleDeleteMenu
} = useTreeTabs({
treeActions,
formActions,
deleteMenu: PublicApi.postMemberOrgDelete,
fetchMenuData: fetchMenuData,
fetchItemDetailData: PublicApi.getMemberOrgGet
})
// 当拥有节点数据并且当前状态是编辑状态时 需回显表单
const formInitValue = (nodeRecord && treeStatus === FormState.EDIT) ? getTreeMaps(nodeRecord.key) : {}
const handleSubmitAllSetting = () => {
formActions.submit()
}
// 保存设置提交
const handleSubmit = (value) => {
// 去掉模拟的key, 为true的时候是编辑
const editOrAdd = nodeRecord && treeStatus === FormState.EDIT
const params = editOrAdd ? { ...value, parentId: nodeRecord.id } : {
...value,
parentId: nodeRecord ? nodeRecord.parentId : 0,
}
const fn = editOrAdd ? PublicApi.postMemberOrgUpdate : PublicApi.postMemberOrgAdd
fn(params).then(res => {
resetMenu()
setTreeStatus(FormState.FREE)
setNodeRecord(undefined)
// 保存后要将是否填写过表单设为false
setIsEditForm(false)
})
}
return <Card className="common-wrapper white-wrapper">
<Row gutter={[36, 36]}>
<Col span={8}>
<h3 className="commonPanelTitle mb-30">选择要编辑的项目</h3>
{
treeData && treeData.length > 0
? <TabTree
fetchData = {params => fetchMenuData(params)}
treeData={treeData}
toolsRender={toolsRender}
actions={treeActions}
customKey='id'
handleSelect={(key, node) => handleSelect(key, node)}
/>
:
<Button block type='primary' onClick={() => handleSelect()}>暂无菜单, 开始新增</Button>
}
</Col>
<Col span={16}>
{treeStatus !== FormState.FREE && <>
<h3 className="commonPanelTitle mb-30">{treeStatus === FormState.ADD ? '新增' : '编辑'}</h3>
<SchemaForm
schema={menuSchema}
value={formInitValue}
actions={formActions}
effects={($) => {
$(ON_FORM_INPUT_CHANGE).subscribe(() => {
setIsEditForm(true)
})
$('onFormReset').subscribe(() => {
console.log('mount')
})
}}
onSubmit={handleSubmit}
>
</SchemaForm>
<Button onClick={handleSubmitAllSetting} type="primary" style={{ marginTop: 32, marginBottom: 16, marginRight: 24}}>
保存设置
</Button>
<Popconfirm title="确定要删除吗?" okText="是" cancelText="否" onConfirm={handleDeleteMenu}>
<Button style={{ marginTop: 32, marginBottom: 16}}>
删除菜单
</Button>
</Popconfirm>
</>
}
</Col>
</Row>
</Card>
}
export default Organ
import { ISchema } from '@formily/antd';
export const menuSchema: ISchema = {
type: 'object',
properties: {
MEGA_LAYOUT: {
type: 'object',
"x-component": 'mega-layout',
"x-component-props": {
labelAlign: 'top',
wrapperCol: 12
},
properties: {
code: {
type: 'string',
title: '组织代码',
required: true,
},
title: {
type: 'string',
title: '组织机构',
required: true
},
remark: {
type: 'textarea',
title: '描述',
"x-component-props": {
rows: 4
}
}
}
}
}
}
\ No newline at end of file
.user-system {
.ant-input-group-addon {
padding: 0;
border: none;
}
}
import React, { ReactNode, useRef } from 'react';
import { history, Link } from 'umi'
import { Button, Popconfirm, Card } from 'antd';
import {
PlusCircleOutlined,
PlayCircleOutlined,
PauseCircleOutlined,
PlusOutlined,
EyeOutlined
} from '@ant-design/icons';
import {StandardTable} from 'god'
import {ColumnType} from 'antd/lib/table/interface'
import { IButtonFilter, IFormFilter } from 'god/dist/src/standard-table/TableController';
import { PublicApi } from '@/services/api';
import EyePreview from '@/components/EyePreview';
import StatusSwitch from '@/components/StatusSwitch';
import { STATUS_ENUM } from '@/constants';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
// 模拟请求
const fetchData = async (params) => {
const { data } = await PublicApi.getMemberUserPage(params)
return data
}
const UserSystem: React.FC<{}> = () => {
const ref = useRef<any>({})
const addItem = () => {
history.push('/memberCenter/systemSetting/authConfig/userSystem/userDetail')
}
const deleteItem = (record) => {
// 删除该项
PublicApi.postMemberUserDelete({
userId: record.userId
}).then(() => {
ref.current.reload()
})
}
const updateItem = (record) => {
history.push(`/memberCenter/systemSetting/authConfig/userSystem/userDetail?id=${record.userId}&preview=0`)
}
const handleStatus = (record) => {
PublicApi.postMemberUserUpdatestatus({
userId: record.userId,
status: record.status === 1 ? 0 : 1
}).then(res => {
ref.current.reload()
})
}
const columns: ColumnType<any>[] = [
{
title: '账号',
dataIndex: 'account',
align: 'center',
key: 'account',
className: 'commonPickColor',
render: (text, record) => <EyePreview url={`/memberCenter/systemSetting/authConfig/userSystem/userDetail?id=${record.userId}&preview=1`}>{text}</EyePreview>
},
{
title: '用户姓名',
dataIndex: 'name',
align: 'center',
key: 'name',
},
{
title: '所属机构',
dataIndex: 'orgName',
align: 'center',
key: 'orgName'
},
{
title: '绑定手机号码',
align: 'center',
dataIndex: 'phone',
key: 'phone',
},
{
title: '所属角色',
align: 'center',
dataIndex: 'roleName',
key: 'roleName',
},
{
title: '最后登录时间',
align: 'center',
dataIndex: 'lastLoginTime',
key: 'lastLoginTime',
},
{
title: '状态',
align: 'center',
dataIndex: 'status',
key: 'status',
render: (text: any, record:any) => <StatusSwitch handleConfirm={() => handleStatus(record)} record={record}/>
},
{
title: '操作',
dataIndex: 'option',
align: 'center',
render: (text:any, record:any) => {
return record.status === 0 && (
<>
<Popconfirm
title="确定要执行这个操作?"
onConfirm={() => deleteItem(record)}
okText="是"
cancelText="否"
>
<Button type='link'>删除</Button>
</Popconfirm>
<Button type='link' onClick={()=>updateItem(record)}>修改</Button>
</>
)
}
}
];
return (
<PageHeaderWrapper>
<Card className="common-wrapper">
<StandardTable
columns={columns}
currentRef={ref}
fetchTableData={(params:any) => fetchData(params)}
formilyLayouts={{
justify: 'space-between'
}}
formilyChilds={{
layouts: {
order: 2
},
children: <Button style={{width: 140}} icon={<PlusOutlined/>} onClick={addItem} type='primary'>新建</Button>
}}
formilyProps={{
layouts: {
order: 3
},
ctx: {
effects: ($) => {
$('onFieldInputChange', 'status').subscribe(() => {
ref.current.reload()
})
},
schema: {
type: 'object',
properties: {
account: {
type: 'Search',
"x-component-props": {
placeholder: '请输入账号'
}
},
name: {
type: 'Search',
"x-component-props": {
placeholder: '请输入用户姓名'
}
},
status: {
type: 'string',
enum: STATUS_ENUM,
"x-component-props": {
placeholder: '请选择状态'
}
}
}
}
}
}}
/>
</Card>
</PageHeaderWrapper>
)
}
export default UserSystem
import { ISchema } from '@formily/antd';
import { PATTERN_MAPS } from '@/constants/regExp';
export const UserDetailSchema:ISchema = {
type: 'object',
properties: {
MEGA_LAYOUT: {
type: 'object',
"x-component": 'mega-layout',
"x-component-props": {
labelCol: 6,
labelAlign: 'left',
full: true,
wrapperCol: 12
},
properties: {
account: {
type: 'string',
title: '登录账号',
"x-rules": [
{
pattern: /^\w{6,20}$/,
message: '请输入由数字字母或者下划线组成的6-20位账号'
}
],
required: true
},
password: {
type: 'password',
title: '登录密码',
"x-rules": [
{
pattern: PATTERN_MAPS.password,
message: '请输入由大小写字母和数字组成的8位密码'
}
],
required: true
},
name: {
type: 'string',
title: '姓名',
maxLength: 16,
required: true
},
phoneLayout: {
type: 'object',
"x-component": 'flex-box',
title: '手机号',
"x-component-props": {
labelcol: 6,
wrappercol: 12
},
required: true,
properties: {
countryCode: {
required: true,
type: 'string',
enum: ['+86'],
"x-mega-props": {
wrapperCol: 24
},
"x-component-props": {
flexcol: {
span: 6
}
}
},
phone: {
type: 'number',
required: true,
"x-mega-props": {
wrapperCol: 24,
full: true
},
"x-rules": [
{
pattern: PATTERN_MAPS.phone,
message: '请输入正确的手机号'
}
],
"x-component-props": {
flexcol: {
flex: 1
}
}
}
}
},
idCardNo: {
type: 'string',
title: '身份证号'
},
email: {
type: 'string',
title: '邮箱',
"x-rules": [
{
pattern: PATTERN_MAPS.email,
message: '请输入正确的邮箱'
}
]
},
jobTitle: {
type: 'string',
title: '职位',
maxLength: 20,
},
orgName: {
type: 'string',
title: '所属组织机构',
required: true,
'x-component-props': {
disabled: true,
addonAfter: "{{connectCategory}}"
},
},
orgId: {
type: 'string',
visible: false
},
memberRoleIds: {
required: true,
type: 'array:string',
"x-component": 'tableTagList',
"x-component-props": {
extra: "{{addRoles}}",
callback: "{{callback}}"
},
title: '关联角色'
},
}
}
}
}
\ No newline at end of file
import React, { useRef, useState, useEffect } from 'react';
import { Button, Modal, Card } from 'antd';
import { createFormActions, FormButtonGroup } from '@formily/antd'
import {LinkOutlined } from '@ant-design/icons';
import {ColumnType} from 'antd/lib/table/interface';
import { PublicApi } from '@/services/api';
import { history } from 'umi';
import { usePageStatus, PageStatus } from '@/hooks/usePageStatus';
import DetailPage from '@/components/DetailPage';
import NiceForm from '@/components/NiceForm';
import { UserDetailSchema } from './schema';
import './index.less'
import ModalTable from '@/components/ModalTable';
import { useRowSelectionTable } from '@/hooks/useRowSelectionTable';
import { findItemAndDelete, omit, getParentTreeTitles } from '@/utils';
import TabTree, { useTreeActions, createTreeActions } from '@/components/TabTree';
import { useTreeTabs } from '@/hooks/useTreeTabs';
import { useHttpRequest } from '@/hooks/useHttpRequest';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ReutrnEle from '@/components/ReturnEle';
const titleRender = (title) => {
if (title === PageStatus.PREVIEW) return '查看用户'
if (title === PageStatus.ADD) return '新增用户'
if (title === PageStatus.EDIT) return '编辑用户'
return ''
}
const fetchOriginTreeData = async (params?) => { // 平台后台树
const res = await PublicApi.getMemberOrgTree({}, { ttl: 10, useCache: true })
return res
}
const userActions = createFormActions()
const originTreeActions = createTreeActions()
const AddUser: React.FC<{}> = () => {
const [originVisible, setOriginVisible] = useState(false)
const [roleVisible, setRoleVisible] = useState(false)
const ref = useRef<any>({})
const [formData, setFormData] = useState<any>(null)
const { id, pageStatus } = usePageStatus()
const [roleSelection, roleSelectCtl] = useRowSelectionTable()
const [originSelectNode, setOriginSelectNode] = useState<any>()
const { data, loading, err, run } = useHttpRequest(id ? PublicApi.postMemberUserUpdate : PublicApi.postMemberUserAdd)
const {
treeData: originTreeData,
} = useTreeTabs({
fetchMenuData: fetchOriginTreeData,
})
useEffect(() => {
if (id) {
PublicApi.getMemberUserGet({
userId: id
}).then(async res => {
const { data } = res
fetchOriginTreeData().then(({data: dataSource}) => {
setFormData({...data, orgName: getParentTreeTitles(dataSource, data.orgId), memberRoleIds: data.memberRoleIds.map((v,i) => {
return {
id: v,
roleName: data.memberRoleNames[i]
}
})})
})
})
}
}, [])
const onFinish = async (values:any) => {
values.memberRoleIds = values.memberRoleIds.map(v => v.id)
const omitValue = omit(values, ['orgName'])
const params = id ? {
...omitValue,
userId: Number(id)
} : omitValue
await run(params)
setTimeout(() => {
history.goBack(-1)
}, 300)
};
// 角色确认弹窗
const roleConfirm = () => {
setRoleVisible(false)
userActions.setFieldValue('memberRoleIds', roleSelectCtl.selectRow)
}
const handleSelectCancel = () => {
setRoleVisible(false)
}
const handleRoleBtn = () => {
setRoleVisible(true)
const selectRoles = userActions.getFieldValue('memberRoleIds')
roleSelectCtl.setSelectRow(selectRoles)
roleSelectCtl.setSelectedRowKeys(selectRoles.map(v => v.id))
}
// 模拟请求
const fetchUserList = async (params:any) => {
const data = await PublicApi.getMemberRolePage(params)
return data.data
}
const columns: ColumnType<any>[] = [
{
title: '角色ID',
dataIndex: 'id',
align: 'center',
key: 'id',
},
{
title: '角色名称',
dataIndex: 'roleName',
align: 'center',
key: 'roleName',
},
{
title: '描述',
align: 'center',
dataIndex: 'remark',
key: 'remark',
ellipsis: true
}
]
const handleOrigin = () => {
setOriginVisible(false)
if(originSelectNode?.id){
userActions.setFieldValue('orgName', originTreeActions.getParentPath(originSelectNode.id))
userActions.setFieldValue('orgId', originSelectNode.id)
}
}
const handleAddRole = () => {
setRoleVisible(true);
}
const handlePlateformSelect = (key, node) => {
setOriginSelectNode({id: key*1, name: node._title})
}
const openOriginTree = () => {
setOriginVisible(true)
}
const connectCategory = pageStatus !== PageStatus.PREVIEW ?
<div className='connectBtn' onClick={openOriginTree}><LinkOutlined style={{marginRight: 4}}/>关联</div>
:
''
const addRoles = pageStatus !== PageStatus.PREVIEW ? <Button block onClick={handleRoleBtn}>添加角色</Button> : ''
return (
<PageHeaderWrapper
onBack={() => history.goBack()}
backIcon={<ReutrnEle description="返回"/>}
className='addRepository'
title={titleRender(pageStatus)}
>
<Card>
<div className="common-wrapper user-system useConnectBtnWrapper">
<NiceForm
onSubmit={onFinish}
schema={UserDetailSchema}
initialValues={formData}
actions={userActions}
editable={pageStatus !== PageStatus.PREVIEW}
effects={($, {setFieldState}) => {
$('onFormInit').subscribe(() => {
if (id) {
setFieldState('password', state => {
state.visible = false
})
}
})
}}
expressionScope={{
connectCategory,
addRoles,
callback: (id) => {
roleSelectCtl.setSelectRow(findItemAndDelete(roleSelectCtl.selectRow, id))
roleSelectCtl.setSelectedRowKeys(findItemAndDelete(roleSelectCtl.selectedRowKeys, id))
}
}}
>
<FormButtonGroup offset={6}>
<Button htmlType='submit' type='primary' hidden={pageStatus === PageStatus.PREVIEW} loading={loading}>提交</Button>
<Button onClick={() => history.goBack()}>取消</Button>
</FormButtonGroup>
</NiceForm>
<ModalTable
modalTitle='选择角色'
visible={roleVisible}
confirm={roleConfirm}
cancel={handleSelectCancel}
columns={columns}
rowSelection={roleSelection}
currentRef={ref}
fetchTableData={(params:any) => fetchUserList(params)}
tableProps={{ rowKey: 'id' }}
formilyProps={{
layouts: {
order: 3
},
ctx: {
schema: {
type: 'object',
properties: {
roleName: {
type: 'Search',
"x-component-props": {
placeholder: '请输入角色名称'
}
}
}
}
}
}
}
/>
<Modal
title="选择组织机构"
visible={originVisible}
onOk={handleOrigin}
onCancel={() => setOriginVisible(false)}
okText="确认"
cancelText="取消"
getContainer='#root'
// destroyOnClose={true}
>
<TabTree
fetchData = {params => fetchOriginTreeData(params)}
treeData={originTreeData}
handleSelect={(key, node) => handlePlateformSelect(key, node)}
actions={originTreeActions}
customKey="id"
/>
</Modal>
</div>
</Card>
</PageHeaderWrapper>
);
};
export default AddUser
import React from 'react'
import { ArrowLeftOutlined } from '@ant-design/icons'
import { history } from 'umi'
import { Modal } from 'antd'
import styles from './index.less'
const ToolBar: React.FC = () => {
const handleGoBack = () => {
Modal.confirm({
content: "是否确认离开模板装修页面?",
okText: "确认",
cancelText: "取消",
onOk: () => {
history.goBack()
}
})
}
return (
<div className={styles.toolbar}>
<div className={styles.toolbar_back_btn} onClick={() => history.goBack()}>
<div className={styles.toolbar_back_btn} onClick={() => handleGoBack()}>
<ArrowLeftOutlined />
</div>
<div className={styles.toolbar_title}>
......
......@@ -174,7 +174,7 @@ const ShopEdit: React.FC<ShopEditPropsType> = (props) => {
...InformationConfig,
...FooterConfig
}
console.log(config)
setComponentConfigs(config)
setLoading(false)
}
......
......@@ -188,48 +188,4 @@
}
}
}
}
.no_result {
padding-top: 120px;
padding-left: 287px;
&_tip {
font-size: 14px;
color: #D32F2F;
font-weight: 500;
display: flex;
&_search {
color: #333333;
}
&_img {
width: 30px;
height: 30px;
overflow: hidden;
margin-right: 16px;
&>img {
width: 30px;
height: 30px;
}
}
}
&_suggest {
margin-top: 12px;
font-size: 12px;
color: #999999;
padding-left: 46px;
&_list {
padding: 0;
margin: 0;
&>li {
list-style: none;
}
}
}
}
\ No newline at end of file
......@@ -4,34 +4,26 @@ import Filter, { FilterType } from '../components/Filter'
import cx from 'classnames'
import { Pagination } from 'antd'
import CommodityList from './list'
import NoResult from './noResult'
import SearchNoResult from '../components/SearchNoResult'
import isEmpty from 'lodash/isEmpty'
import { Spin } from 'antd'
import { PublicApi } from '@/services/api'
import { GetSearchCommodityShopGetCommodityListResponseDetail } from '@/services'
import { LAYOUT_TYPE } from '@/constants'
import { GetSearchShopEnterpriseGetCommodityListResponseDetail } from '@/services'
import bannerImg from '@/assets/imgs/banner_2.png'
import arrowDownIcon from '@/assets/imgs/arrow_down.png'
import arrowDownActiveIcon from '@/assets/imgs/arrow_down_active.png'
import styles from './index.less'
interface filterValueType {
key: string;
key: string | string[];
title: string;
type: FILTER_TYPE;
}
enum FILTER_TYPE {
category = 'category', // 分类
brand = 'brand', // 品牌
style = 'style', // 风格
price = 'price', // 价格
area = 'area', // 适用地区
commodityType = 'commodityType', // 商品类型
type: FilterType;
}
interface CommodityPropsType {
location: any,
layoutType: 'mall' | 'shop' | 'channel'
layoutType: LAYOUT_TYPE.mall | LAYOUT_TYPE.shop | LAYOUT_TYPE.channel
}
interface filterQuery {
......@@ -52,11 +44,11 @@ interface filterQuery {
const Commodity: React.FC<CommodityPropsType> = (props) => {
const { layoutType } = props
const { query: { search = "" } } = props.location
const { query: { categoryId, categoryName } } = props.location
const [loading, setLoading] = useState<boolean>(true)
const [showType, setShowType] = useState<number>(1) // 展示方式:1:矩阵排列; 2:列表排列
const [filterList, setFilterList] = useState([])
const [commodityList, setCommodityList] = useState<GetSearchCommodityShopGetCommodityListResponseDetail[]>([])
const [commodityList, setCommodityList] = useState<GetSearchShopEnterpriseGetCommodityListResponseDetail[]>([])
const [current, setCurrent] = useState<number>(1)
const [pageSize, setPageSize] = useState<number>(20)
const [totalCount, setTotalCount] = useState<number>(0)
......@@ -65,24 +57,31 @@ const Commodity: React.FC<CommodityPropsType> = (props) => {
useEffect(() => {
fetchCommodityList()
}, [filterParam, current])
useEffect(() => {
if (categoryId && categoryName) {
handleFilter({
key: [categoryId],
title: decodeURIComponent(atob(categoryName)),
type: FilterType.category,
})
}
}, [filterParam, current, search])
}, [props.location.query])
const fetchCommodityList = () => {
let param: filterQuery = {
current,
pageSize
}
if (!!search) {
param.name = search
}
if (!isEmpty(filterParam)) {
param = Object.assign(param, filterParam)
}
setLoading(true)
//@ts-ignore
PublicApi.getSearchCommodityShopGetCommodityList(param).then(res => {
PublicApi.getSearchShopEnterpriseGetCommodityList(param).then(res => {
setLoading(false)
if (res.code === 1000) {
setCommodityList(res.data.data)
......@@ -105,14 +104,29 @@ const Commodity: React.FC<CommodityPropsType> = (props) => {
} else {
tempFilterList = [...tempFilterList, filterValue]
}
console.log(tempFilterList, "tempFilterList")
setFilterList(tempFilterList)
handleFilterChange(tempFilterList)
}
const handleFilterChange = (newFilterList: any) => {
let tempFilterParam: any = {}
for (let filterItem of newFilterList) {
switch (filterItem.type) {
case FilterType.category:
tempFilterParam.categoryId = filterItem.key[0]
break
default:
break
}
}
setFilterParam(tempFilterParam)
}
const handleDeleteFilterItem = (key: string) => {
let tempFilterList = [...filterList]
tempFilterList = tempFilterList.filter(item => String(item.key) !== String(key))
setFilterList(tempFilterList)
handleFilterChange(tempFilterList)
}
/**
......@@ -120,6 +134,7 @@ const Commodity: React.FC<CommodityPropsType> = (props) => {
*/
const handleResetFilter = () => {
setFilterList([])
handleFilterChange([])
}
const handlePageChange = (page) => {
......@@ -130,7 +145,7 @@ const Commodity: React.FC<CommodityPropsType> = (props) => {
<div className={styles.commodity}>
<div className={styles.mall_container}>
<div className={styles.commodity_container}>
<Filter onFilter={handleFilter} filterConfig={filterConfig} />
<Filter onFilter={handleFilter} filterConfig={filterConfig} layoutType={layoutType} />
<div className={styles.commodity_main}>
<div className={styles.banner}>
<img src={bannerImg} />
......@@ -185,7 +200,7 @@ const Commodity: React.FC<CommodityPropsType> = (props) => {
</div>
</div>
{
(commodityList.length === 0 || !commodityList) ? <NoResult search={search} /> : (
(commodityList.length === 0 || !commodityList) ? <SearchNoResult search="" /> : (
<>
<Spin spinning={loading}>
<CommodityList showType={showType} commodityList={commodityList} layoutType={layoutType} />
......
import React, { useState, useEffect } from 'react'
import { CaretUpOutlined, CaretDownOutlined, UnorderedListOutlined, AppstoreOutlined, CloseOutlined } from '@ant-design/icons'
import Filter, { FilterType } from '../components/Filter'
import cx from 'classnames'
import { history } from 'umi'
import { Pagination } from 'antd'
import CommodityList from './list'
import SearchNoResult from '../components/SearchNoResult'
import isEmpty from 'lodash/isEmpty'
import { Spin } from 'antd'
import { PublicApi } from '@/services/api'
import { GetSearchShopEnterpriseGetCommodityListResponseDetail } from '@/services'
import arrowDownIcon from '@/assets/imgs/arrow_down.png'
import arrowDownActiveIcon from '@/assets/imgs/arrow_down_active.png'
import styles from './index.less'
interface filterValueType {
key: string;
title: string;
type: FilterType;
}
interface CommodityPropsType {
location: any,
layoutType: 'mall' | 'shop' | 'channel'
}
interface filterQuery {
current: number;
pageSize: number;
name?: string;
categoryId?: number;
customerCategoryId?: number;
provinceCode?: number;
cityCode?: number;
brandId?: number;
customerAttributeList?: any;
Min?: number;
Max?: number;
priceType?: number;
}
const CommoditySearch: React.FC<CommodityPropsType> = (props) => {
const { layoutType } = props
const { query: { search = "" } } = props.location
const [loading, setLoading] = useState<boolean>(true)
const [showType, setShowType] = useState<number>(1) // 展示方式:1:矩阵排列; 2:列表排列
const [filterList, setFilterList] = useState([])
const [commodityList, setCommodityList] = useState<GetSearchShopEnterpriseGetCommodityListResponseDetail[]>([])
const [current, setCurrent] = useState<number>(1)
const [pageSize, setPageSize] = useState<number>(20)
const [totalCount, setTotalCount] = useState<number>(0)
const [filterParam, setFilterParam] = useState<filterQuery | {}>({})
const filterConfig = [FilterType.commonlyUsed, FilterType.category, FilterType.style, FilterType.brand, FilterType.price, FilterType.useArea, FilterType.commodityType]
useEffect(() => {
fetchCommodityList()
}, [filterParam, current, search])
const fetchCommodityList = () => {
let param: filterQuery = {
current,
pageSize
}
if (!!search) {
param.name = search
}
if (!isEmpty(filterParam)) {
param = Object.assign(param, filterParam)
}
setLoading(true)
//@ts-ignore
PublicApi.getSearchShopEnterpriseGetCommodityList(param).then(res => {
setLoading(false)
if (res.code === 1000) {
setCommodityList(res.data.data)
setTotalCount(res.data.totalCount)
}
})
}
const handleFilter = (filterValue: filterValueType) => {
let filteState = filterList.some(item => item.type === filterValue.type)
let tempFilterList = [...filterList]
if (filteState) {
tempFilterList = tempFilterList.map(item => {
if (item.type === filterValue.type) {
return filterValue
}
return item
})
} else {
tempFilterList = [...tempFilterList, filterValue]
}
setFilterList(tempFilterList)
handleFilterChange(tempFilterList)
}
const handleFilterChange = (newFilterList: any) => {
let tempFilterParam: any = {}
for (let filterItem of newFilterList) {
switch (filterItem.type) {
case FilterType.category:
tempFilterParam.categoryId = filterItem.key[0]
break
default:
break
}
}
setFilterParam(tempFilterParam)
}
const handleDeleteFilterItem = (key: string) => {
let tempFilterList = [...filterList]
tempFilterList = tempFilterList.filter(item => String(item.key) !== String(key))
setFilterList(tempFilterList)
handleFilterChange(tempFilterList)
}
/**
* 重置筛选
*/
const handleResetFilter = () => {
if (search) {
setFilterList([])
history.push('/commodity/search')
} else {
setFilterList([])
handleFilterChange([])
}
}
const handlePageChange = (page) => {
setCurrent(page)
}
/**
* 清除搜索
*/
const handleClearSearch = () => {
history.push('/commodity/search')
}
return (
<div className={styles.commodity}>
<div className={styles.mall_container}>
<div className={styles.commodity_container}>
<Filter onFilter={handleFilter} filterConfig={filterConfig} />
<div className={styles.commodity_main}>
<div className={styles.tool_bar}>
<div className={styles.tool_bar_left}>
<div className={styles.tool_bar_filter_item}>
<span>销量</span>
<i className={styles.icon}>
<img src={arrowDownIcon} />
</i>
</div>
<div className={styles.tool_bar_filter_item}>
<span>信用</span>
<i className={styles.icon}>
<img src={arrowDownIcon} />
</i>
</div>
<div className={styles.tool_bar_filter_item}>
<span>价格</span>
<div className={styles.price_filter_box}>
<CaretUpOutlined className={styles.icon} />
<CaretDownOutlined className={styles.icon} />
</div>
</div>
</div>
<div className={styles.tool_bar_right}>
<div className={styles.count}>
<span></span>
<label>{totalCount}</label>
<span>个商品</span>
</div>
<AppstoreOutlined className={cx(styles.icon, showType === 1 ? styles.active : '')} onClick={() => setShowType(1)} />
<UnorderedListOutlined className={cx(styles.icon, showType === 2 ? styles.active : '')} onClick={() => setShowType(2)} />
</div>
</div>
<div className={styles.filter_bar}>
<div className={styles.filter_bar_left}>
<div className={styles.filter_bar_left_text1}>当前搜索:</div>
{/* <div className={styles.filter_bar_left_split}></div>
<div className={styles.filter_bar_left_text} onClick={handleResetFilter}>重置</div> */}
</div>
<div className={styles.filter_bar_list}>
{
search && (
<div className={styles.filter_bar_list_item}>
<span className={styles.filter_bar_list_item_text}>{search}</span>
<CloseOutlined className={styles.filter_bar_list_item_icon} onClick={() => handleClearSearch()} />
</div>
)
}
{
filterList.map(item => (
<div className={styles.filter_bar_list_item} key={item.key}>
<span className={styles.filter_bar_list_item_text}>{item.title}</span>
<CloseOutlined className={styles.filter_bar_list_item_icon} onClick={() => handleDeleteFilterItem(item.key)} />
</div>
))
}
{
(search || filterList.length > 0) && <div className={styles.filter_bar_reset} onClick={handleResetFilter}>重置</div>
}
</div>
</div>
{
(commodityList.length === 0 || !commodityList) ? <SearchNoResult search={search} /> : (
<>
<Spin spinning={loading}>
<CommodityList showType={showType} commodityList={commodityList} layoutType={layoutType} />
</Spin>
<div className={styles.pagination_wrap}>
<Pagination showQuickJumper showSizeChanger={false} onChange={handlePageChange} current={current} pageSize={pageSize} total={totalCount} />
</div>
</>
)
}
</div>
</div>
</div>
</div>
)
}
export default CommoditySearch
import React, { useEffect } from 'react'
import { MenuOutlined, RightOutlined } from '@ant-design/icons'
import { inject, observer } from 'mobx-react'
import './index.less'
import { Link } from 'umi'
import { isEmpty } from '@formily/antd/esm/shared'
import { LAYOUT_TYPE } from '@/constants'
import isEmpty from 'lodash/isEmpty'
import './index.less'
interface CategoryPropsType {
CategoryStore?: any;
layoutType?: LAYOUT_TYPE.mall | LAYOUT_TYPE.shop | LAYOUT_TYPE.channel
}
const Category: React.FC<CategoryPropsType> = (props) => {
......@@ -14,7 +16,7 @@ const Category: React.FC<CategoryPropsType> = (props) => {
useEffect(() => {
if (isEmpty(categoryList)) {
fetchCategoryList()
// fetchCategoryList()
}
}, [])
......
import React, { useEffect, useState } from 'react'
import { Tree } from 'antd'
import FilterBox from '../FilterBox'
import { LAYOUT_TYPE } from '@/constants'
import { inject, observer } from 'mobx-react'
import isEmpty from 'lodash/isEmpty'
import styles from './index.less'
interface CategoryPropsType {
onFilter?: Function;
CategoryStore?: any;
layoutType?: LAYOUT_TYPE.mall | LAYOUT_TYPE.shop | LAYOUT_TYPE.channel
}
const Category: React.FC<CategoryPropsType> = (props) => {
const { onFilter, CategoryStore: { categoryList } } = props
const { onFilter, CategoryStore: { enterpriseCategoryList, storeCategoryList, fetchEnterpriseCategoryList, fetchStoreCategoryList }, layoutType = LAYOUT_TYPE.mall } = props
const [expandedKeys, setExpandedKeys] = useState<string[]>([])
const [treeData, setTreeData] = useState<any>([])
useEffect(() => {
if (categoryList && categoryList.length > 0) {
initTreeData(categoryList)
switch (layoutType) {
case LAYOUT_TYPE.mall:
fetchEnterpriseCategoryList()
break
case LAYOUT_TYPE.shop:
break
case LAYOUT_TYPE.channel:
fetchStoreCategoryList()
break
}
}, [categoryList])
}, [layoutType])
useEffect(() => {
if (!isEmpty(enterpriseCategoryList) || !isEmpty(storeCategoryList)) {
switch (layoutType) {
case LAYOUT_TYPE.mall:
initTreeData(enterpriseCategoryList)
break
case LAYOUT_TYPE.shop:
break
case LAYOUT_TYPE.channel:
initTreeData(storeCategoryList)
break
}
}
}, [enterpriseCategoryList, storeCategoryList])
const initTreeData = (list: any) => {
let initExpandKeys = []
let result: any = list.map(item => {
initExpandKeys.push(item.id)
return {
title: item.title,
key: item.id,
children: item.children.map(secondCategoryItem => {
initExpandKeys.push(secondCategoryItem.id)
return {
title: secondCategoryItem.title,
key: secondCategoryItem.id,
......@@ -39,6 +69,8 @@ const Category: React.FC<CategoryPropsType> = (props) => {
})
}
})
// setExpandedKeys(initExpandKeys)
setTreeData(result)
}
......@@ -51,15 +83,20 @@ const Category: React.FC<CategoryPropsType> = (props) => {
})
}
const handleExpand = (expandedKeys) => {
setExpandedKeys(expandedKeys)
}
return (
<FilterBox
title="分类"
>
<div className={styles.filter_category}>
<Tree
defaultExpandedKeys={[2, "4"]}
expandedKeys={expandedKeys}
onSelect={handleSelect}
treeData={treeData}
onExpand={handleExpand}
/>
</div>
</FilterBox>
......
......@@ -10,11 +10,13 @@ import CommodityType from './components/CommodityType'
import ActiveStores from './components/ActiveStores'
import NewJoin from './components/NewJoin'
import Points from './components/Points'
import { LAYOUT_TYPE } from '@/constants'
import './index.less'
interface FilterPropsType {
onFilter?: Function;
filterConfig?: string[]
filterConfig?: string[],
layoutType?: LAYOUT_TYPE.mall | LAYOUT_TYPE.shop | LAYOUT_TYPE.channel
}
export enum FilterType {
......@@ -57,11 +59,15 @@ export enum FilterType {
/**
* 所需积分
*/
points = 'points'
points = 'points',
/**
* 商品名称
*/
name = 'name'
}
const Filter: React.FC<FilterPropsType> = (props) => {
const { onFilter, filterConfig = [] } = props
const { onFilter, filterConfig = [], layoutType = LAYOUT_TYPE.mall } = props
const handleFilter = (filterValue: any) => {
onFilter(filterValue)
......@@ -74,7 +80,7 @@ const Filter: React.FC<FilterPropsType> = (props) => {
case FilterType.commonlyUsed:
return <CommonlyUsed key={type} />
case FilterType.category:
return <Category onFilter={handleFilter} key={type} />
return <Category onFilter={handleFilter} key={type} layoutType={layoutType} />
case FilterType.style:
return <Style onFilter={handleFilter} key={type} />
case FilterType.brand:
......
......@@ -5,7 +5,7 @@ import { Link, history } from 'umi'
import { FileTextOutlined } from '@ant-design/icons'
import logo from '@/theme/imgs/logo_w.png'
import isEmpty from 'lodash/isEmpty'
import './index.less'
import styles from './index.less'
interface HeaderPropsType {
......@@ -14,13 +14,14 @@ interface HeaderPropsType {
const Header: React.FC<HeaderPropsType> = (props) => {
const [searchType, setSearchType] = useState<number>(1) // 1:商品; 2:店铺
const [searchValue, setSearchValue] = useState<string>("")
const { search } = history.location.query
useEffect(() => {
const { search } = history.location.query
if (!!search) {
setSearchValue(search)
} else {
setSearchValue("")
}
}, [])
}, [search])
const handleChangeSearchType = (type: number) => {
if (searchType !== type) {
......@@ -30,34 +31,40 @@ const Header: React.FC<HeaderPropsType> = (props) => {
const handleSearchCommodity = () => {
if (!isEmpty(searchValue)) {
history.push(`/commodity?search=${encodeURIComponent(searchValue)}`)
if (searchType === 1) {
history.push(`/commodity/search?search=${encodeURIComponent(searchValue)}`)
} else {
history.push(`/shops?search=${encodeURIComponent(searchValue)}`)
}
} else {
history.push(`/commodity`)
if (searchType === 1) {
history.push(`/commodity`)
} else {
history.push(`/shops`)
}
}
}
return (
<div className="header">
<div className="header_container">
<div className="logo">
<Link to="/">
<img src={logo} />
</Link>
<div className={styles.header}>
<div className={styles.header_container}>
<div className={styles.logo}>
<img src={logo} />
</div>
<div className="mall_search">
<div className="mall_search_tags">
<div className={cx("mall_search_tags_item", searchType === 1 ? 'active' : '')} onClick={() => handleChangeSearchType(1)}>商品</div>
<div className={cx("mall_search_tags_item", searchType === 2 ? 'active' : '')} onClick={() => handleChangeSearchType(2)}>店铺</div>
<div className={styles.mall_search}>
<div className={styles.mall_search_tags}>
<div className={cx(styles.mall_search_tags_item, searchType === 1 ? styles.active : '')} onClick={() => handleChangeSearchType(1)}>商品</div>
<div className={cx(styles.mall_search_tags_item, searchType === 2 ? styles.active : '')} onClick={() => handleChangeSearchType(2)}>店铺</div>
</div>
<div className="mall_search_box">
<Input className="mall_search_input" value={searchValue} placeholder="请输入关键词" onChange={e => setSearchValue(e.target.value)} onPressEnter={() => handleSearchCommodity()} />
<div className="search_btn" onClick={() => handleSearchCommodity()}>搜索</div>
<div className={styles.mall_search_box}>
<Input className={styles.mall_search_input} value={searchValue} placeholder="请输入关键词" onChange={e => setSearchValue(e.target.value)} onPressEnter={() => handleSearchCommodity()} />
<div className={styles.search_btn} onClick={() => handleSearchCommodity()}>搜索</div>
</div>
</div>
<div className="shopping_cart mall" onClick={() => history.push('/purchaseOrder')}>
<div className="badge">0</div>
<FileTextOutlined className="card_icon" />
<div className={cx(styles.shopping_cart, styles.mall)} onClick={() => history.push('/purchaseOrder')}>
<div className={styles.badge}>0</div>
<FileTextOutlined className={styles.card_icon} />
<span>进货单</span>
</div>
</div>
......
.no_result {
padding-top: 120px;
padding-left: 287px;
&_tip {
font-size: 14px;
color: #D32F2F;
font-weight: 500;
display: flex;
&_search {
color: #333333;
}
&_img {
width: 30px;
height: 30px;
overflow: hidden;
margin-right: 16px;
&>img {
width: 30px;
height: 30px;
}
}
}
&_suggest {
margin-top: 12px;
font-size: 12px;
color: #999999;
padding-left: 46px;
&_list {
padding: 0;
margin: 0;
&>li {
list-style: none;
}
}
}
}
\ No newline at end of file
......@@ -5,16 +5,26 @@ interface NoResultPropsType {
search?: string
}
const NoResult: React.FC<NoResultPropsType> = (props) => {
const SearchNoResult: React.FC<NoResultPropsType> = (props) => {
const { search } = props
return (
<div className={styles.no_result}>
<div className={styles.no_result_tip}>
<div className={styles.no_result_tip_img}></div>
<div className={styles.no_result_tip_text}>
抱歉,没有找到与“
<span className={styles.no_result_tip_search}>{search}</span>
”相关的商品
{
search ? (
<>
抱歉,没有找到与“
<span className={styles.no_result_tip_search}>{search}</span>
”相关的商品
</>
) : (
<>
抱歉,没有找到相关的商品
</>
)
}
</div>
</div>
<div className={styles.no_result_suggest}>
......@@ -29,4 +39,4 @@ const NoResult: React.FC<NoResultPropsType> = (props) => {
)
}
export default NoResult
export default SearchNoResult
import React from 'react'
import { EnvironmentOutlined } from '@ant-design/icons'
import './index.less'
import { inject, observer } from 'mobx-react'
import cx from 'classnames'
import styles from './index.less'
interface TopBarPropsType {
langComponent?: React.ReactNode
langComponent?: React.ReactNode;
UserStore?: any;
}
const TopBar: React.FC<TopBarPropsType> = (props) => {
const { langComponent } = props
const { userInfo } = props.UserStore
return (
<div className="topbar">
<div className="topbar_container">
<ul className="topbar_menu left">
<li className="topbar_menu_item pad_left_0">
<div className={styles.topbar}>
<div className={styles.topbar_container}>
<ul className={cx(styles.topbar_menu, styles.left)}>
<li className={cx(styles.topbar_menu_item, styles.pad_left_0)}>
<span>XX商城欢迎你!</span>
</li>
<li className="topbar_menu_item">
<EnvironmentOutlined className="icon" />
<li className={styles.topbar_menu_item}>
<EnvironmentOutlined className={styles.icon} />
<span>广州</span>
</li>
</ul>
<ul className="topbar_menu right">
<li className="topbar_menu_item">
<a href="/user/login">请登录</a>
</li>
<li className="topbar_menu_item">
<a href="/user/register">注册</a>
</li>
<li className="topbar_menu_item">
<ul className={cx(styles.topbar_menu, styles.right)}>
{
userInfo ? (
<li className={styles.topbar_menu_item}>
<span>{userInfo?.name}</span>
</li>
) : (
<>
<li className={styles.topbar_menu_item}>
<a href="/user/login">请登录</a>
</li>
<li className={styles.topbar_menu_item}>
<a href="/user/register">注册</a>
</li>
</>
)
}
<li className={styles.topbar_menu_item}>
<a href="/memberCenter/home">会员中心</a>
</li>
<li className="topbar_menu_item">我的消息</li>
<li className="topbar_menu_item">客户服务</li>
<li className={styles.topbar_menu_item}>我的消息</li>
<li className={styles.topbar_menu_item}>
<a href="http://p.qiao.baidu.com/cps/chat?siteId=11220066&userId=24534830&siteToken=49f62d4365e46c0f2c81538e4d168ab9" target="_blank">客户服务</a>
</li>
{
langComponent && (<li className="topbar_menu_item nopad">{langComponent}</li>)
langComponent && (<li className={cx(styles.topbar_menu_item, styles.nopad)}>{langComponent}</li>)
}
</ul>
</div>
......@@ -42,4 +57,4 @@ const TopBar: React.FC<TopBarPropsType> = (props) => {
)
}
export default TopBar
\ No newline at end of file
export default inject('UserStore')(observer(TopBar))
\ No newline at end of file
......@@ -92,6 +92,7 @@ const MallIndex: React.FC<MallIndexPropsType> = (props) => {
let categoryDetail: any = await fetchCategoryById(item.id)
result.push(
<FloorLine
linkUrl={`/commodity?categoryId=${item.id}&categoryName=${btoa(encodeURIComponent(item.name))}`}
anchor={`floorline_${item.id}`}
key={item.id}
title={item.name}
......
......@@ -13,6 +13,12 @@
.information_focus {
display: flex;
.information_focus_carousel {
width: 598px;
height: 444px;
}
&_left {
position: relative;
margin-right: 4px;
......@@ -22,7 +28,9 @@
&_img {
width: 598px;
height: 444px;
.common_background()
cursor: pointer;
transition: all 1s;
.common_background();
}
}
}
......
import React, { Fragment } from 'react'
import cx from 'classnames'
import { Input, Pagination } from 'antd'
import { Carousel, Pagination } from 'antd'
import { Link } from 'umi'
import { ClockCircleOutlined, EyeOutlined } from '@ant-design/icons'
import informationImg1 from '@/assets/imgs/information_1.png'
......@@ -16,92 +16,92 @@ interface InformationPropsType {
const MockList = [
{
id: new Date().getTime(),
id: new Date().getTime() + 1,
title: '今日热点',
list: [
{
id: new Date().getTime()
id: new Date().getTime() + 11
},
{
id: new Date().getTime()
id: new Date().getTime() + 12
},
{
id: new Date().getTime()
id: new Date().getTime() + 13
},
{
id: new Date().getTime()
id: new Date().getTime() + 14
},
]
},
{
id: new Date().getTime(),
id: new Date().getTime() + 2,
title: '今日热点',
list: [
{
id: new Date().getTime()
id: new Date().getTime() + 21
},
{
id: new Date().getTime()
id: new Date().getTime() + 22
},
{
id: new Date().getTime()
id: new Date().getTime() + 23
},
{
id: new Date().getTime()
id: new Date().getTime() + 24
},
]
},
{
id: new Date().getTime(),
id: new Date().getTime() + 3,
title: '行业头条',
list: [
{
id: new Date().getTime()
id: new Date().getTime() + 31
},
{
id: new Date().getTime()
id: new Date().getTime() + 32
},
{
id: new Date().getTime()
id: new Date().getTime() + 33
},
{
id: new Date().getTime()
id: new Date().getTime() + 34
},
]
},
{
id: new Date().getTime(),
id: new Date().getTime() + 4,
title: '专题报道',
list: [
{
id: new Date().getTime()
id: new Date().getTime() + 41
},
{
id: new Date().getTime()
id: new Date().getTime() + 42
},
{
id: new Date().getTime()
id: new Date().getTime() + 43
},
{
id: new Date().getTime()
id: new Date().getTime() + 44
},
]
},
{
id: new Date().getTime(),
id: new Date().getTime() + 5,
title: '政策法规',
list: [
{
id: new Date().getTime()
id: new Date().getTime() + 51
},
{
id: new Date().getTime()
id: new Date().getTime() + 52
},
{
id: new Date().getTime()
id: new Date().getTime() + 53
},
{
id: new Date().getTime()
id: new Date().getTime() + 54
},
]
},
......@@ -115,9 +115,18 @@ const Information: React.FC<InformationPropsType> = (props) => {
<BreadCrumbs />
<div className={styles.information_focus}>
<div className={styles.information_focus_left}>
<div className={styles.information_focus_imgbox_main}>
<div className={styles.information_focus_imgbox_main_img} style={{ backgroundImage: `url(${informationImg1})` }} />
</div>
<Carousel className={styles.information_focus_carousel} pauseOnDotsHover>
<div className={styles.information_focus_carousel_item}>
<div className={styles.information_focus_imgbox_main}>
<div className={styles.information_focus_imgbox_main_img} style={{ backgroundImage: `url(${informationImg1})` }} />
</div>
</div>
<div className={styles.information_focus_carousel_item}>
<div className={styles.information_focus_imgbox_main}>
<div className={styles.information_focus_imgbox_main_img} style={{ backgroundImage: `url(${informationImg2})` }} />
</div>
</div>
</Carousel>
</div>
<div className={styles.information_focus_right}>
<div className={styles.information_focus_imgbox_sub_1}>
......
import React, { useEffect } from 'react'
import React, { useEffect, useState } from 'react'
import {
BasicLayoutProps as ProLayoutProps,
getMenuData
} from '@ant-design/pro-layout';
} from '@ant-design/pro-layout'
import SelectLang from '@/layouts/components/SelectLang'
import { useIntl } from 'umi';
import { useIntl } from 'umi'
import { inject, observer } from 'mobx-react'
import TopBar from '../components/TopBar'
import ChannelHeader from '../components/ChannelHeader'
import MainNav from '../components/MainNav'
......@@ -18,10 +19,12 @@ interface LXMallLayoutPropsType {
routes: Array<any>
};
location: any;
SiteStore?: any;
}
const LXChannelLayout: React.FC<LXMallLayoutPropsType> = (props) => {
const { children, location } = props
const [templateName] = useState<string>('theme-channel-science')
const getMenuRouter = (routes: any, pathname: any) => {
let list = routes.filter((item: any) => pathname.indexOf(item.key) > -1)
......@@ -33,8 +36,8 @@ const LXChannelLayout: React.FC<LXMallLayoutPropsType> = (props) => {
const menuRouter = getMenuRouter(menuData, location.pathname)
useEffect(() => {
let body = document.getElementsByTagName('body')[0];
body.className = `theme-channel-science`;
let body = document.getElementsByTagName('body')[0]
body.className = templateName
}, [])
return (
......@@ -65,4 +68,4 @@ const LXChannelLayout: React.FC<LXMallLayoutPropsType> = (props) => {
)
}
export default LXChannelLayout
export default inject("SiteStore")(observer(LXChannelLayout))
import React, { useEffect } from 'react'
import React, { useEffect, useState } from 'react'
import {
BasicLayoutProps as ProLayoutProps,
getMenuData
} from '@ant-design/pro-layout';
} from '@ant-design/pro-layout'
import SelectLang from '@/layouts/components/SelectLang'
import { useIntl } from 'umi';
import { useIntl } from 'umi'
import { inject, observer } from 'mobx-react'
import TopBar from '../components/TopBar'
import Header from '../components/Header'
import MainNav from '../components/MainNav'
......@@ -18,10 +19,13 @@ interface LXMallLayoutPropsType {
routes: Array<any>
};
location: any;
SiteStore?: any;
}
const LXMallLayout: React.FC<LXMallLayoutPropsType> = (props) => {
const { children, location } = props
const [templateName] = useState<string>('theme-mall-science')
const { siteId } = props.SiteStore
const getMenuRouter = (routes: any, pathname: any) => {
let list = routes.filter((item: any) => pathname.indexOf(item.key) > -1)
......@@ -31,10 +35,10 @@ const LXMallLayout: React.FC<LXMallLayoutPropsType> = (props) => {
const basicInfo = getMenuData(props.route.routes, { locale: true }, formatMessage)
const menuData = basicInfo.menuData ? basicInfo.menuData.filter(item => !item.redirect) : []
const menuRouter = getMenuRouter(menuData, location.pathname)
useEffect(() => {
console.log(`tempalteName=theme-mall-science`)
let body = document.getElementsByTagName('body')[0];
body.className = `theme-mall-science`;
body.className = templateName;
}, [])
return (
......@@ -65,4 +69,4 @@ const LXMallLayout: React.FC<LXMallLayoutPropsType> = (props) => {
)
}
export default LXMallLayout
export default inject("SiteStore")(observer(LXMallLayout))
import React, { useEffect } from 'react'
import React, { useEffect, useState } from 'react'
import {
BasicLayoutProps as ProLayoutProps,
getMenuData
} from '@ant-design/pro-layout';
import { useIntl } from 'umi';
import TopBar from '../components/TopBar'
import Advert from '../components/Advert'
import { inject, observer } from 'mobx-react'
import ShopHeader from '../components/ShopHeader'
import MainNav from '../components/MainNav'
import SideNav from '../components/SideNav'
......@@ -18,16 +18,17 @@ interface LXMallLayoutPropsType {
routes: Array<any>
};
location: any;
SiteStore?: any;
}
const LXMallLayout: React.FC<LXMallLayoutPropsType> = (props) => {
const LXShopLayout: React.FC<LXMallLayoutPropsType> = (props) => {
const { children, location } = props
const [templateName] = useState<string>('theme-shop-science')
useEffect(() => {
console.log('当前使用店铺模板')
let body = document.getElementsByTagName('body')[0];
body.className = `${body.className} theme-shop-science`;
body.className = templateName;
}, [])
const { formatMessage } = useIntl();
......@@ -56,4 +57,5 @@ const LXMallLayout: React.FC<LXMallLayoutPropsType> = (props) => {
)
}
export default LXMallLayout
export default inject("SiteStore")(observer(LXShopLayout))
......@@ -88,23 +88,6 @@ const ShopList: React.FC<ShopListPropsType> = (props) => {
</div>
</div>
</div>
{/* <div className="filter_bar">
<div className="filter_bar_left">
<div className="filter_bar_left_text">保存为常用筛选</div>
<div className="filter_bar_left_split"></div>
<div className="filter_bar_left_text" onClick={handleResetFilter}>重置</div>
</div>
<div className="filter_bar_list">
{
filterList.map(item => (
<div className="filter_bar_list_item" key={item.key}>
<span className="filter_bar_list_item_text">{item.title}</span>
<CloseOutlined className="filter_bar_list_item_icon" onClick={() => handleDeleteFilterItem(item.key)} />
</div>
))
}
</div>
</div> */}
{
!!search ? <NoResult search={search} /> : (
<>
......
......@@ -11,10 +11,15 @@ import {
PlusOutlined,
LinkOutlined,
} from '@ant-design/icons'
import { Button } from 'antd'
import { Button, message } from 'antd'
import NiceForm from '@/components/NiceForm'
import ModalTable from '@/components/ModalTable'
import { GlobalConfig } from '@/global/config'
import { FORM_FILTER_PATH } from '@/formSchema/const'
import SearchSelect from '@/components/NiceForm/components/SearchSelect'
import Search from '@/components/NiceForm/components/Search'
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch'
import Submit from '@/components/NiceForm/components/Submit'
export interface PositionSettingProps {
addSchemaAction: ISchemaFormActions,
......@@ -27,10 +32,7 @@ const fetchMemberList = async (params) => {
return res.data
}
const fetchProductList = async (params) => {
const res = await PublicApi.getProductCommodityGetCommodityDetailList(params)
return res.data
}
const PositionSetting:React.FC<PositionSettingProps> = (props) => {
const { addSchemaAction, schema, formSubmit } = props
......@@ -47,6 +49,14 @@ const PositionSetting:React.FC<PositionSettingProps> = (props) => {
const initValue = useInitValue(PublicApi.getWarehouseFreightSpaceDetails)
useUnitPreview(initValue, addSchemaAction)
const fetchProductList = async (params) => {
const res = await PublicApi.getProductCommodityCommonGetCommodityDetailList({
...params,
shopType: addSchemaAction.getFieldValue('shopType')
})
return res.data
}
// 会员选择后的表格
const handleDeleteTable = (id) => {
const value = addSchemaAction.getFieldValue('applyMember')
......@@ -62,6 +72,11 @@ const PositionSetting:React.FC<PositionSettingProps> = (props) => {
// 弹出商品选择
const handleAddProductBtn = () => {
const shopType = addSchemaAction.getFieldValue('shopType')
if (!shopType) {
message.error('请先选择商城类型')
return false
}
productRowCtl.setSelectedRowKeys([])
productRowCtl.setSelectRow([])
setVisibleChannelRroduct(true)
......@@ -211,9 +226,59 @@ const PositionSetting:React.FC<PositionSettingProps> = (props) => {
properties: {
name: {
type: 'string',
"x-component": 'Search',
"x-component-props": {
placeholder: '请输入商品名称'
'x-component': 'ModalSearch',
'x-component-props': {
placeholder: '请输入商品名称/ID',
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: {
categoryId: {
type: 'string',
"x-component": 'SearchSelect',
"x-component-props": {
placeholder: '请选择品类',
className: 'fixed-ant-selected-down', // 该类强制将显示的下拉框出现在select下, 只有这里出现问题, ??
fetchSearch: PublicApi.getProductSelectGetSelectCategory,
style: {
width: 160
}
}
},
brandId: {
type: 'string',
"x-component": 'SearchSelect',
"x-component-props": {
placeholder: '请选择品牌',
fetchSearch: PublicApi.getProductSelectGetSelectBrand,
style: {
width: 160
}
}
},
submit: {
"x-component": 'Submit',
"x-mega-props": {
span: 1
},
"x-component-props": {
children: '查询'
}
}
}
}
}
......@@ -272,7 +337,18 @@ const PositionSetting:React.FC<PositionSettingProps> = (props) => {
fetchTableData={params => fetchProductList(params)}
formilyProps={
{
ctx: { schema: formProduct }
ctx: {
schema: formProduct,
components: { ModalSearch: Search, SearchSelect, Submit } ,
effects: ($, actions) => {
useStateFilterSearchLinkageEffect(
$,
actions,
'name',
FORM_FILTER_PATH,
);
}
}
}
}
tableProps={{
......
......@@ -8,11 +8,12 @@ import { SHOP_TYPES } from '@/constants';
// 将获取的商城转化为可用类型
const getShopTypeMap = (() => {
return GlobalConfig.web.shopInfo.reduce((prev, next) => {
if (!prev.find(v => v.value === next.id)) {
prev.push({
label: next.name,
value: next.id
})
const shopTypeEnumValue = SHOP_TYPES.find(v => v.value === next.type)
if (!shopTypeEnumValue) {
return prev
}
if (!prev.find(v => v.value === shopTypeEnumValue.value)) {
prev.push(shopTypeEnumValue)
}
return prev
}, [])
......
......@@ -18,11 +18,7 @@ const LoginWrap: React.FC = () => {
PublicApi.postMemberLogin(value).then(res => {
const { data, code } = res
if (code === 1000) {
setAuth({
memberId: data.memberId,
userId: data.userId,
token: data.token
})
setAuth(data)
setRouters(data.urls)
// 此处需使用href跳转, 否则无法触发app.ts中的路由初始化校验
window.location.href = '/memberCenter/home'
......
/*
* @Author: LeeJiancong
* @Date: 2020-07-22 09:54:50
* @LastEditors: LeeJiancong
* @LastEditTime: 2020-07-22 10:23:38
*/
import * as Api from './index'
import request from '@/utils/request'
import * as LogisticsApi from './LogisticsApi'
import * as PassApi from './PassApi'
import * as WarehouseApi from './WarehouseApi'
import * as MemberApi from './MemberApi'
import * as ProductApi from './ProductApi'
import * as TemplateApi from './TemplateApi'
import * as PayApi from './PayApi'
import * as SearchApi from './SearchApi'
import * as OrderApi from './OrderApi'
/**
* 可在这里写入自定义的接口
......@@ -15,4 +16,14 @@ export const CustomApi = {
}
// 公共的接口,从yapi拉下
export const PublicApi = Api
export const PublicApi = {
...LogisticsApi,
...PassApi,
...WarehouseApi,
...MemberApi,
...ProductApi,
...TemplateApi,
...PayApi,
...SearchApi,
...OrderApi,
}
import { action, computed, observable, runInAction } from 'mobx'
import { GlobalConfig } from '@/global/config'
import { PublicApi } from '@/services/api'
import { GetSearchCommodityGetCategoryTreeResponse } from '@/services'
const defaultCategory = [
{
......@@ -47,16 +45,45 @@ const defaultCategory = [
]
class CategoryStore {
@observable public categoryList: GetSearchCommodityGetCategoryTreeResponse = []; // 品类列表
@observable public categoryList: any = []; // 品类列表
@observable public enterpriseCategoryList: any = []
@observable public storeCategoryList: any = []
/**
* 企业商城商品分类列表
*/
@action.bound
public async fetchCategoryList() {
let res = await PublicApi.getSearchCommodityGetCategoryTree()
let res = await PublicApi.getSearchShopEnterpriseGetCategoryTree()
runInAction(() => {
this.enterpriseCategoryList = res.data || []
})
}
/**
* 企业商城商品分类列表
*/
@action.bound
public async fetchEnterpriseCategoryList() {
let res = await PublicApi.getSearchShopEnterpriseGetCategoryTree()
runInAction(() => {
this.enterpriseCategoryList = res.data || []
})
}
/**
* 店铺商城商品分类列表
*/
@action.bound
public async fetchStoreCategoryList() {
let res = await PublicApi.getSearchShopStoreGetCustomerCategoryTree()
runInAction(() => {
this.categoryList = res.data || []
this.storeCategoryList = res.data || []
})
}
}
export default CategoryStore
import { action, computed, observable, runInAction } from 'mobx'
import { FILTE_RTYPE } from '@/constants'
interface filterValueType {
key: string | string[];
title: string;
type: FILTE_RTYPE;
}
class FilterStore {
/**
* 筛选条件列表
*/
@observable filterList = []
@action
public handleFilter = (filterValue: filterValueType) => {
}
}
export default FilterStore
\ No newline at end of file
......@@ -27,12 +27,12 @@ import { IProductModule } from '@/module/productModule'
*/
export interface IStore {
userStore: ILoginModule;
UserStore: ILoginModule;
ProductStore: IProductModule;
}
export const store = {
userStore: new UserStore,
UserStore: new UserStore,
ThemeStore: new ThemeStore,
ProductStore: new ProductStroe,
SiteStore: new SiteStore,
......
import { action, computed, observable, runInAction } from 'mobx'
import { ILoginModule } from '@/module/userModule';
import { ILoginModule } from '@/module/userModule'
import { getAuth } from '@/utils/auth'
import { localStorage } from '@/utils/storage'
// import { userDetailGet } from '@/services/user'
......@@ -9,7 +10,7 @@ class LoginStore implements ILoginModule {
@observable public username: string = 'admin';
@observable public password: string = "123456";
@observable public res: object = {};
@observable public userInfo = {} //userInfo ? JSON.parse(userInfo) : {}
@observable public userInfo = getAuth()
// 可以改变对应的状态值
// @todo 接入更新用户信息接口
......
......@@ -47,9 +47,10 @@
margin: 0;
&_item {
position: relative;
padding-left: 20px;
padding: 12px 19px 8px 20px;
padding-top: 12px;
height: 56px;
list-style: none;
cursor: pointer;
......
......@@ -3,6 +3,7 @@ import { isDev } from '@/constants'
export interface AuthInfo {
userId: number,
memberId: number,
name: string,
token: string
}
......
/*
* @Author: LeeJiancong
* @Date: 2020-07-22 09:54:50
* @LastEditors: LeeJiancong
* @Copyright: 1549414730@qq.com
* @LastEditTime: 2020-08-24 16:09:13
*/
import { Config } from 'god-yapi2ts'
const tokens = [
'b063a0a29fb1a570d9f00eaabbdd8ccfe8e6e10e24739441990cc1098e79b601', // 业务中台管理平台
'7c8f235d95f6224ceb97c4d832b09658f9a75fb8721a95699b230af0733d7fa4', // 仓库服务
'8d14d945507d1f8cd89afe139ca6d111bbad25f702fafe0aec59d3c9cd2e0ffe', // 物流服务
'3a46198c5b97ac7147e5b07ad2dff5ac5c93c1afed47e1911961db87149e6ebf', // 商户会员管理服务
'efe99e20ed1375dc0db3e809e4fc7692f42ecebaf60cd77e65c50ed65d6ba6c4', // 商品服务
'7ec923520215c7e2f771867cb4d29cafbf823daf0fb2d3d9fa70b57a523c8bfb', // 店铺服务
'c789e0e56ee8a8cc2fbd85f930eb2928c58fc1014583c6643acf29cff954da49', // 支付服务
'ca19f532efba91f7773cbfbd526b798c6ac83df670071e97d72c50dca1d53a48', // 搜索服务
'5de0aaeaac12c8d911d86dada6cd128993e34cd6e13135fa79246aa5979a2bcd' //订单服务
const tokenList = [
{ name: 'Pass', token: 'b063a0a29fb1a570d9f00eaabbdd8ccfe8e6e10e24739441990cc1098e79b601' }, // 业务中台管理平台
{ name: 'Warehouse', token: '7c8f235d95f6224ceb97c4d832b09658f9a75fb8721a95699b230af0733d7fa4' }, // 仓库服务
{ name: 'Logistics', token: '8d14d945507d1f8cd89afe139ca6d111bbad25f702fafe0aec59d3c9cd2e0ffe' }, // 物流服务
{ name: 'Member', token: '3a46198c5b97ac7147e5b07ad2dff5ac5c93c1afed47e1911961db87149e6ebf' }, // 商户会员管理服务
{ name: 'Product', token: 'efe99e20ed1375dc0db3e809e4fc7692f42ecebaf60cd77e65c50ed65d6ba6c4' }, // 商品服务
{ name: 'Template', token: '7ec923520215c7e2f771867cb4d29cafbf823daf0fb2d3d9fa70b57a523c8bfb' }, // 店铺模板服务
{ name: 'Pay', token: 'c789e0e56ee8a8cc2fbd85f930eb2928c58fc1014583c6643acf29cff954da49' }, // 支付服务
{ name: 'Search', token: 'ca19f532efba91f7773cbfbd526b798c6ac83df670071e97d72c50dca1d53a48' }, // 搜索服务
{ name: 'Order', token: '5de0aaeaac12c8d911d86dada6cd128993e34cd6e13135fa79246aa5979a2bcd' }, //订单服务
]
const genMap = (tokens) => {
return tokens.map(v => {
return {
token: v,
const getConfigMap = (tokens) => tokens.map(v => ({
serverUrl: 'http://10.0.0.25:4000/',
typesOnly: false,
reactHooks: {
enabled: false,
},
outputFilePath: `src/services/${v.name}Api.ts`,
requestFunctionFilePath: 'request.ts',
dataKey: 'data',
projects: [
{
token: v.token,
categories: [
{
id: 0,
......@@ -33,21 +31,7 @@ const genMap = (tokens) => {
},
]
}
})
}
const config: Config = [
{
serverUrl: 'http://10.0.0.25:4000/',
typesOnly: false,
reactHooks: {
enabled: false,
},
outputFilePath: 'src/services/index.ts',
requestFunctionFilePath: 'request.ts',
dataKey: 'data',
projects: genMap(tokens),
},
]
],
}))
export default config
\ No newline at end of file
export default getConfigMap(tokenList)
\ No newline at end of file
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