Commit 336c1150 authored by Bill's avatar Bill

feat: 对接整改,考评,投诉与建议

parent 438a5749
......@@ -469,6 +469,23 @@ const MemberRoute: RouterChild = {
hideInMenu: true,
noMargin: true,
},
{
path: '/memberCenter/memberAbility/profile/suggestion',
name: '投诉建议管理',
component: "@/pages/member/memberQuery/suggest"
},
{
path: '/memberCenter/memberAbility/profile/suggestion/add',
name: '新增会员反馈',
component: "@/pages/member/memberQuery/suggest/add"
},
{
path: '/memberCenter/memberAbility/profile/suggestion/detail',
name: '会员反馈详情',
component: "@/pages/member/memberQuery/suggest/detail",
hideInMenu: true,
noMargin: true,
},
],
},
......@@ -673,15 +690,23 @@ const MemberRoute: RouterChild = {
component: '@/pages/member/complaintsAndSuggests/index',
},
{
path: '/memberCenter/memberAbility/complainAndSuggest/add',
path: '/memberCenter/memberAbility/complainAndSuggest/list/add',
name: '新增投诉建议',
component: '@/pages/member/complaintsAndSuggests/add',
hideInMenu: true
},
{
path: '/memberCenter/memberAbility/complainAndSuggest/detail',
path: '/memberCenter/memberAbility/complainAndSuggest/list/detail',
name: '处理投诉建议',
component: '@/pages/member/complaintsAndSuggests/detail',
noMargin: true,
hideInMenu: true
},
{
path: '/memberCenter/memberAbility/complainAndSuggest/list/edit',
name: '修改投诉建议',
component: '@/pages/member/complaintsAndSuggests/add',
hideInMenu: true
}
]
},
......
......@@ -83,10 +83,10 @@ registerVirtualBox('LeftRightLayout', (_props) => {
const { children, props } = _props;
const leftItems = children.filter(
child => child.props.schema['x-component-props'] && child.props.schema['x-component-props'].position === 'left'
);
);
const rightItems = children.filter(
child => child.props.schema['x-component-props'] && child.props.schema['x-component-props'].position === 'right'
);
);
const wrapProps = props['x-component-props'] ? props['x-component-props'].wrapProps : {};
const leftProps = props['x-component-props'] ? props['x-component-props'].leftProps : {};
const rightProps = props['x-component-props'] ? props['x-component-props'].rightProps : {};
......@@ -123,4 +123,4 @@ registerVirtualBox('CardBox', (_props) => {
</div>
</CardBox>
)
})
\ No newline at end of file
})
......@@ -24,7 +24,7 @@ const EXCHANGE_APPLICATION = `/memberCenter/afterService/exchangeApplication/exc
const EXCHANGE_HANDLE = '/memberCenter/afterService/exchangeManage/exchangeQuery';
const AfterSoldCenter: React.FC<Iprops> = () => {
const { loading, responseData, ref, isError } = useViewRequest<GetReportMemberHomeGetAfterSaleTallyResponse, any>(PublicApi.getReportMemberHomeGetAfterSaleTally, {})
const { loading, responseData, ref, filterEmptyList, isError } = useViewRequest<GetReportMemberHomeGetAfterSaleTallyResponse, any>(PublicApi.getReportMemberHomeGetAfterSaleTally, {})
const auth = getAuth();
const roleType = auth.memberRoleType;
return (
......@@ -44,31 +44,7 @@ const AfterSoldCenter: React.FC<Iprops> = () => {
loading={loading}
isError={isError}
>
{
responseData && Object.keys(responseData).map((record: keyof GetReportMemberHomeGetAfterSaleTallyResponse) => {
return (
<div className={styles.wrapRow} key={record}>
<span className={styles.rowTitle}>{KEY_TITLE[record]}</span>
<div className={styles.rowValues}>
{
responseData[record].map((item, key) => {
return (
<div className={styles.wrapCol} key={key}>
<div className={styles.colTitle}>{item.name}</div>
{
item.link
? <Link to={item.link} className={styles.colValue}>{item.count}</Link>
: <div className={styles.colValue}>{item.count}</div>
}
</div>
)
})
}
</div>
</div>
)
})
}
<Layout.StaticsDataList title={KEY_TITLE} dataSource={filterEmptyList}></Layout.StaticsDataList>
</Layout>
)
}
......
import React, { Fragment, useMemo } from 'react';
import styles from './center.less'
import create_shop from '@/assets/imgs/create_shop.png';
import { BellOutlined, RightOutlined } from '@ant-design/icons'
import { Button } from 'antd';
import { Link } from 'umi';
import Authorize from '../Authorize';
import { PublicApi } from '@/services/api';
import { getAuth } from '@/utils/auth';
import useViewRequest from '../../hooks/useViewRequest';
import { GetTemplateWebMemberShopWebFindCurrMemberShopResponse } from '@/services/TemplateV2Api';
import Layout from './layout';
interface Iprops {}
const SHOP_CENTER = '/memberCenter/shopAbility/template'
const CREATE_SHOP = '/memberCenter/shopAbility/infoManage';
const ChannelMallCenter: React.FC<Iprops> = () => {
const { loading, responseData, isError, ref } = useViewRequest<GetTemplateWebMemberShopWebFindCurrMemberShopResponse, any>(PublicApi.getTemplateWebMemberShopWebFindCurrMemberShop, {})
const userAuth = getAuth();
const tagList = useMemo(() => [
{
icon: create_shop,
url: SHOP_CENTER,
title: "创建渠道商城"
},
{
icon: create_shop,
url: SHOP_CENTER,
title: "渠道商城模板"
},
{
icon: create_shop,
url: SHOP_CENTER,
title: "渠道商城装修"
},
{
icon: create_shop,
url: SHOP_CENTER,
title: "业务员绑定渠道"
}
], [])
return (
<Layout
viewRef={ref}
title="渠道商城中心"
tips="您还没有创建渠道商城,请先创建渠道商城"
extra={
<Authorize url={SHOP_CENTER}>
<div>
<Link to={SHOP_CENTER}>进入店铺中心</Link>
</div>
</Authorize>
}
loading={loading}
>
<Fragment>
{
responseData && responseData.id && (
<div className={styles.ding_tips}>
<div>
<BellOutlined />
<span style={{marginLeft: '12px'}}>您还没有申请电子签章,请先申请电子签章</span>
</div>
<Button size="small" type="primary">点击申请</Button>
</div>
)
}
<Layout.Tag tagList={tagList}></Layout.Tag>
</Fragment>
</Layout>
)
}
export default ChannelMallCenter
import React, { ReactNode, useEffect, useState } from 'react';
import styles from './Container.less';
import {useInViewport} from '@umijs/hooks';
import { Skeleton } from 'antd';
interface Iprops {
/**
* 标题
*/
title: string,
/**
* 标题下面的一行字
*/
tips: string,
/**
* 主要用在header 右边连接ReactNode
*/
extra?: ReactNode,
children: (<T>(params: {loading: boolean, requestData: T}) => React.ReactElement),
/**
* request
*/
request?: () => Promise<any>
};
const AbilityContainer: React.FC<Iprops> = (props) => {
const { title = '', tips = '' } = props;
const [inViewPort, ref] = useInViewport<HTMLDivElement>();
const [loading, setLoading] = useState<boolean>(false);
const [flag, setFlag] = useState<boolean>(false);
const [data, setData] = useState<any>({});
useEffect(() => {
if(inViewPort && !flag) {
setLoading(true);
props?.request()
.then(({code, data}) => {
if(code === 1000) {
setData(data)
}
setFlag(true)
}).catch(error => {
setFlag(false)
}).finally(() => {
setLoading(false)
})
}
}, [inViewPort])
return (
<div className={styles.container} ref={ref}>
<div className={styles.header}>
<div className={styles.left}>
<div className={styles.title}>{title}</div>
<div className={styles.tips}>{tips}</div>
</div>
<div className={styles.extra}>
{props.extra}
</div>
</div>
<div className={styles.body}>
{
!!props.request
? <>
{props.children({
loading: loading,
requestData: data,
})}
</>
: props.children
}
</div>
</div>
)
}
export default AbilityContainer;
import React, { Fragment, useMemo } from 'react';
import styles from './center.less';
import { PublicApi } from '@/services/api'
import { Link } from 'umi'
import Layout from './layout';
import Authorize from '../Authorize';
import useViewRequest from '../../hooks/useViewRequest';
import { GetReportMemberHomeGetOrderTallyResponse, GetReportMemberHomeGetPurchaseTallyResponse } from '@/services/ReportV2Api';
import { BellOutlined } from '@ant-design/icons';
import {Button} from 'antd';
interface Iprops {};
const { StaticsDataList } = Layout
const url = '/memberCenter/handling/assign/query';
const KEY_TITLE = {
saleOrderList: '销售订单',
purchaseOrderList: '采购订单',
}
const PurchaseCenter: React.FC<Iprops> = () => {
const { loading, responseData, filterEmptyList, isError, ref } = useViewRequest<GetReportMemberHomeGetOrderTallyResponse, any>(PublicApi.getReportMemberHomeGetOrderTally, {})
return (
<Layout
viewRef={ref}
title="订单中心"
tips="提供订单管理、签订电子合同等功能"
extra={
<Authorize
url={url}
>
<div>
<Link to={url}>进入订单中心</Link>
</div>
</Authorize>
}
loading={loading}
isError={isError}
>
<StaticsDataList title={KEY_TITLE} dataSource={filterEmptyList as GetReportMemberHomeGetPurchaseTallyResponse} />
</Layout>
)
}
export default PurchaseCenter
......@@ -16,7 +16,7 @@ const KEY_TITLE = {
}
const ProcessCenter: React.FC<Iprops> = () => {
const { loading, responseData, isError, ref } = useViewRequest<GetReportMemberHomeGetEnhanceTallyResponse, any>(PublicApi.getReportMemberHomeGetEnhanceTally, {})
const { loading, responseData, isError,filterEmptyList, ref } = useViewRequest<GetReportMemberHomeGetEnhanceTallyResponse, any>(PublicApi.getReportMemberHomeGetEnhanceTally, {})
return (
<Layout
viewRef={ref}
......@@ -34,31 +34,7 @@ const ProcessCenter: React.FC<Iprops> = () => {
loading={loading}
isError={isError}
>
{
responseData && Object.keys(responseData).map((record: keyof GetReportMemberHomeGetEnhanceTallyResponse) => {
return (
<div className={styles.wrapRow} key={record}>
<span className={styles.rowTitle}>{KEY_TITLE[record]}</span>
<div className={styles.rowValues}>
{
responseData[record]?.map((_item, key) => {
return (
<div className={styles.wrapCol} key={key}>
<div className={styles.colTitle}>{_item.name}</div>
{
_item.link
? <Link to={_item.link} className={styles.colValue}>{_item.count}</Link>
: <div className={styles.colValue}>{_item.count}</div>
}
</div>
)
})
}
</div>
</div>
)
})
}
<Layout.StaticsDataList dataSource={filterEmptyList} title={KEY_TITLE}></Layout.StaticsDataList>
</Layout>
)
}
......
import React, { Fragment } from 'react';
import React, { Fragment, useMemo } from 'react';
import styles from './center.less';
import { PublicApi } from '@/services/api'
import { Link } from 'umi'
......@@ -10,7 +10,8 @@ import { BellOutlined } from '@ant-design/icons';
import {Button} from 'antd';
interface Iprops {};
const url = '/memberCenter/handling/assign/query'
const { StaticsDataList } = Layout
const url = '/memberCenter/handling/assign/query';
const KEY_TITLE = {
purchaseInquiryList: '采购询价 ',
......@@ -25,7 +26,8 @@ const KEY_TITLE = {
}
const PurchaseCenter: React.FC<Iprops> = () => {
const { loading, responseData, isError, ref } = useViewRequest<GetReportMemberHomeGetPurchaseTallyResponse, any>(PublicApi.getReportMemberHomeGetPurchaseTally, {})
const { loading, responseData, filterEmptyList, isError, ref } = useViewRequest<GetReportMemberHomeGetPurchaseTallyResponse, any>(PublicApi.getReportMemberHomeGetPurchaseTally, {})
return (
<Layout
viewRef={ref}
......@@ -55,31 +57,7 @@ const PurchaseCenter: React.FC<Iprops> = () => {
</div>
)
}
{
responseData && Object.keys(responseData).map((record: keyof GetReportMemberHomeGetPurchaseTallyResponse) => {
return (
<div className={styles.wrapRow} key={record}>
<span className={styles.rowTitle}>{KEY_TITLE?.[record]}</span>
<div className={styles.rowValues}>
{
responseData[record]?.map((_item, key) => {
return (
<div className={styles.wrapCol} key={key}>
<div className={styles.colTitle}>{_item.name}</div>
{
_item.link
? <Link to={_item.link} className={styles.colValue}>{_item.count}</Link>
: <div className={styles.colValue}>{_item.count}</div>
}
</div>
)
})
}
</div>
</div>
)
})
}
<StaticsDataList title={KEY_TITLE} dataSource={filterEmptyList as GetReportMemberHomeGetPurchaseTallyResponse} />
</Fragment>
</Layout>
......
......@@ -20,7 +20,7 @@ const KEY_TITLE = {
receivableList: '应收账款结算'
}
const SettlementCenter: React.FC<Iprops> = () => {
const { loading, responseData, isError, ref } = useViewRequest<GetReportMemberHomeGetSettleAccountTallyResponse, any>(PublicApi.getReportMemberHomeGetSettleAccountTally, {})
const { loading, responseData, filterEmptyList, isError, ref } = useViewRequest<GetReportMemberHomeGetSettleAccountTallyResponse, any>(PublicApi.getReportMemberHomeGetSettleAccountTally, {})
const userAuth = getAuth();
const urls = userAuth.urls;
......@@ -78,7 +78,7 @@ const SettlementCenter: React.FC<Iprops> = () => {
})
}
</div>
{
{/* {
responseData && Object.keys(responseData).map((record: keyof GetReportMemberHomeGetSettleAccountTallyResponse) => {
return (
<div className={styles.wrapRow} key={record}>
......@@ -102,7 +102,9 @@ const SettlementCenter: React.FC<Iprops> = () => {
</div>
)
})
}
} */}
<Layout.StaticsDataList title={KEY_TITLE} dataSource={filterEmptyList as GetReportMemberHomeGetSettleAccountTallyResponse} />
</>
</Layout>
)
......
......@@ -25,7 +25,7 @@ const PURCHASE_URL = '/memberCenter/tranactionAbility/purchaseOrder/orderList';
const SALE_URL = '/memberCenter/tranactionAbility/saleOrder/orderList';
const TradeCenter: React.FC<Iprops> = () => {
const { loading, responseData, isError, ref } = useViewRequest<GetReportMemberHomeGetTradeTallyResponse, any>(PublicApi.getReportMemberHomeGetTradeTally, {})
const { loading, responseData, isError, filterEmptyList, ref } = useViewRequest<GetReportMemberHomeGetTradeTallyResponse, any>(PublicApi.getReportMemberHomeGetTradeTally, {})
const auth = getAuth();
const roleType = auth.memberRoleType;
......@@ -46,31 +46,7 @@ const TradeCenter: React.FC<Iprops> = () => {
}
loading={loading}
>
{
responseData && Object.keys(responseData).map((record: keyof GetReportMemberHomeGetTradeTallyResponse) => {
return (
<div className={styles.wrapRow} key={record}>
<span className={styles.rowTitle}>{KEY_TITLE[record]}</span>
<div className={styles.rowValues}>
{
responseData[record]?.map((item, key) => {
return (
<div className={styles.wrapCol} key={key}>
<div className={styles.colTitle}>{item.name}</div>
{
item.link
? <Link to={item.link} className={styles.colValue}>{item.count}</Link>
: <div className={styles.colValue}>{item.count}</div>
}
</div>
)
})
}
</div>
</div>
)
})
}
<Layout.StaticsDataList title={KEY_TITLE} dataSource={filterEmptyList}></Layout.StaticsDataList>
</Layout>
)
}
......
......@@ -7,6 +7,9 @@ import LogisticsCenter from './LogisticsCenter'
import ProcessCenter from './ProcessCenter';
import FundCenter from './FundCenter';
import Contract from './Contract';
import PurchaseCenter from './PurchaseCenter';
import OrderCenter from './OrderCenter';
import ChannelMallCenter from './ChannelMallCenter';
export {
TradeCenter,
......@@ -17,5 +20,8 @@ export {
LogisticsCenter,
ProcessCenter,
FundCenter,
Contract
Contract,
PurchaseCenter,
OrderCenter,
ChannelMallCenter
}
import React, { ReactNode, useEffect, useState } from 'react';
import { Button } from 'antd';
import styles from './Container.less';
import {useInViewport} from '@umijs/hooks';
import { Skeleton } from 'antd';
import useViewRequest from '../../hooks/useViewRequest';
import { Link } from 'umi';
import layoutStyles from './center.less';
import Authorize from '../Authorize';
import { RightOutlined } from '@ant-design/icons';
interface LayoutType {
StaticsDataList: typeof StaticsDataList,
Tag: typeof Tag,
}
interface Iprops {
viewRef: any,
......@@ -36,7 +43,7 @@ interface Iprops {
// onRefresh: () => void
};
const Layout: React.FC<Iprops> = (props) => {
const Layout: LayoutType & React.FC<Iprops> = (props) => {
const { title, tips, extra, children, loading, isError, customizeErrorRender, viewRef } = props;
const renderStatus = () => {
......@@ -74,6 +81,85 @@ const Layout: React.FC<Iprops> = (props) => {
)
}
interface IDataListProps<T> {
dataSource: T,
title: {
[key in keyof T]: string
}
}
const StaticsDataList = (props) => {
const { dataSource, title } = props;
return (
<>
{
dataSource && Object.keys(dataSource).map((record) => {
return (
<div className={layoutStyles.wrapRow} key={record}>
<span className={layoutStyles.rowTitle}>{title?.[record]}</span>
<div className={layoutStyles.rowValues}>
{
dataSource[record]?.map((_item, key) => {
return (
<div className={layoutStyles.wrapCol} key={key}>
<div className={layoutStyles.colTitle}>{_item.name}</div>
{
_item.link
? <Link to={_item.link} className={layoutStyles.colValue}>{_item.count}</Link>
: <div className={layoutStyles.colValue}>{_item.count}</div>
}
</div>
)
})
}
</div>
</div>
)
})
}
</>
)
}
interface TagProps {
tagList: {
icon: any,
url: string,
title: string,
}[]
}
const Tags = (props: TagProps) => {
const { tagList } = props;
return (
<div className={layoutStyles.centerRow}>
{
tagList.map((_item) => {
return (
<Authorize url={_item.url} canView={true} key={_item.title}>
<Link to={_item.url} className={layoutStyles.tagsItem}>
<div className={layoutStyles.hoverLink}>
<img src={_item.icon} className={layoutStyles.icon}/>
<div className={layoutStyles.text}>{_item.title}</div>
<div className={layoutStyles.hoverIconConatiner}>
<div className={layoutStyles.hoverIcon}>
<RightOutlined style={{ color: '#fff', fontSize: '8px'}} />
</div>
</div>
</div>
</Link>
</Authorize>
)
})
}
</div>
)
}
Layout.Tag = Tags;
Layout.StaticsDataList = StaticsDataList
Layout.defaultProps = {
title: '',
tips: '',
......
import { useInViewport } from '@umijs/hooks';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
interface ResponseDataType {
code: number,
......@@ -49,7 +49,27 @@ function useViewRequest<T, P>(fn: (postData: P) => Promise<ResponseDataType & {
fetchData(refreshParams)
}
return { loading, isError, ref, hasRequest, refresh, responseData }
const filterEmptyList = useMemo(() => {
if(!responseData) {
return responseData
}
if(Array.isArray(responseData)) {
return responseData
}
const result = {};
if (!responseData) {
return result
}
Object.keys(responseData).forEach((_row) => {
if (responseData[_row] && responseData[_row].length !== 0) {
result[_row] = responseData[_row];
}
})
console.log("result", result);
return result;
}, [responseData])
return { loading, isError, ref, hasRequest, refresh, responseData, filterEmptyList }
}
export default useViewRequest
......@@ -8,7 +8,7 @@ import LatestAnnounce from './components/LatestAnnounces';
import RecentVisit from './components/RecentVisit';
import AnyQuestion from './components/AnyQuestion';
import AdvertisementContainer from './components/AdvertisementSpace';
import { TradeCenter, FundCenter, ShopCenter, ProductCenter, SettlementCenter, AfterSoldCenter, LogisticsCenter, ProcessCenter, Contract } from './components/Centers';
import { TradeCenter, FundCenter, ShopCenter, ProductCenter, SettlementCenter, AfterSoldCenter, LogisticsCenter, ProcessCenter, Contract, PurchaseCenter, OrderCenter, ChannelMallCenter } from './components/Centers';
import styles from './index.less'
import { CompassFilled } from '@ant-design/icons';
import { PublicApi } from '@/services/api';
......@@ -24,6 +24,9 @@ const ComponentSelect = {
"物流中心": LogisticsCenter,
"加工中心": ProcessCenter,
"合同中心": Contract,
"采购中心": PurchaseCenter,
"订单中心": OrderCenter,
'渠道商城中心': ChannelMallCenter,
"数据中心": '',
"风控中心": ''
}
......@@ -38,13 +41,6 @@ const Home: React.FC<{}> = () => {
try {
const { data, code, message } = await PublicApi.getReportMemberHomeGetDataLayout();
if (code === 1000) {
// const tempData = {
// code: 11,
// name: '合同中心',
// sort: 11,
// isShow: 1,
// }
// const newList = data.concat(tempData);
setLayout(data)
}
} finally {
......@@ -64,7 +60,7 @@ const Home: React.FC<{}> = () => {
}
return (
<PageHeaderWrapper>
<PageHeaderWrapper title="首页">
<div className={styles.userGuaid} style={{display: visible ? 'block': 'none'}}>
<UseGuaid/>
</div>
......@@ -73,7 +69,7 @@ const Home: React.FC<{}> = () => {
<UserCenter />
<div className={styles.otherCenters}>
{
layout.map((item) => {
(layout as any[]).map((item) => {
const RenderComponent = ComponentSelect[item.name]
return (
<div
......@@ -81,10 +77,7 @@ const Home: React.FC<{}> = () => {
style={{order: item.sort, display: (item.isShow ? 'block' : 'none')}}
key={item.code}
>
{
RenderComponent &&
<RenderComponent />
}
{RenderComponent && <RenderComponent />}
</div>
)
})
......
import React, { useEffect, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Card, Button } from 'antd';
import { history } from 'umi';
import { history, Prompt } from 'umi';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { LinkOutlined, SaveOutlined } from '@ant-design/icons';
import SchemaForm, { createFormActions } from '@formily/antd';
import SchemaForm, { createFormActions, FormEffectHooks } from '@formily/antd';
import ReutrnEle from '@/components/ReturnEle';
import NiceForm from '@/components/NiceForm';
import { complaintAddSchema } from './common/schema/add';
import FormilyUploadFiles from '@/components/UploadFiles/FormilyUploadFiles'
import FormilyRangeTime from '@/components/RangeTime/FormilyRangeTime';
import FormilyCountryPhone from './components/CountryPhone/FormilyCountryPhone';
import FormilyCustomizeRadioButton from './components/CustomizeRadioButton/FormilyCustomizeRadioButton';
import useModal from '../memberEvaluate/hooks/useModal';
import TableModal from '../components/TableModal';
import { memberColumns } from '../memberInspection/common/columns/memberColumns';
import { userColumns } from '../memberInspection/common/columns/userColumns';
import { memberSchema, userSchema } from '../memberInspection/common/schema/add'
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch';
import { PublicApi } from '@/services/api';
import { GetMemberInspectMembersRequest, GetMemberInspectMembersResponse, GetMemberInspectUsersRequest, GetMemberInspectUsersResponse } from '@/services/MemberV2Api';
import { usePageStatus } from '@/hooks/usePageStatus';
import useInitialValue from '../common/hooks/useInitialValue';
type SubmitType = {
/**
* 业务类型1-投诉2-建议
*/
type: 1 | 2,
/**
* 事件分类1-关于产品2-关于订单3-关于配送4-关于售后5-关于服务6-其他
*/
classify: number,
subject: string,
memberName: string,
subMemberId: number,
subRoleId: number,
/**
* 自定义用户时userId = 0
*/
byUserId: number,
byUserEditName: string,
// phoneData: {
// code: string,
// phone: string,
// },
byUserEditPhone: string
eventTime: string,
eventDesc: string,
eventSuggest: string,
attachments: {
name: string,
url: string
}[]
}
const formActions = createFormActions()
const { onFormInputChange$ } = FormEffectHooks;
const DEFAULT_RETURN_DATA = {
totalCount: 0,
data: []
}
const SuggestAdd = () => {
const { visible, toggle } = useModal();
const { visible: userModalVisible, toggle: userModalToggle } = useModal();
const [userModalValue, setUserModalValue] = useState([]);
const [memberModalValue, setMemberModalValue] = useState([])
const InspectionAdd = () => {
const [initialValue, setInitialValue] = useState({
files: [{
uid: '3',
name: 'zzz.png',
url: 'http://www.baidu.com/zzz.png',
}],
completeDate: '2021-05-20',
tele: {
code: '+86',
phone: '1367272729'
const { id } = usePageStatus();
const isEdit = useMemo(() => id && typeof id === 'string', [id]);
const params = useMemo(() => { return id ? { id: id.toString() } : null }, [id]);
const { initialValue } = useInitialValue(PublicApi.getMemberComplaintUpperGet, params);
const [unsaved, setUnsaved] = useState<boolean>(false);
const [submitLoading, setSubmitLoading] = useState<boolean>(false);
useEffect(() => {
if (!initialValue) {
return ;
}
});
const { name, subMemberId, subRoleId, byUserName, byUserPhone, byUserId, ...rest } = initialValue;
setMemberModalValue([{
name: name,
subMemberId: subMemberId,
subRoleId: subRoleId
}])
setUserModalValue([
{
name: byUserName,
userId: byUserId,
phone: byUserPhone
}
])
formActions.setFieldState('*(byUserEditName, phone)', (state) => {
state.props['x-component-props'].disabled = byUserId;
})
}, [initialValue])
const handleSubmit = (value: any) => {
console.log(value);
const formatedValue = useMemo(() => {
if (!initialValue) {
return ;
}
const { name, byUserName, byUserPhone, ...rest } = initialValue;
return {
...rest,
memberName: name,
byUserEditName: byUserName,
byUserEditPhone: byUserPhone,
}
}, [initialValue])
const handleSubmit = async (value: SubmitType) => {
setSubmitLoading(true)
const { memberName, attachments, ...rest } = value;
const service = !isEdit ? PublicApi.postMemberComplaintUpperAdd : PublicApi.postMemberComplaintUpperUpdate;
const tempPostData = {
attachments: attachments?.map((_row) => ({
name: _row.name,
url: _row.url
})),
...rest,
}
const postData = isEdit ? {...tempPostData, id: id} : tempPostData
const { data, code } = await service(postData as any);
setSubmitLoading(false)
setUnsaved(false)
if (code === 1000) {
history.goBack();
}
}
const handleFetchData = useCallback(async (params: GetMemberInspectMembersRequest) => {
const { data, code } = await PublicApi.getMemberInspectMembers(params);
if (code === 1000) {
return data;
}
return DEFAULT_RETURN_DATA
}, [])
const handleOnOk = (selectRowKeys: string[] | number[], selectRowRecord: GetMemberInspectMembersResponse["data"] ) => {
const target = selectRowRecord[0];
formActions.setFieldValue('memberName', target.name)
formActions.setFieldValue('subMemberId', target.subMemberId);
formActions.setFieldValue('subRoleId', target.subRoleId);
setMemberModalValue(selectRowRecord)
toggle(false)
}
const handleFetchUserData = useCallback(async (params: GetMemberInspectUsersRequest) => {
console.log(params);
const { data, code } = await PublicApi.getMemberInspectUsers(params);
if (code === 1000) {
return data;
}
return DEFAULT_RETURN_DATA
}, [])
const handleUserOnOk = (selectRowKeys: string[] | number[], selectRowRecord: GetMemberInspectUsersResponse["data"] ) => {
const target = selectRowRecord[0];
formActions.setFieldValue('byUserEditName', target?.name)
formActions.setFieldValue('byUserId', target?.userId)
formActions.setFieldValue('byUserEditPhone', target?.phone)
const disabled = target?.userId ? true : false
formActions.setFieldState('*(byUserEditName, byUserEditPhone)', (state) => {
state.props['x-component-props'].disabled = disabled;
})
setUserModalValue(selectRowRecord);
userModalToggle(false)
}
return (
<PageHeaderWrapper
onBack={() => history.goBack()}
backIcon={<ReutrnEle description="返回" />}
title="生产通知单"
title={isEdit ? '修改投诉建议' : '新增投诉建议'}
extra={
(
<Button
key={1}
type="primary"
icon={<SaveOutlined />}
// loading={submitLoading}
loading={submitLoading}
onClick={() => formActions.submit()}
>
保存
......@@ -54,23 +194,68 @@ const InspectionAdd = () => {
<NiceForm
onSubmit={handleSubmit}
editable={true}
value={initialValue}
initialValues={formatedValue}
schema={complaintAddSchema}
actions={formActions}
components={{FormilyUploadFiles, FormilyRangeTime, FormilyCountryPhone}}
components={{FormilyUploadFiles, FormilyRangeTime, FormilyCountryPhone, FormilyCustomizeRadioButton}}
expressionScope={{
connectMember: (
<div>
<div onClick={() => toggle(true)}>
<LinkOutlined style={{marginRight: 4}}/>
选择
</div>
),
connectUser: (
<div onClick={() => userModalToggle(true)}>
<LinkOutlined style={{marginRight: 4}}/>
选择用户
</div>
)
}}
effects={() => {}}
effects={() => {
onFormInputChange$().subscribe(() => {
if (!unsaved) {
setUnsaved(true);
}
});
}}
/>
</Card>
<TableModal
visible={visible}
onClose={() => toggle(false)}
title={"选择会员"}
columns={memberColumns}
schema={memberSchema}
onOk={handleOnOk}
fetchData={handleFetchData}
tableProps={{
rowKey: (record) => `${record.subMemberId}_${record.subRoleId}`,
}}
mode={"radio"}
value={memberModalValue}
/>
<TableModal
visible={userModalVisible}
onClose={() => userModalToggle(false)}
title={"选择用户"}
columns={userColumns}
schema={userSchema}
customizeRadio
onOk={handleUserOnOk}
fetchData={handleFetchUserData}
effects={($,actions) => {
useStateFilterSearchLinkageEffect($, actions, 'name', FORM_FILTER_PATH );
}}
tableProps={{
rowKey: 'userId',
}}
mode={"radio"}
value={userModalValue}
/>
<Prompt when={unsaved} message="您还有未保存的内容,是否确定要离开?" />
</PageHeaderWrapper>
)
}
export default InspectionAdd;
export default SuggestAdd;
import React from 'react';
import { ColumnsType } from 'antd/es/table';
import { Link } from 'umi';
/**
* 列表页column
......@@ -7,42 +8,70 @@ import { ColumnsType } from 'antd/es/table';
const listColumns: ColumnsType<any> = [
{
title: '序号',
dataIndex: 'no',
},
{
title: '会员名称',
dataIndex: 'memberName',
dataIndex: 'name',
},
{
title: '事件分类',
dataIndex: 'startTime',
sorter: (a, b) => a.startTime - b.startTime,
dataIndex: 'classifyName',
filters: [
{
text: '投诉',
value: 1,
},
{
text: '建议',
value: 2
}
],
onFilter: (value, record) => record.classify === value,
},
{
title: '事件主题',
dataIndex: 'endTime',
sorter: (a, b) => a.endTime - b.endTime,
dataIndex: 'subject',
},
{
title: '事件事件',
dataIndex: 'outerStatus'
title: '事件时间',
dataIndex: 'eventTime'
},
{
title: '提出人',
dataIndex: 'outerStatus'
dataIndex: 'byUserName'
},
{
title: '提出人电话',
dataIndex: 'outerStatus'
dataIndex: 'byUserPhone'
},
{
title: '处理时间',
dataIndex: 'outerStatus'
dataIndex: 'handleTime'
},
{
title: '状态',
dataIndex: 'outerStatus'
dataIndex: 'statusName'
}
]
export default listColumns;
export const setColumnsByLinks = (link?: { [key: string]: string }) => {
const linksColumns: ColumnsType<any> = [
{
title: '序号',
dataIndex: 'id',
render: (text, record) => {
return (
<div style={{ display: 'flex', flexDirection: 'column', }}>
{
link?.['detail'] && (
<Link to={`${link?.['detail']}?id=${record.id}`}>{record.id}</Link>
) || (
<span>{record.id}</span>
)
}
</div>
)
}
},
]
return linksColumns.concat(listColumns);
}
// export default listColumns;
import { GetMemberComplaintSubGetResponse } from "@/services/MemberV2Api";
import { useMemo, useState } from "react";
export default function useGetAnchorHeader(blackList: string[] = []) {
type Options<T = any> = {
initialValue: T,
}
export default function useGetAnchorHeader<T>(blackList: string[] = [], options?: Options<T>) {
const [headers, setHeaders] = useState(() => {
return [
{
......@@ -14,8 +19,18 @@ export default function useGetAnchorHeader(blackList: string[] = []) {
]
})
const cacheHeaders = useMemo(() => {
return headers.filter((_item) => !blackList.includes(_item.key))
}, [blackList, headers])
const temp = headers.filter((_item) => !blackList.includes(_item.key))
if(options && options.initialValue) {
return temp.filter((_row) => {
console.log(options.initialValue)
if (_row.key === 'result' && (options.initialValue as unknown as GetMemberComplaintSubGetResponse).handleResult === null) {
return false
}
return true;
})
}
return temp;
}, [blackList, headers, options.initialValue])
return {headers: cacheHeaders};
}
......@@ -7,50 +7,54 @@ function useGetDetailCommon({ initialValue }) {
return [
{
title: '事件主题',
value: '123'
value: initialValue?.subject
},
{
title: "业务类型",
value: '温州皮革厂'
value: initialValue?.typeName
},
{
title: '事件事件',
value: '合格率达到90%'
title: '事件时间',
value: initialValue?.eventTime,
},
{
title: '事件描述',
value: 'Mx20为达标'
value: initialValue?.eventDesc
},
{
title: "会员名称",
value: '2020-12-01'
value: initialValue?.name
},
{
title: "附件",
value: (
<div>123</div>
<div>
{
initialValue?.attachments?.map((_row) => {
return (
<a key={_row.url} href={_row.href}>{_row.name}</a>
)
})
}
</div>
)
},
{
title: "事件建议",
value: '建议'
value: initialValue?.eventSuggest
},
{
title: "提出人",
span: 2,
value: (
<div>质检部门</div>
)
value: initialValue?.byUserName
},
{
title: '事件分类',
value: (
<div>1123</div>
)
value: initialValue?.classifyName
},
{
title:'提出人电话',
value: '12312312',
value: initialValue?.byUserPhone
}
]
}, [initialValue])
......@@ -60,23 +64,33 @@ function useGetDetailCommon({ initialValue }) {
return [
{
title: '处理结果',
value: '123'
value: initialValue?.handleResult
},
{
title: '处理人',
value: '整改通过'
value: initialValue?.handleUserName
},
{
title: "附件",
value: '123'
value: (
<div>
{
initialValue?.handleAttachments.map((_row) => {
return (
<a key={_row.url} href={_row.href}>{_row.name}</a>
)
})
}
</div>
)
},
{
title: '处理时间',
value: '13'
value: initialValue?.handleTime
},
{
title: "处理人电话",
value: '123',
value: initialValue?.handleUserPhone
}
]
}, [initialValue])
......
......@@ -14,87 +14,197 @@ export const complaintAddSchema: ISchema = {
"tab": "基本信息"
},
properties: {
layout: {
flexLayout: {
type: 'object',
'x-component': 'mega-layout',
'x-component': 'LeftRightLayout',
'x-component-props': {
labelCol: 4,
wrapperCol: 10,
labelAlign: 'left'
leftProps: {
span: 10
},
rightProps: {
span: 13,
},
wrapProps: {
align: 'start',
justify: 'space-between',
}
},
properties: {
theme: {
title: '事件主题',
type: 'string',
'x-rules': [
{
required: true,
message: '请填写事件主题'
},
{
limitByte: true, // 自定义校验规则
maxByte: 60
}
],
layout: {
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
placeholder: '最长60个字符,30个汉字'
labelCol: 4,
full: true,
labelAlign: 'left',
position: 'left',
},
properties: {
type: {
title: '业务类型',
type: 'object',
'x-component': 'FormilyCustomizeRadioButton',
enum: [
{ label: '投诉', value :1 },
{ label: '建议', value: 2 },
],
'x-rules': [
{
required: true,
message: '请选择业务类型'
}
]
},
classify: {
title: '事件分类',
type: 'string',
enum: [
{ label: '关于产品', value :1 },
{ label: '关于订单', value: 2 },
{ label: '关于配送', value: 3 },
{ label: '关于售后', value: 4 },
{ label: '关于服务', value: 5 },
{ label: '其他', value: 6 },
],
'x-rules': [
{
required: true,
message: '请选择事件分类'
}
]
},
subject: {
title: '事件主题',
type: 'string',
'x-rules': [
{
required: true,
message: '请填写事件主题'
},
{
limitByte: true, // 自定义校验规则
maxByte: 60
}
],
'x-component-props': {
placeholder: '最长60个字符,30个汉字'
}
},
memberName: {
title: '会员名称',
type: 'string',
"x-component-props": {
disabled: true,
addonAfter: "{{connectMember}}"
},
"x-rules": [
{
required: true,
message: '请选择会员名称'
}
],
},
subMemberId: {
title: "下级会员id",
type: 'string',
display: false
},
subRoleId: {
title: '下级会员角色Id',
type: 'string',
display: false,
},
eventTime: {
title: '事件时间',
type: "date",
'x-component-props': {
format: 'YYYY-MM-DD HH:mm:ss'
}
},
byUserEditName: {
type: 'string',
title: "提出人",
"x-component-props": {
addonAfter: "{{connectUser}}"
},
"x-rules": [
{
required: true,
message: '请填写提出人'
}
],
},
byUserId: {
title: '提出人id',
type: 'string',
display: false,
},
// phoneData: {
// type: 'string',
// title: '提出人电话',
// 'x-component': 'FormilyCountryPhone',
// enum: [
// {
// label: '中国',
// value: '+86'
// }
// ]
// },
byUserEditPhone: {
type: "string",
title: "提出人电话",
'x-component-props': {},
},
eventSuggest: {
title: '事件建议',
type: 'textarea',
"x-component-props": {
style: {
width: '100%'
}
},
'x-rules': [
{
limitByte: true, // 自定义校验规则
maxByte: 60
}
]
},
}
},
desc: {
title: '事件描述',
type: 'textarea',
},
memberName: {
title: '会员名称',
type: 'string',
"x-component-props": {
disabled: true,
addonAfter: "{{connectMember}}"
rightLayout: {
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
labelCol: 4,
labelAlign: 'left',
position: 'right',
full: true,
},
"x-rules": [
{
required: true,
message: '请选择会员名称'
}
],
},
proposer: {
type: 'string',
title: "提出人",
"x-rules": [
{
required: true,
message: '请填写提出人'
}
],
},
tele: {
type: 'string',
title: '提出人电话',
'x-component': 'FormilyCountryPhone',
enum: [
{
label: '123',
value: '123'
}
]
},
suggest: {
title: '事件建议',
type: 'textarea',
"x-component-props": {
style: {
width: '100%'
properties: {
eventDesc: {
title: '事件描述',
type: 'textarea',
'x-rules': [
{
limitByte: true, // 自定义校验规则
maxByte: 60
}
]
},
attachments: {
title: '考察要求附件',
type: 'object',
'x-component': 'FormilyUploadFiles',
}
},
},
files: {
title: '考察要求附件',
type: 'object',
'x-component': 'FormilyUploadFiles',
}
}
}
}
},
}
},
}
......
......@@ -16,22 +16,39 @@ export const handleFormSchema: ISchema = {
labelAlign: 'left'
},
properties: {
result: {
handleResult: {
title: '处理结果',
type: 'string'
type: 'string',
"x-rules": [
{
required: true,
message: '请填写处理结果'
}
],
},
dealTime: {
handleTime: {
title: "处理时间",
type: "string"
type: "date",
"x-component-props": {
format: "YYYY-MM-DD HH:mm:ss"
},
"x-rules": [
{
required: true,
message: '请填写选择处理时间'
}
],
},
people: {
handleUserEditName: {
title: '处理人',
type: 'object',
"x-component": 'FormilySelectMember',
type: 'string',
// "x-component": 'FormilySelectMember',
// "x-component-props": {
// customizeRender: "{{connectMember}}",
// fetchData: "{{handleFetchUserData}}"
// },
"x-component-props": {
// disabled: true,
// addonAfter: "{{connectMember}}"
customizeRender: "{{connectMember}}"
addonAfter: "{{connectUser}}"
},
"x-rules": [
{
......@@ -40,18 +57,25 @@ export const handleFormSchema: ISchema = {
}
],
},
phone: {
handleUserId: {
title: '处理人id',
type: 'string',
display: false,
"x-component-props": {},
},
handleUserEditPhone: {
type: 'string',
title: '提出人电话',
'x-component': 'FormilyCountryPhone',
enum: [
{
label: '123',
value: '123'
}
]
// 'x-component': 'FormilyCountryPhone',
"x-component-props": {},
// enum: [
// {
// label: '123',
// value: '123'
// }
// ]
},
files: {
handleAttachments: {
title: '考察要求附件',
type: 'object',
'x-component': 'FormilyUploadFiles',
......
......@@ -44,35 +44,24 @@ export const complaintAndSuggestListSchema: ISchema = {
},
},
properties: {
theme: {
subject: {
type: 'string',
'x-component-props': {
placeholder: '修改主题',
placeholder: '事件主题',
allowClear: true,
style: {
width: 160,
},
},
},
time: {
'[eventTimeStart, eventTimeEnd]': {
type: 'daterange',
default: undefined,
'x-component-props': {
placeholder: '会员等级(全部)',
placeholder: ["事件开始时间", "事件结束时间"],
allowClear: true,
style: {
width: 160,
},
},
},
outerStatus: {
type: 'string',
enum: [],
'x-component-props': {
placeholder: '外部状态',
allowClear: true,
style: {
width: 160,
width: 240,
},
},
},
......
import React, { useEffect } from 'react';
import CustomizeRadioButton from '.';
import { Options } from '.';
interface Iprops {
editable: boolean,
value: Options["value"],
mutators: {
change: (value: Options["value"]) => void
},
props: {
enum: Options[],
}
errors: string[]
}
const FormilyCustomizeRadioButton: React.FC<Iprops> & { isFieldComponent: boolean } = (_props: Iprops) => {
const { value, editable, props, mutators, errors } = _props;
const enumData = props.enum;
const componentProps = props?.['x-component-props'] || {};
const handleChange = (data: string | number) => {
mutators.change?.(data);
}
return (
<div>
<CustomizeRadioButton
disabled={!editable}
options={enumData}
value={value}
onChange={handleChange}
{...componentProps}
/>
{
errors.length > 0 && (
<p style={{color: '#ff4d4f'}}>{errors.join("")}</p>
)
}
</div>
)
}
FormilyCustomizeRadioButton.isFieldComponent = true
export default FormilyCustomizeRadioButton
.container {
display: flex;
flex-direction: row;
align-items: center;
.item {
padding: 10px @padding-md;
border: 1px solid #E3E4E5;
margin-right: @margin-md;
line-height: 12px;
font-size: 12px;
cursor: pointer;
}
.activeItem {
color: @main-color;
border-color: @main-color;
}
}
import React, { useEffect, useState } from 'react'
import styles from './index.less';
import cs from 'classnames';
export type Options = {
label: string,
value: number | string,
}
interface Iprops {
options: Options[],
value?: number | string,
// defaultValue?: number | string,
disabled?: boolean,
onChange?: null | ((value: number | string) => void),
}
const CustomizeRadioButton: React.FC<Iprops> = (props: Iprops) => {
const { options, value, onChange, disabled } = props;
const [innerValue, setInnerValue] = useState(() => value || '');
useEffect(() => {
if ("value" in props && typeof value !== 'undefined') {
setInnerValue(value);
}
}, [value])
const handleChange = (value: number | string) => {
if (disabled) {
return
}
if (!("value" in props)) {
setInnerValue(value);
}
if(onChange) {
onChange(value)
}
}
return (
<div className={styles.container}>
{
options.map((_item) => {
return (
<div
key={_item.value}
className={cs(styles.item, { [styles.activeItem]: _item.value === innerValue })}
onClick={() => handleChange(_item.value)}
>
{_item.label}
</div>
)
})
}
</div>
)
}
CustomizeRadioButton.defaultProps = {
onChange: null,
// defaultValue: '',
disabled: false,
}
export default CustomizeRadioButton
import React from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { Spin, Card, Table, Button, Drawer } from 'antd';
import AnchorPage from '@/layouts/AnchorPage';
import CustomizeColumn from '@/components/CustomizeColumn';
......@@ -15,55 +15,106 @@ import FormilyCountryPhone from './components/CountryPhone/FormilyCountryPhone';
import { handleFormSchema } from './common/schema/handle';
import { LinkOutlined } from '@ant-design/icons';
import FormilySelectMember from '../memberEvaluate/components/FormilySelectMember';
import { usePageStatus } from '@/hooks/usePageStatus';
import useInitialValue from '../common/hooks/useInitialValue';
import { GetMemberComplaintUpperGetRequest, GetMemberComplaintUpperGetResponse, GetMemberInspectUsersRequest, GetMemberInspectUsersResponse } from '@/services/MemberV2Api';
import TableModal from '../components/TableModal';
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch';
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { userColumns } from '../memberInspection/common/columns/userColumns';
import { memberSchema, userSchema } from '../memberInspection/common/schema/add';
import { history } from 'umi';
type SubmitData = {
handleUserEditName: string,
handleUserId: number,
handleUserEditPhone: string,
handleResult: string,
handleTime: string,
handleAttachments: {
name: string,
url: string,
}[]
}
const formActions = createFormActions()
const DEFAULT_RETURN_DATA = {
totalCount: 0,
data: []
}
const TobeEvaluateDetail = () => {
const { initialValue } = useFetchInfo(PublicApi.getMemberAbilitySubGet, { id: 1})
const { id } = usePageStatus();
const params = useMemo(() => { return id ? { id: id.toString() } : null }, [id]);
const { initialValue } = useInitialValue<GetMemberComplaintUpperGetResponse, GetMemberComplaintUpperGetRequest>(PublicApi.getMemberComplaintUpperGet, params);
const { visible, toggle } = useModal();
const { headers } = useGetAnchorHeader()
const { visible: userModalVisible, toggle: userModalToggle } = useModal();
const [userModalValue, setUserModalValue] = useState<any>([])
const { headers } = useGetAnchorHeader([], {initialValue})
const { basicInfo, resultInfo } = useGetDetailCommon({initialValue})
const onSubmitRes = () => {
toggle(true);
}
const handleSubmit = (value: { result: string, reason: string }) => {
const handleSubmit = async (value: SubmitData) => {
console.log(value);
const { handleAttachments, ...rest } = value;
const postData = {
id: id,
...rest,
handleAttachments: handleAttachments?.map((_row) => ({
name: _row.name,
url: _row.url
}))
}
const { data, code } = await PublicApi.postMemberComplaintUpperSend(postData);
if (code === 1000) {
history.goBack()
}
}
/**
* @todo 这里类型等接口改
*/
const connectMember = (value: any, toggleDrawer: (flag: boolean) => void) => {
return (
<div style={{ display: 'flex', flexDirection: "row", alignItems: 'center', border: '1px solid #ccc'}}>
<div style={{ flex: 1, padding: '0 8px' }}>{value?.name}</div>
<div style={{ padding: '0 8px', borderLeft: "1px solid #ccc" }} onClick={() => toggleDrawer(true)}>
<LinkOutlined /> 重新选择
</div>
</div>
)
const handleFetchUserData = useCallback(async (params: GetMemberInspectUsersRequest) => {
const { data, code } = await PublicApi.getMemberInspectUsers(params);
if (code === 1000) {
return data;
}
return DEFAULT_RETURN_DATA
}, [])
const handleUserOnOk = (selectRowKeys: string[] | number[], selectRowRecord: GetMemberInspectUsersResponse["data"] ) => {
const target = selectRowRecord[0];
formActions.setFieldValue('handleUserEditName', target?.name)
formActions.setFieldValue('handleUserId', target?.userId)
formActions.setFieldValue('handleUserEditPhone', target?.phone)
const disabled = target?.userId ? true : false
formActions.setFieldState('*(handleUserEditName, handleUserEditPhone)', (state) => {
state.props['x-component-props'].disabled = disabled;
})
setUserModalValue(selectRowRecord);
userModalToggle(false)
}
return (
<Spin spinning={false}>
<AnchorPage
title={`温州市隆昌皮业有限公式`}
title={initialValue?.subject}
anchors={headers}
extra={
<Button type="primary" onClick={onSubmitRes}>确认整改结果</Button>
<Button type="primary" onClick={onSubmitRes}>处理结果信息</Button>
}
>
<CustomizeColumn id="detail" data={basicInfo} title="基本信息" column={3} />
<div style={{ margin: `${theme["@margin-md"]} 0` }}>
<CustomizeColumn id="result" data={resultInfo} title="整改结果" column={1} />
</div>
{/* <div style={{ margin: `${theme["@margin-md"]} 0` }}>
<Card title="流转记录" id="record" extra={<Button>内部流转</Button>}>
<Table columns={recordColumn}></Table>
</Card>
</div> */}
<CustomizeColumn id="detail" data={basicInfo} title="投诉建议信息" column={3} />
{
initialValue?.handleResult && (
<div style={{ margin: `${theme["@margin-md"]} 0` }}>
<CustomizeColumn id="result" data={resultInfo} title="处理结果信息" column={3} />
</div>
)
}
</AnchorPage>
<Drawer
visible={visible}
......@@ -87,10 +138,35 @@ const TobeEvaluateDetail = () => {
actions={formActions}
onSubmit={handleSubmit}
expressionScope={{
connectMember
connectUser: (
<div onClick={() => userModalToggle(true)}>
<LinkOutlined style={{marginRight: 4}}/>
选择
</div>
),
handleFetchUserData
}}
/>
</Drawer>
<TableModal
modalType="Drawer"
visible={userModalVisible}
onClose={() => userModalToggle(false)}
title={"选择用户"}
columns={userColumns}
schema={userSchema}
customizeRadio
onOk={handleUserOnOk}
fetchData={handleFetchUserData}
value={userModalValue}
effects={($,actions) => {
useStateFilterSearchLinkageEffect($, actions, 'name', FORM_FILTER_PATH );
}}
tableProps={{
rowKey: 'userId',
}}
mode={"radio"}
/>
</Spin>
)
}
......
import React from 'react';
import { Card, Space, Button } from 'antd'
import queryColumns from './common/columns';
import React, { useRef, useState } from 'react';
import { Card, Space, Button, Spin } from 'antd'
import { setColumnsByLinks } from './common/columns';
import { complaintAndSuggestListSchema } from './common/schema';
import useFetchList from '../memberEvaluate/hooks/useFetchList';
import { PlusOutlined } from '@ant-design/icons';
......@@ -10,31 +10,83 @@ import { PublicApi } from '@/services/api';
import CustomizeQueryList from '../components/CustomizeQueryList';
import { Link } from 'umi';
import useColumns from '../memberRectification/common/hooks/useColumns';
import { GetMemberComplaintUpperPageRequest } from '@/services/MemberV2Api';
import moment from 'moment';
interface Iprops {};
const queryColumns = setColumnsByLinks({
detail: '/memberCenter/memberAbility/complainAndSuggest/list/detail'
})
const List: React.FC<Iprops> = (props: Iprops) => {
const { fetchListData } = useFetchList();
const { columns } = useColumns(queryColumns)
const [ currentIdIsInLoading, setCurrentIdIsInLoading ] = useState<number[]>([]);
const ref = useRef<any>({})
const { columns } = useColumns(queryColumns, [
{
title: '操作',
render: (text, record) => {
const loading = currentIdIsInLoading.includes(record.id);
if(record.submitOrUpdateOrDelete) {
return (
<Space>
<Spin spinning={loading}>
<a onClick={() => handleSubmit({id: record.id}, "submit")}>提交投诉建议</a>
</Spin>
<Link to={`/memberCenter/memberAbility/complainAndSuggest/list/edit?id=${record.id}`}>修改</Link>
<Spin spinning={loading}>
<a onClick={() => handleSubmit({id: record.id}, "delete")}>删除</a>
</Spin>
</Space>
)
}
if (record.handle) {
return <Link to={`/memberCenter/memberAbility/complainAndSuggest/list/detail?id=${record.id}`}>处理投诉结果</Link>
}
return null
}
}
])
const controllerBtns = (
<div>
<Link to={"/memberCenter/memberAbility/memberEvaluate/evaluate/createEvaluate/add"}>
<Button type="primary">
<PlusOutlined /> 新建
</Button>
<Link to={"/memberCenter/memberAbility/complainAndSuggest/list/add"}>
<Button type="primary" icon={<PlusOutlined />}>新建</Button>
</Link>
</div>
)
const handleFetch = async (params) => {
const result = fetchListData(PublicApi.getMemberAbilitySubPage, params);
const handleFetch = async (params: GetMemberComplaintUpperPageRequest) => {
const { eventTimeEnd, eventTimeStart, ...rest } = params;
let postData: Partial<GetMemberComplaintUpperPageRequest> = rest;
if (typeof eventTimeEnd !== 'undefined') {
postData = {
...rest,
// eventTimeStart: moment(eventTimeStart, 'YYYY-MM-DD').startOf("day").format('YYYY-MM-DD HH:mm:ss'),
// eventTimeEnd: moment(eventTimeEnd, 'YYYY-MM-DD').endOf("day").format('YYYY-MM-DD HH:mm:ss'),
}
}
const result = fetchListData(PublicApi.getMemberComplaintUpperPage, postData);
return result
}
const handleSubmit = async (params: {id: number}, type: "submit" | "delete") => {
const newList = [...currentIdIsInLoading];
newList.push(params.id);
const service = type === 'submit' ? PublicApi.postMemberComplaintUpperSubmit : PublicApi.postMemberComplaintUpperDelete;
setCurrentIdIsInLoading(newList)
const { data, code } = await service(params);
setCurrentIdIsInLoading((prev) => prev.filter((_item) => _item !== params.id));
if (code === 1000) {
ref.current?.submit()
}
}
return (
<Card>
<CustomizeQueryList
ref={ref}
columns={columns}
schema={complaintAndSuggestListSchema}
fetchListData={handleFetch}
......@@ -42,12 +94,8 @@ const List: React.FC<Iprops> = (props: Iprops) => {
controllerBtns,
}}
effects={($, actions) => {
useStateFilterSearchLinkageEffect(
$,
actions,
'name',
FORM_FILTER_PATH,
);
useStateFilterSearchLinkageEffect($, actions,'name', FORM_FILTER_PATH,);
// useAsyncSelect('status', fetchStatusOptions);
}}
/>
</Card>
......
import { ISchema } from '@formily/antd';
import React, { useEffect, useRef, useState } from 'react';
import { Modal, Row, Col } from 'antd';
import { Modal, Row, Col, Drawer,Button } from 'antd';
import { createFormActions } from '@formily/antd';
import { StandardTable } from 'god';
import { ColumnsType } from 'antd/es/table';
......@@ -9,6 +9,7 @@ import NiceForm from '@/components/NiceForm';
const formActions = createFormActions();
interface Iprops {
modalType?: "Modal" | "Drawer"
/**
* 是否显示
*/
......@@ -25,7 +26,7 @@ interface Iprops {
* table Ccolumn
*/
columns: ColumnsType,
footer?: React.ReactNode,
tableProps?: {
rowKey: string | ((record) => any)
......@@ -52,7 +53,7 @@ interface Iprops {
}
const TableModal: React.FC<Iprops> = (props: Iprops) => {
const { title, visible, schema, columns, effects, tableProps, mode, expressionScope, fetchData, onClose, onOk, value, format, customizeRadio } = props;
const { title, visible, schema, columns, effects, tableProps, mode, expressionScope, fetchData, onClose, onOk, value, format, customizeRadio, modalType, footer } = props;
const ref = useRef<any>({});
const isFirstLoad = useRef<boolean>(true)
const [selectRow, setSelectRow] = useState<number[] | string[]>(() => {
......@@ -103,17 +104,36 @@ const TableModal: React.FC<Iprops> = (props: Iprops) => {
};
const handleSearch = (params: any) => {
const res = format && format(params) || params;
res.current?.reload(res)
const res = (format && format(params)) || params;
ref.current?.reload(res)
}
const Component = modalType === 'Modal' ? Modal : Drawer;
const renderFooter = () => {
return (
<div style={{ textAlign: 'right'}}>
<Button onClick={handleOnClose} style={{ marginRight: 8 }}>
取消
</Button>
<Button onClick={handleOk} type="primary">
提交
</Button>
</div>
)
}
const otherProps = modalType === 'Drawer' ? { footer: renderFooter() } : { onOk: handleOk}
return (
<Modal
<Component
title={title}
visible={visible}
onCancel={handleOnClose}
onOk={handleOk}
// onOk={handleOk}
width={840}
{...otherProps}
>
<StandardTable
columns={columns}
......@@ -147,7 +167,7 @@ const TableModal: React.FC<Iprops> = (props: Iprops) => {
>
</StandardTable>
</Modal>
</Component>
)
}
......@@ -161,6 +181,8 @@ TableModal.defaultProps = {
expressionScope: {},
format: null,
customizeRadio: false,
modalType: "Modal",
footer: null
}
export default TableModal;
......@@ -68,7 +68,13 @@ const FormilySelectMember: React.FC<Iprops> & { isFieldComponent: boolean } = (p
];
const fetchListData = async (params: any) => {
return await fetchData(params);
if(fetchData) {
return await fetchData(params);
}
return {
totalCount: 0,
data: [],
}
}
const onSelectChange = (record, selected: Boolean, selectedRows) => {
......
......@@ -59,6 +59,7 @@ const InspectionAdd = () => {
const [memberModalValue, setMemberModalValue] = useState([])
const [initialValue, setInitialValue] = useState<GetMemberInspectGetResponse | null>(null);
const [unsaved, setUnsaved] = useState<boolean>(false);
const [submitLoading, setSubmitLoading] = useState(false);
useEffect(() => {
if(!isEdit) {
......@@ -128,11 +129,13 @@ const InspectionAdd = () => {
}, [])
const handleOnSubmit = async (values: SubmitDataType) => {
console.log(values);
const { name, userName, score, attachments, reports, ...rest } = values;
const { name, userName, userId, score, attachments, reports, ...rest } = values;
setSubmitLoading(true)
const postTempData = {
...rest,
score: +score,
userEditName: userName,
userId: userId || 0,
attachments: attachments?.map((_row) => ({
name: _row.name,
url: _row.url
......@@ -145,6 +148,8 @@ const InspectionAdd = () => {
const service = isEdit ? PublicApi.postMemberInspectUpdate : PublicApi.postMemberInspectAdd;
const postData: EditPostData | PostData = isEdit ? { ...postTempData, id: id } : postTempData;
const { code, data } = await service(postData as any);
setUnsaved(false)
setSubmitLoading(false)
if (code === 1000) {
history.goBack();
}
......@@ -171,7 +176,7 @@ const InspectionAdd = () => {
backIcon={<ReutrnEle description="返回" />}
title={isEdit ? '修改会员考察' : "新增会员考察"}
extra={
<Button type="primary" icon={<SaveOutlined />} onClick={() => formActions.submit()}>
<Button loading={submitLoading} type="primary" icon={<SaveOutlined />} onClick={() => formActions.submit()}>
保存
</Button>
}
......
......@@ -144,6 +144,10 @@ export const InspectionAddSchema: ISchema = {
{
required: true,
message: '请输入考察评分'
},
{
pattern: /^((\d|[1-9]\d)(\.\d{1,2})?|100)$/,
message: '请填写0-100的数字,可以保留2位小数'
}
],
},
......
import moment from "moment"
import React from "react"
import { Link } from 'umi'
export const commonColumns = [
{
title: '考评单号/主题',
dataIndex: 'id',
render: (text, record) => {
return (
<div style={{ display: 'flex', flexDirection: 'column', }}>
<Link to={`/memberCenter/memberAbility/profile/memberRectification/detail?id=${record.id}`}>{record.id}</Link>
<p>{record.subject}</p>
</div>
)
}
},
{
title: '会员名称',
dataIndex: 'upperMemberName',
},
{
title: '整改期限开始日期',
dataIndex: 'rectifyDayStart',
sorter: (_a, _b) => moment(_a.rectifyTimeStart).valueOf() - moment(_b.rectifyTimeStart).valueOf(),
},
{
title: '整改期限截止日期',
dataIndex: 'rectifyDayEnd',
sorter: (_a, _b) => moment(_a.rectifyTimeEnd).valueOf() - moment(_b.rectifyTimeEnd).valueOf(),
},
{
title: '整改结果',
dataIndex: 'agreeResultName',
}
]
import React, { useMemo, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { Spin, Card, Table, Button, Drawer } from 'antd';
import AnchorPage from '@/layouts/AnchorPage';
import CustomizeColumn from '@/components/CustomizeColumn';
......@@ -9,7 +9,7 @@ import useGetAnchorHeader from '../../memberRectification/common/hooks/useGetAnc
import useGetDetailCommon from '../../memberRectification/common/hooks/useGetDetailCommon';
import { usePageStatus } from '@/hooks/usePageStatus';
import useInitialValue from '@/pages/handling/common/hooks/useInitialValue';
import { GetMemberRectifyWaitAddGetResponse } from '@/services/MemberV2Api';
import { GetMemberRectifyManageGetResponse, GetMemberRectifyWaitAddGetResponse } from '@/services/MemberV2Api';
import { findLastIndexFlowState } from '@/utils';
import FlowRecords from '@/components/FlowRecords';
import { innerColumns, outerColumns } from '../../memberRectification//common/columns/historyColumn'
......@@ -19,16 +19,17 @@ import NiceForm from '@/components/NiceForm';
import { rectificationReportSchema } from './schema'
import { history } from 'umi';
import FormilyUploadFiles from '@/components/UploadFiles/FormilyUploadFiles';
import { FormOutlined } from '@ant-design/icons';
const formActions = createFormActions()
const rectificationAddDetail = () => {
const { visible, toggle } = useModal();
const { headers } = useGetAnchorHeader(["editInfo", "result"])
const { headers, setHeaders } = useGetAnchorHeader()
const { id } = usePageStatus();
const params = useMemo(() => { return id ? { id: id.toString() } : null }, [id]);
const { loading, initialValue }= useInitialValue<GetMemberRectifyWaitAddGetResponse, { id: string }>(PublicApi.getMemberRectifyManageGet, params)
const { basicInfo, editInfo, resultInfo } = useGetDetailCommon({initialValue});
const { loading, initialValue }= useInitialValue<GetMemberRectifyManageGetResponse, { id: string }>(PublicApi.getMemberRectifyManageGet, params)
const { basicInfo, editInfo, resultInfo } = useGetDetailCommon({initialValue: initialValue as GetMemberRectifyManageGetResponse as any});
const [submitLoading, setSubmitLoading] = useState<boolean>(false);
const outerVerifySteps = useMemo(() => {
......@@ -43,17 +44,34 @@ const rectificationAddDetail = () => {
}))
}, [initialValue])
useEffect(() => {
if (!initialValue) {
return;
}
const newHeaders = headers.filter((_item) => {
if (initialValue?.reportAttachments.length === 0 && _item.key === 'editInfo') {
return false
}
if (initialValue?.agreeResult === null && _item.key === 'result') {
return false
}
return true
})
setHeaders(newHeaders)
}, [initialValue])
const onSubmitRes = () => {
formActions.submit()
}
const handleSubmit = async (value: { reportDigest: string, reportAttachments: { label: string, url: string }[] }) => {
const handleSubmit = async (value: { reportDigest: string, reportAttachments: { name: string, url: string }[] }) => {
setSubmitLoading(true)
const { data, code } = await PublicApi.postMemberRectifyManageUpdateReport({
id: id,
reportDigest: value.reportDigest,
reportAttachments: value.reportAttachments.map((_row) => ({
name: _row.label,
name: _row.name,
url: _row.url,
}))
})
......@@ -69,6 +87,9 @@ const rectificationAddDetail = () => {
<AnchorPage
title={initialValue?.subject}
anchors={headers}
extra={
<Button type="primary" icon={<FormOutlined />} onClick={() => toggle(true)}>填写整改信息</Button>
}
>
<AuditProcess
outerVerifySteps={outerVerifySteps}
......@@ -78,17 +99,22 @@ const rectificationAddDetail = () => {
<div id="basicInfo" style={{ margin: `${theme["@margin-md"]} 0` }}>
<CustomizeColumn data={basicInfo} title="基本信息" column={3} />
</div>
<div id="editInfo" style={{ margin: `${theme["@margin-md"]} 0` }}>
<CustomizeColumn data={editInfo} title="整改信息" column={1} />
</div>
<div id="result" style={{ margin: `${theme["@margin-md"]} 0` }}>
<CustomizeColumn data={resultInfo} title="整改结果" column={1} />
</div>
{
(initialValue?.reportDigest || initialValue?.reportAttachments.length > 0) && (
<div id="editInfo" style={{ margin: `${theme["@margin-md"]} 0` }}>
<CustomizeColumn data={editInfo} title="整改信息" column={1} />
</div>
)
}
{
initialValue?.agreeResult && (
<div id="result" style={{ margin: `${theme["@margin-md"]} 0` }}>
<CustomizeColumn data={resultInfo} title="整改结果" column={1} />
</div>
)
}
<div id="record">
<FlowRecords
innerRowkey="id"
innerColumns={innerColumns as any}
innerDataSource={initialValue?.innerHistory}
outerRowkey="id"
outerColumns={outerColumns as any}
outerDataSource={initialValue?.outerHistory}
......@@ -97,7 +123,7 @@ const rectificationAddDetail = () => {
<Drawer
visible={visible}
onClose={() => toggle(false)}
width={440}
width={540}
title="确认整改结果"
footer={
<div style={{ textAlign: 'right'}}>
......
......@@ -12,6 +12,7 @@ import CustomizeQueryList from '../../components/CustomizeQueryList';
import { Link } from 'umi';
import useColumns from '../../memberRectification/common/hooks/useColumns';
import { useAsyncSelect } from '@/formSchema/effects/useAsyncSelect';
import { commonColumns } from './common/columns'
interface Iprops {};
......@@ -23,11 +24,15 @@ const List: React.FC<Iprops> = (props: Iprops) => {
const { fetchListData } = useFetchList();
const ref = useRef<any>({})
const [ currentIdIsInLoading, setCurrentIdIsInLoading ] = useState<number[]>([])
const { columns, fetchStatusOptions } = useColumns(queryColumns, [
const { columns, fetchStatusOptions } = useColumns(commonColumns, [
{
title: "操作",
render: (text, record) => {
const isloading = currentIdIsInLoading.includes(record.id);
const rectifyOrUpdate = record.rectifyOrUpdate;
if (!rectifyOrUpdate) {
return ;
}
return (
<Space>
<Spin spinning={isloading}>
......
import { FORM_FILTER_PATH } from "@/formSchema/const";
import { ISchema } from "@formily/antd";
import React from 'react';
export const querySchema: ISchema = {
type: 'object',
......@@ -78,48 +79,39 @@ export const querySchema: ISchema = {
export const rectificationReportSchema: ISchema = {
type: "object",
properties: {
tabs: {
type: "object",
"x-component": "tab",
layout: {
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
labelCol: 4,
labelAlign: 'left',
full: true,
},
properties: {
"tab-1": {
type: "object",
"x-component": "tabpane",
"x-component-props": {
"tab": "基本信息"
},
properties: {
layout: {
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
labelCol: 4,
wrapperCol: 10,
labelAlign: 'left'
},
properties: {
reportDigest: {
title: '摘要',
type: 'textarea',
'x-rules': [
{
limitByte: true, // 自定义校验规则
maxByte: 120
}
],
'x-component-props': {
placeholder: '最长120个字符,60个汉字'
}
},
reportAttachments: {
title: '考察要求附件',
type: 'object',
'x-component': 'FormilyUploadFiles',
}
}
reportDigest: {
title: '摘要',
type: 'textarea',
'x-rules': [
{
limitByte: true, // 自定义校验规则
maxByte: 120
}
],
'x-component-props': {
placeholder: '最长120个字符,60个汉字'
}
},
reportAttachments: {
title: <div>附件 <span style={{color: '#FF4D4F'}}>*</span></div>,
type: 'object',
'x-component': 'FormilyUploadFiles',
'x-rules': [
{
required: true, // 自定义校验规则
message: '请上传附件'
}
],
}
}
}
}
......
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Card, Button } from 'antd';
import { history, Prompt } from 'umi';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { LinkOutlined, SaveOutlined } from '@ant-design/icons';
import SchemaForm, { createFormActions, FormEffectHooks } from '@formily/antd';
import ReutrnEle from '@/components/ReturnEle';
import NiceForm from '@/components/NiceForm';
import { complaintAddSchema } from './common/schema/add';
import FormilyUploadFiles from '@/components/UploadFiles/FormilyUploadFiles'
import FormilyRangeTime from '@/components/RangeTime/FormilyRangeTime';
import FormilyCountryPhone from '../../complaintsAndSuggests/components/CountryPhone/FormilyCountryPhone';
import FormilyCustomizeRadioButton from '../../complaintsAndSuggests/components/CustomizeRadioButton/FormilyCustomizeRadioButton';
import useModal from '../../memberEvaluate/hooks/useModal';
import TableModal from '../../components/TableModal';
import { memberColumns } from '../../memberInspection/common/columns/memberColumns';
import { userColumns } from '../../memberInspection/common/columns/userColumns';
import { memberSchema, userSchema } from '../../memberInspection/common/schema/add'
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch';
import { PublicApi } from '@/services/api';
import { GetMemberInspectMembersRequest, GetMemberInspectMembersResponse, GetMemberInspectUsersRequest, GetMemberInspectUsersResponse } from '@/services/MemberV2Api';
import { usePageStatus } from '@/hooks/usePageStatus';
import useInitialValue from '../../common/hooks/useInitialValue';
type SubmitType = {
/**
* 业务类型1-投诉2-建议
*/
type: 1 | 2,
/**
* 事件分类1-关于产品2-关于订单3-关于配送4-关于售后5-关于服务6-其他
*/
classify: number,
subject: string,
memberName: string,
subMemberId: number,
subRoleId: number,
/**
* 自定义用户时userId = 0
*/
byUserId: number,
byUserEditName: string,
// phoneData: {
// code: string,
// phone: string,
// },
byUserEditPhone: string
eventTime: string,
eventDesc: string,
eventSuggest: string,
attachments: {
name: string,
url: string
}[]
}
const formActions = createFormActions()
const { onFormInputChange$ } = FormEffectHooks;
const DEFAULT_RETURN_DATA = {
totalCount: 0,
data: []
}
const SuggestAdd = () => {
const { visible, toggle } = useModal();
const { visible: userModalVisible, toggle: userModalToggle } = useModal();
const [userModalValue, setUserModalValue] = useState([]);
const [memberModalValue, setMemberModalValue] = useState([])
const { id } = usePageStatus();
const isEdit = useMemo(() => id && typeof id === 'string', [id]);
const params = useMemo(() => { return id ? { id: id.toString() } : null }, [id]);
const { initialValue } = useInitialValue(PublicApi.getMemberComplaintSubGet, params);
const [unsaved, setUnsaved] = useState<boolean>(false);
const [submitLoading, setSubmitLoading] = useState<boolean>(false);
useEffect(() => {
if (!initialValue) {
return ;
}
const { upperName, memberId, roleId, byUserName, byUserPhone, byUserId, ...rest } = initialValue;
setMemberModalValue([{
name: upperName,
memberId: memberId,
subRoleId: roleId
}])
setUserModalValue([
{
name: byUserName,
userId: byUserId,
phone: byUserPhone
}
])
formActions.setFieldState('*(byUserEditName, phone)', (state) => {
state.props['x-component-props'].disabled = byUserId;
})
}, [initialValue])
const formatedValue = useMemo(() => {
if (!initialValue) {
return ;
}
const { name, byUserName, byUserPhone, ...rest } = initialValue;
return {
...rest,
memberName: name,
byUserEditName: byUserName,
byUserEditPhone: byUserPhone,
}
}, [initialValue])
const handleSubmit = async (value: SubmitType) => {
console.log(value);
setSubmitLoading(true)
const { memberName, attachments, ...rest } = value;
const service = !isEdit ? PublicApi.postMemberComplaintSubAdd : PublicApi.postMemberComplaintSubUpdate;
const tempPostData = {
attachments: attachments?.map((_row) => ({
name: _row.name,
url: _row.url
})),
...rest,
}
const postData = isEdit ? {...tempPostData, id: id} : tempPostData
const { data, code } = await service(postData as any);
setSubmitLoading(false)
setUnsaved(false)
if (code === 1000) {
history.goBack();
}
}
const handleFetchData = useCallback(async (params: GetMemberInspectMembersRequest) => {
const { data, code } = await PublicApi.getMemberInspectMembers(params);
if (code === 1000) {
return data;
}
return DEFAULT_RETURN_DATA
}, [])
const handleOnOk = (selectRowKeys: string[] | number[], selectRowRecord: GetMemberInspectMembersResponse["data"] ) => {
const target = selectRowRecord[0];
formActions.setFieldValue('memberName', target.name)
formActions.setFieldValue('memberId', target.subMemberId);
formActions.setFieldValue('roleId', target.subRoleId);
setMemberModalValue(selectRowRecord)
toggle(false)
}
const handleFetchUserData = useCallback(async (params: GetMemberInspectUsersRequest) => {
const { data, code } = await PublicApi.getMemberInspectUsers(params);
if (code === 1000) {
return data;
}
return DEFAULT_RETURN_DATA
}, [])
const handleUserOnOk = (selectRowKeys: string[] | number[], selectRowRecord: GetMemberInspectUsersResponse["data"] ) => {
const target = selectRowRecord[0];
formActions.setFieldValue('byUserEditName', target?.name)
formActions.setFieldValue('byUserId', target?.userId)
formActions.setFieldValue('byUserEditPhone', target?.phone)
const disabled = target?.userId ? true : false
formActions.setFieldState('*(byUserEditName, byUserEditPhone)', (state) => {
state.props['x-component-props'].disabled = disabled;
})
setUserModalValue(selectRowRecord);
userModalToggle(false)
}
return (
<PageHeaderWrapper
onBack={() => history.goBack()}
backIcon={<ReutrnEle description="返回" />}
title={isEdit ? '修改投诉建议' : '新增投诉建议'}
extra={
(
<Button
type="primary"
icon={<SaveOutlined />}
loading={submitLoading}
onClick={() => formActions.submit()}
>
保存
</Button>
)
}
>
<Card>
<NiceForm
onSubmit={handleSubmit}
editable={true}
initialValues={formatedValue}
schema={complaintAddSchema}
actions={formActions}
components={{FormilyUploadFiles, FormilyRangeTime, FormilyCustomizeRadioButton}}
expressionScope={{
connectMember: (
<div onClick={() => toggle(true)}>
<LinkOutlined style={{marginRight: 4}}/>
选择
</div>
),
connectUser: (
<div onClick={() => userModalToggle(true)}>
<LinkOutlined style={{marginRight: 4}}/>
选择用户
</div>
)
}}
effects={() => {
onFormInputChange$().subscribe(() => {
if (!unsaved) {
setUnsaved(true);
}
});
}}
/>
</Card>
<TableModal
visible={visible}
onClose={() => toggle(false)}
title={"选择会员"}
columns={memberColumns}
schema={memberSchema}
onOk={handleOnOk}
fetchData={handleFetchData}
tableProps={{
rowKey: (record) => `${record.subMemberId}_${record.subRoleId}`,
}}
mode={"radio"}
value={memberModalValue}
/>
<TableModal
visible={userModalVisible}
onClose={() => userModalToggle(false)}
title={"选择用户"}
columns={userColumns}
schema={userSchema}
customizeRadio
onOk={handleUserOnOk}
fetchData={handleFetchUserData}
effects={($,actions) => {
useStateFilterSearchLinkageEffect($, actions, 'name', FORM_FILTER_PATH );
}}
tableProps={{
rowKey: 'userId',
}}
mode={"radio"}
value={userModalValue}
/>
<Prompt when={unsaved} message="您还有未保存的内容,是否确定要离开?" />
</PageHeaderWrapper>
)
}
export default SuggestAdd;
import React from 'react';
import { ColumnsType } from 'antd/es/table';
import { Link } from 'umi';
/**
* 列表页column
*/
const listColumns: ColumnsType<any> = [
{
title: '上级会员名称',
dataIndex: 'upperName',
},
{
title: '业务类型',
dataIndex: 'typeName',
filters: [
{
text: '投诉',
value: 1,
},
{
text: '建议',
value: 2
}
],
onFilter: (value, record) => record.type === value,
},
{
title: '事件分类',
dataIndex: 'classifyName',
},
{
title: '事件主题',
dataIndex: 'subject',
},
{
title: '事件时间',
dataIndex: 'eventTime'
},
{
title: '提出人',
dataIndex: 'byUserName'
},
{
title: '提出人电话',
dataIndex: 'byUserPhone'
},
{
title: '处理时间',
dataIndex: 'handleTime'
},
{
title: '状态',
dataIndex: 'statusName'
}
]
export const setColumnsByLinks = (link?: { [key: string]: string }) => {
const linksColumns: ColumnsType<any> = [
{
title: '序号',
dataIndex: 'id',
render: (text, record) => {
return (
<div style={{ display: 'flex', flexDirection: 'column', }}>
{
link?.['detail'] && (
<Link to={`${link?.['detail']}?id=${record.id}`}>{record.id}</Link>
) || (
<span>{record.id}</span>
)
}
</div>
)
}
},
]
return linksColumns.concat(listColumns);
}
// export default listColumns;
import React from 'react';
import { useMemo } from "react";
function useGetDetailCommon({ initialValue }) {
const basicInfo = useMemo(() => {
return [
{
title: '事件主题',
value: initialValue?.subject
},
{
title: "业务类型",
value: initialValue?.typeName
},
{
title: '事件时间',
value: initialValue?.eventTime,
},
{
title: '事件描述',
value: initialValue?.eventDesc
},
{
title: "会员名称",
value: initialValue?.name
},
{
title: "附件",
value: (
<div>
{
initialValue?.attachments?.map((_row) => {
return (
<a key={_row.url} href={_row.href}>{_row.name}</a>
)
})
}
</div>
)
},
{
title: "事件建议",
value: initialValue?.eventSuggest
},
{
title: "提出人",
span: 2,
value: initialValue?.byUserName
},
{
title: '事件分类',
value: initialValue?.classifyName
},
{
title:'提出人电话',
value: initialValue?.byUserPhone
}
]
}, [initialValue])
const resultInfo = useMemo(() => {
return [
{
title: '处理结果',
value: initialValue?.handleResult
},
{
title: '处理人',
value: initialValue?.handleUserName
},
{
title: "附件",
value: (
<div>
{
initialValue?.handleAttachments.map((_row) => {
return (
<a key={_row.url} href={_row.href}>{_row.name}</a>
)
})
}
</div>
)
},
{
title: '处理时间',
value: initialValue?.handleTime
},
{
title: "处理人电话",
value: initialValue?.handleUserPhone
}
]
}, [initialValue])
return { basicInfo, resultInfo };
}
export default useGetDetailCommon;
import { ISchema } from '@formily/antd';
export const complaintAddSchema: ISchema = {
type: "object",
properties: {
tabs: {
type: "object",
"x-component": "tab",
properties: {
"tab-1": {
type: "object",
"x-component": "tabpane",
"x-component-props": {
"tab": "基本信息"
},
properties: {
layout: {
type: 'object',
'x-component': 'mega-layout',
'x-component-props': {
labelCol: 4,
full: true,
wrapperCol: 10,
labelAlign: 'left',
},
properties: {
type: {
title: '业务类型',
type: 'object',
'x-component': 'FormilyCustomizeRadioButton',
enum: [
{ label: '投诉', value :1 },
{ label: '建议', value: 2 },
],
'x-rules': [
{
required: true,
message: '请选择业务类型'
}
]
},
classify: {
title: '事件分类',
type: 'string',
enum: [
{ label: '关于产品', value :1 },
{ label: '关于订单', value: 2 },
{ label: '关于配送', value: 3 },
{ label: '关于售后', value: 4 },
{ label: '关于服务', value: 5 },
{ label: '其他', value: 6 },
],
'x-rules': [
{
required: true,
message: '请选择事件分类'
}
]
},
subject: {
title: '事件主题',
type: 'string',
'x-rules': [
{
required: true,
message: '请填写事件主题'
},
{
limitByte: true, // 自定义校验规则
maxByte: 60
}
],
'x-component-props': {
placeholder: '最长60个字符,30个汉字'
}
},
memberName: {
title: '会员名称',
type: 'string',
"x-component-props": {
disabled: true,
addonAfter: "{{connectMember}}"
},
"x-rules": [
{
required: true,
message: '请选择会员名称'
}
],
},
memberId: {
title: "下级会员id",
type: 'string',
display: false
},
roleId: {
title: '下级会员角色Id',
type: 'string',
display: false,
},
eventTime: {
title: '事件时间',
type: "date",
'x-component-props': {
format: 'YYYY-MM-DD HH:mm:ss'
}
},
byUserEditName: {
type: 'string',
title: "提出人",
"x-component-props": {
addonAfter: "{{connectUser}}"
},
"x-rules": [
{
required: true,
message: '请填写提出人'
}
],
},
byUserId: {
title: '提出人id',
type: 'string',
display: false,
},
// phoneData: {
// type: 'string',
// title: '提出人电话',
// 'x-component': 'FormilyCountryPhone',
// enum: [
// {
// label: '中国',
// value: '+86'
// }
// ]
// },
byUserEditPhone: {
type: "string",
title: "提出人电话",
'x-component-props': {},
},
eventSuggest: {
title: '事件建议',
type: 'textarea',
"x-component-props": {
style: {
width: '100%'
}
},
'x-rules': [
{
limitByte: true, // 自定义校验规则
maxByte: 60
}
]
},
eventDesc: {
title: '事件描述',
type: 'textarea',
'x-rules': [
{
required: true,
message: "请填写事件描述",
},
{
limitByte: true, // 自定义校验规则
maxByte: 60
}
]
},
attachments: {
title: '考察要求附件',
type: 'object',
'x-component': 'FormilyUploadFiles',
}
}
},
}
},
}
}
}
}
import { ISchema } from '@formily/antd';
import { FORM_FILTER_PATH } from '@/formSchema/const';
/**
* 会员整改页scheam
*/
export const complaintAndSuggestListSchema: ISchema = {
type: 'object',
properties: {
mageLayout: {
type: 'object',
'x-component': 'Mega-Layout',
properties: {
topLayout: {
type: 'object',
'x-component': 'Mega-Layout',
'x-component-props': {
grid: true,
},
properties: {
ctl: {
type: 'object',
'x-component': 'Children',
'x-component-props': {
children: '{{controllerBtns}}',
},
},
name: {
type: 'string',
'x-component': 'Search',
'x-component-props': {
placeholder: '搜索',
tip: '输入 会员名称 进行搜索',
},
},
},
},
[FORM_FILTER_PATH]: {
type: 'object',
'x-component': 'Flex-Layout',
'x-component-props': {
colStyle: {
marginLeft: 20,
},
},
properties: {
subject: {
type: 'string',
'x-component-props': {
placeholder: '事件主题',
allowClear: true,
style: {
width: 160,
},
},
},
'[eventTimeStart, eventTimeEnd]': {
type: 'daterange',
default: undefined,
'x-component-props': {
placeholder: '会员等级(全部)',
allowClear: true,
style: {
width: 160,
},
},
},
submit: {
'x-component': 'Submit',
'x-mega-props': {
span: 1,
},
'x-component-props': {
children: '查询',
},
},
},
},
},
},
},
};
import React, { useCallback, useMemo, useState } from 'react';
import { Spin } from 'antd';
import AnchorPage from '@/layouts/AnchorPage';
import CustomizeColumn from '@/components/CustomizeColumn';
import { PublicApi } from '@/services/api';
import theme from '../../../../../config/lingxi.theme.config';
import useGetAnchorHeader from '../../complaintsAndSuggests/common/hooks/useGetAnchorHeader';
import useGetDetailCommon from './common/hooks/useGetDetailCommon';
import { usePageStatus } from '@/hooks/usePageStatus';
import useInitialValue from '../../common/hooks/useInitialValue';
import { GetMemberComplaintSubGetRequest, GetMemberComplaintSubGetResponse, } from '@/services/MemberV2Api';
const TobeEvaluateDetail = () => {
const { id } = usePageStatus();
const params = useMemo(() => { return id ? { id: id.toString() } : null }, [id]);
const { initialValue } = useInitialValue<GetMemberComplaintSubGetResponse, GetMemberComplaintSubGetRequest>(PublicApi.getMemberComplaintSubGet, params);
const { headers } = useGetAnchorHeader([], {initialValue: initialValue})
const { basicInfo, resultInfo } = useGetDetailCommon({initialValue})
return (
<Spin spinning={false}>
<AnchorPage
title={initialValue?.subject}
anchors={headers}
>
<CustomizeColumn id="detail" data={basicInfo} title="投诉建议信息" column={3} />
{
initialValue?.handleResult && (
<div style={{ margin: `${theme["@margin-md"]} 0` }}>
<CustomizeColumn id="result" data={resultInfo} title="处理结果信息" column={3} />
</div>
)
}
</AnchorPage>
</Spin>
)
}
export default TobeEvaluateDetail
import React, { useRef, useState } from 'react';
import { Card, Space, Button, Spin } from 'antd'
import { setColumnsByLinks } from './common/columns';
import { complaintAndSuggestListSchema } from './common/schema';
import useFetchList from '../../memberEvaluate/hooks/useFetchList';
import { PlusOutlined } from '@ant-design/icons';
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch';
import { PublicApi } from '@/services/api';
import CustomizeQueryList from '../../components/CustomizeQueryList';
import { Link } from 'umi';
import useColumns from '../../memberRectification/common/hooks/useColumns';
import { GetMemberComplaintSubPageRequest, GetMemberComplaintUpperPageRequest } from '@/services/MemberV2Api';
interface Iprops {};
const queryColumns = setColumnsByLinks({
detail: '/memberCenter/memberAbility/profile/suggestion/detail'
})
const List: React.FC<Iprops> = (props: Iprops) => {
const { fetchListData } = useFetchList();
const [ currentIdIsInLoading, setCurrentIdIsInLoading ] = useState<number[]>([]);
const ref = useRef<any>({})
const { columns } = useColumns(queryColumns, [
{
title: '操作',
render: (text, record) => {
if(!record.submitOrUpdateOrDelete) {
return null
}
const loading = currentIdIsInLoading.includes(record.id);
return (
<Space>
<Spin spinning={loading}>
<a onClick={() => handleSubmit({id: record.id}, "submit")}>提交投诉建议</a>
</Spin>
<Link to={`/memberCenter/memberAbility/profile/suggestion/edit?id=${record.id}`}>修改</Link>
<Spin spinning={loading}>
<a onClick={() => handleSubmit({id: record.id}, "delete")}>删除</a>
</Spin>
</Space>
)
}
}
])
const controllerBtns = (
<div>
<Link to={"/memberCenter/memberAbility/profile/suggestion/add"}>
<Button type="primary" icon={<PlusOutlined />}>新建</Button>
</Link>
</div>
)
const handleFetch = async (params: GetMemberComplaintSubPageRequest) => {
const result = fetchListData(PublicApi.getMemberComplaintSubPage, params);
return result
}
const handleSubmit = async (params: {id: number}, type: "submit" | "delete") => {
const newList = [...currentIdIsInLoading];
newList.push(params.id);
const service = type === 'submit' ? PublicApi.postMemberComplaintSubSubmit : PublicApi.postMemberComplaintSubDelete;
setCurrentIdIsInLoading(newList)
const { data, code } = await service(params);
setCurrentIdIsInLoading((prev) => prev.filter((_item) => _item !== params.id));
if (code === 1000) {
ref.current?.submit()
}
}
return (
<Card>
<CustomizeQueryList
ref={ref}
columns={columns}
schema={complaintAndSuggestListSchema}
fetchListData={handleFetch}
expressionScope={{
controllerBtns,
}}
effects={($, actions) => {
useStateFilterSearchLinkageEffect($, actions,'name', FORM_FILTER_PATH,);
// useAsyncSelect('status', fetchStatusOptions);
}}
/>
</Card>
)
}
export default List
......@@ -26,7 +26,7 @@ const listColumns: ColumnsType<any> = [
},
{
title: '整改结果',
dataIndex: 'result',
dataIndex: 'agreeResultName',
},
{
title: '外部状态',
......
......@@ -7,6 +7,10 @@ type OptionType = {
value: string
}
type Options = {
fetchStatusListApi: () => Promise<any>
}
/**
* 我分成每个模块都有一个column的hook, 合并action 列以及 状态列筛选列,这里请求方法直接写死算了
* 但其实可以把整个hook提取出来,整成公用的
......@@ -14,7 +18,7 @@ type OptionType = {
* @param actionColumn
* @returns
*/
export default function useColumns<T>(columnsList: ColumnsType<T>, actionColumn?: ColumnsType<T>, options?: { [key: string]: any } ) {
export default function useColumns<T>(columnsList: ColumnsType<T>, actionColumn?: ColumnsType<T>, options?: Options & { [key: string]: any } ) {
const [columns, setColumns] = useState(() => {
return columnsList.concat(actionColumn || []);
});
......
import { black } from "chalk";
import { useMemo, useState } from "react";
export default function useGetAnchorHeader(blackList: string[] = []) {
......@@ -26,9 +25,10 @@ export default function useGetAnchorHeader(blackList: string[] = []) {
}
]
})
const cacheHeaders = useMemo(() => {
return headers.filter((_item) => !blackList.includes(_item.key))
}, [blackList, headers])
return {headers: cacheHeaders};
return {headers: cacheHeaders, setHeaders};
}
......@@ -21,7 +21,7 @@ function useGetDetailCommon({ initialValue }: { initialValue: GetMemberRectifyWa
},
{
title: "会员名称",
value: initialValue?.name
value: initialValue?.name || (initialValue as any)?.upperMemberName
},
{
title: '整改要求',
......
import React, { useMemo } from 'react';
import React, { useEffect, useMemo } from 'react';
import { Spin, Card, Table, Button } from 'antd';
import AnchorPage from '@/layouts/AnchorPage';
import CustomizeColumn from '@/components/CustomizeColumn';
......@@ -9,17 +9,17 @@ import useGetAnchorHeader from '../common/hooks/useGetAnchorHeader';
import useGetDetailCommon from '../common/hooks/useGetDetailCommon';
import { usePageStatus } from '@/hooks/usePageStatus';
import useInitialValue from '@/pages/handling/common/hooks/useInitialValue';
import { GetMemberRectifyWaitAddGetResponse } from '@/services/MemberV2Api';
import { GetMemberRectifySummaryGetResponse } from '@/services/MemberV2Api';
import { findLastIndexFlowState } from '@/utils';
import FlowRecords from '@/components/FlowRecords';
import { innerColumns, outerColumns } from '../common/columns/historyColumn'
const rectificationAddDetail = () => {
const { headers } = useGetAnchorHeader(["editInfo", "result"])
const { headers, setHeaders } = useGetAnchorHeader()
const { id } = usePageStatus();
const params = useMemo(() => { return id ? { id: id.toString() } : null }, [id]);
const { loading, initialValue }= useInitialValue<GetMemberRectifyWaitAddGetResponse, { id: string }>(PublicApi.getMemberRectifyWaitAddGet, params)
const { basicInfo, editInfo, resultInfo } = useGetDetailCommon({initialValue})
const { loading, initialValue }= useInitialValue<GetMemberRectifySummaryGetResponse, { id: string }>(PublicApi.getMemberRectifySummaryGet, params)
const { basicInfo, editInfo, resultInfo } = useGetDetailCommon({initialValue: initialValue as any })
const outerVerifySteps = useMemo(() => {
if (!initialValue?.outerVerifySteps) {
......@@ -32,6 +32,24 @@ const rectificationAddDetail = () => {
status: (initialValue?.currentOuterStep > item.step ? 'finish' : 'wait') as 'finish' | 'wait',
}))
}, [initialValue])
useEffect(() => {
if (!initialValue) {
return;
}
const newHeaders = headers.filter((_item) => {
if (initialValue?.reportAttachments.length === 0 && _item.key === 'editInfo') {
return false
}
if (initialValue?.agreeResult === null && _item.key === 'result') {
return false
}
return true
})
setHeaders(newHeaders)
}, [initialValue])
return (
<Spin spinning={loading}>
<AnchorPage
......@@ -46,12 +64,20 @@ const rectificationAddDetail = () => {
<div id="basicInfo" style={{ margin: `${theme["@margin-md"]} 0` }}>
<CustomizeColumn data={basicInfo} title="基本信息" column={3} />
</div>
<div id="editInfo" style={{ margin: `${theme["@margin-md"]} 0` }}>
<CustomizeColumn data={editInfo} title="整改信息" column={1} />
</div>
<div id="result" style={{ margin: `${theme["@margin-md"]} 0` }}>
<CustomizeColumn data={resultInfo} title="整改结果" column={1} />
</div>
{
(initialValue?.reportDigest || initialValue?.reportAttachments.length > 0) && (
<div id="editInfo" style={{ margin: `${theme["@margin-md"]} 0` }}>
<CustomizeColumn data={editInfo} title="整改信息" column={1} />
</div>
)
}
{
initialValue?.agreeResult && (
<div id="result" style={{ margin: `${theme["@margin-md"]} 0` }}>
<CustomizeColumn data={resultInfo} title="整改结果" column={1} />
</div>
)
}
<div id="record">
<FlowRecords
innerRowkey="id"
......
......@@ -26,7 +26,7 @@ const TobeEvaluateDetail = () => {
const { headers } = useGetAnchorHeader(["editInfo", "result"])
const { id } = usePageStatus();
const params = useMemo(() => { return id ? { id: id.toString() } : null }, [id]);
const { loading, initialValue }= useInitialValue<GetMemberRectifyWaitAddGetResponse, { id: string }>(PublicApi.getMemberRectifyWaitAddGet, params)
const { loading, initialValue }= useInitialValue<GetMemberRectifyWaitAddGetResponse, { id: string }>(PublicApi.getMemberRectifyWaitConfirmGet, params)
const { basicInfo, editInfo, resultInfo } = useGetDetailCommon({initialValue});
const [submitLoading, setSubmitLoading] = useState<boolean>(false);
......
......@@ -25,6 +25,9 @@ const List: React.FC<Iprops> = (props: Iprops) => {
title: "操作",
render: (text, record) => {
const {sendOrUpdateOrDel} = record;
if (!record.confirm) {
return null
}
return (
<Space>
<Link to={`/memberCenter/memberAbility/memberRectification/tobeConfirmRectification/detail?id=${record.id}`}>确认</Link>
......
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