import * as React from 'react'
import Header from '../Header/Header'
import Aside from '../Aside/Aside'
import { HashRouter as Router, Route, Switch, Redirect } from 'react-router-dom'
import * as styles from './Layout.module.less'
import { Breadcrumb, Modal, Drawer, Button,Carousel, Checkbox } from 'antd'
import actions from 'src/store/modules/common/actions'
import actionsLogin from 'src/store/modules/login/actions'
import actionsAttr from 'src/store/modules/attrManage/actions'
import attrManageApi from 'src/service/attrManageApi'
import routes from '../../../router'
import { IMenuInfo } from '../../../types'
import {
FIX_USER,
FIX_TEST,
FIX_COMMENT,
FIX_DOC,
CONTACT_US,
FIX_CONTACT_US,
FIX_GUIDE_CLOSE,
} from 'src/utils/icons'
import Feedback from 'src/pages/Dashboard/Feedback'
import AuthManage from 'src/pages/AuthManage'
import OptLog from 'src/pages/OptLog'
import { connect } from 'react-redux'
import TestWindow from 'src/pages/TestWindow'
import {
isConfirmUserAgreement,
confirmUserAgreement,
getIsTip,
noviceguideAdd,
} from 'src/service'
import contactus_erweima from 'src/assets/images/contactus-erweima.png'
import { bindActionCreators } from 'redux'
import commonApi from 'src/service/commonApi'
import page1 from 'src/assets/images/giude/page-1.png'
import page2 from 'src/assets/images/giude/page-2.png'
import page3 from 'src/assets/images/giude/page-3.png'
import page4 from 'src/assets/images/giude/page-4.png'
import page5 from 'src/assets/images/giude/page-5.png'
import page6 from 'src/assets/images/giude/page-6.png'
import page7 from 'src/assets/images/giude/page-7.png'
import page8 from 'src/assets/images/giude/page-8.png'
interface IProps {
primaryFlag:number
actions: typeof actions
actionsLogin: typeof actionsLogin
actionsAttr: typeof actionsAttr
menuDisplay: boolean
authUrls: any[],
functions: any[],
}
/**
* 用户协议,做少展示秒数
*/
const maxSecond = 5
/**
* 引导浮层信息
*/
const guideList = [
{
url:page1,
title:'苏宁客服机器人迎来全新改版啦!',
content:`新版机器人新增智能催拍功能,提高店铺销售转化;
增加智能辅助功能,提供人工客服工作效率;高级问答的自助功能,减少用户配置,问答更高效;
问题发现和问法推荐,降低商家运维成本;增强了知识库答案配置功能,满足不同场景问答需求;
完善了数据看板内容,丰富了数据展示形式;增加机器人待办事项提醒功能,提供商家运维效率。
机器人管理后台页面布局更加优化,功能使用更加便捷!`,
},
{
url:page2,
title:'智能催拍:',
content:`针对有购买意愿的买家咨询未但下单,
通过客服机器人自动计算出符合催拍条件的用户,在黄金时间内发起催拍,
用特定营销话术、商品卖点、优惠券等,促成用户下单,帮助店铺提升转化率,减少人工催拍投入成本。
机器人管理后台页面布局更加优化,功能使用更加便捷!`,
},
{
url:page3,
title:'智能辅助:',
content:`人工客服使用机器人智能辅助功能,
由机器人在线实时为人工客服推荐答案,人工客服可对推荐答案二次编辑或直接采纳回复用户,
减少了人工坐席查找答案、组织话术的过程,释放了人工压力、提高了效率。`,
},
{
url:page4,
title:'自助功能:',
content:`机器人自助功能是高级问答能力,
自助功能能够根据用户编码、商品、订单信息完成自助查询、操作等功能,比如查询物流、申请发票、修改送货信息等。
不同于简答的一问一答,自助功能通过引导式交互进行多轮对话,提高用户问题的解决效率。`,
},
{
url:page5,
title:'问题发现和问法推荐:',
content:`问题发现和问法推荐:为了提升机器人应答能力,
降低机器人配置门槛,算法将自动从店铺实时数据中挖掘知识点会话语料,补充到店铺知识点,
以帮助机器人主动成长。PS:店铺主动参与确认才能提升保证数据优化功能生效,实现机器人在应答中自动提升。`,
},
{
url:page6,
title:'知识库配置:',
content:`问答能力是sunny机器人解决用户问题的基础能力,
其数据来源是知识点库提供的,知识库的数据量越多,数据质量越高,机器人识别的越准确,问答能力也越强,
知识库分为行业高频问题和自定义问题,行业高频问题是由算法基于海量买家问题训练得出,您只需要配置相应答案即可,
自定义问题是未被行业高频问题覆盖的买家问题,您可以自定义添加问法和答案,
由于行业高频问题已覆盖了线上绝大多数的用户问法,能极大的降低您的知识配置成本。`,
},
{
url:page7,
title:'数据看板:',
content:`数据看板是sunny机器人提供的数据功能,
将机器人与用户的对话进行回流,展示机器人的接待情况,询单情况以及各功能的使用情况,
方便店铺通过数据的回流反馈指导店铺运营,降低店铺的运营成本,提高运营效率。`,
},
{
url:page8,
title:'待办事项:',
content:`根据店铺使用情况智能生成的店铺任务单,
及时提醒店铺需关注维护的重要功能,主动跟进运维人员的事务完成情况。在机器人管理后台首页位置,
突出显示日常待办事项,引导运维人员优化机器人运维数据,提升机器人整体能力。`,
},
]
class Layout extends React.Component<IProps> {
state = {
/**
* 意见反馈弹框
*/
showFeedbackDialog: false,
/**
* 用户协议弹框
*/
showAgreementDialog: false,
/**
* 用户协议 - 当前剩余秒数
*/
second: maxSecond,
/**
* 成员管理弹框
*/
memberManageVisible: false,
/**
* 测试窗弹框
*/
showTestWindow: false,
/**
* 引导卡片悬浮层
*/
guideMaskVisible: false,
/**
* 新手引导当前页码
*/
guideStep: 0,
/**
* 引导卡片不在提醒
*/
guideNotAlert:false,
showLogDialog: false,
}
carouselRef:any = React.createRef<any>()
componentDidMount() {
/**
* 逻辑:
* 用户协议弹框优先级大于引导浮层,所以需要先校验用户协议是否已签署
* 1.如果已签署,在查看引导浮层是否需要提示
* 2.如果未签署,则显示用户协议弹框,在用户协议弹框确认操作之后在校验是否需要显示引导浮层弹框
*/
this.getConfirmUserAgreementState()
this.getProductUsable()
this.setAttrList()
}
setAttrList = async ()=>{
const attrsList = await attrManageApi.queryParameterInfo({skuCodeList:[]})
this.props.actionsAttr.updateAttrsList(attrsList)
}
componentWillReceiveProps(newProps: any){
//控制按钮权限
this.setAuth(newProps.functions)
}
setAuth = (functions: any[])=>{
setTimeout(()=>{
this.setBtn(functions)
}, 500)
window.addEventListener('hashchange',()=>{
setTimeout(()=>{
this.setBtn(functions)
}, 500)
})
}
setBtn = (functions: any[])=>{
//针对 antd button
let btnF = document.getElementsByClassName('ant-btn')
for(let i=0;i<btnF.length;i++){
if(btnF[i].getAttribute('data-code-type')){
if(functions.includes(btnF[i].getAttribute('data-code-type'))){
btnF[i].removeAttribute('disabled')
}else{
btnF[i].setAttribute('disabled', 'true')
}
}
}
const btnS = document.getElementsByClassName('ant-switch')
for(let i=0;i<btnS.length;i++){
if(btnS[i].getAttribute('data-code-type')){
if(functions.includes(btnS[i].getAttribute('data-code-type'))){
btnS[i].removeAttribute('disabled')
}else{
btnS[i].setAttribute('disabled', 'true')
}
}
}
// 针对添加的 class
const btnC = document.getElementsByClassName('dataCodeType')
console.log(btnC)
for(let i=0;i<btnC.length;i++){
if(btnC[i].getAttribute('data-code-type')){
if(functions.includes(btnC[i].getAttribute('data-code-type'))){
btnC[i].classList.remove('disabledStyCli')
}else{
btnC[i].classList.add('disabledStyCli')
}
}
}
}
getProductUsable = async () => {
const result = await commonApi.productUsable()
this.props.actions.getProductUsable(result)
}
/**
* 获取用户协议是否已签署
*/
getConfirmUserAgreementState = async () => {
const result = await isConfirmUserAgreement()
if (!result) {
this.setState({
showAgreementDialog: true,
})
this.updateUserAgreementTimes()
} else {
this.getGuideState()
}
}
/**
* 更新用户协议时间
*/
updateUserAgreementTimes = () => {
setTimeout(() => {
let second = this.state.second
second--
this.setState({
second,
})
this.updateUserAgreementTimes()
}, 1000)
}
/**
* 获取新手引导是否显示
*/
getGuideState = async () => {
const result = await getIsTip()
if (result.isTip === 1) {
this.setState({
guideMaskVisible: true,
})
}
}
/**
* 设置新手引导已读
*/
guideAdd = async (isTip:number) => {
if(isTip === 0){
const params = {
isTip,
}
await noviceguideAdd(params)
}
this.setState({
guideMaskVisible: false,
})
}
/**
* 关闭引导浮层
*/
colseGuide = () => {
this.guideAdd(this.state.guideNotAlert?0:1)
}
/**
* 面包屑点击事件
* @param url 需要跳转的url
* @param props 当前面包屑所在页面的props
*/
routeUrl = (url: string, props: any) => {
if (url && url !== props.location.pathname) {
props.history.push(url)
}
}
/**
* 弹框关闭通用方法
* @param sign
*/
handleCancel = (sign: string) => {
this.setState({
[sign]: false,
})
}
/**
* 意见反馈弹框确认事件
* @param form
*/
submitFeedback = (form: object) => {
}
/**
* 右下角悬浮按钮点击事件
* @param sign
*/
fixBtnClick = (sign: string) => {
this.setState({
[sign]: true,
})
}
/**
* 用户协议 - 确认
*/
handleAgreementOk = async () => {
const params = {
status: 1,
}
await confirmUserAgreement(params)
this.handleAgreementCancle()
// 检查是否需要显示引导浮层
this.getGuideState()
}
/**
* 用户协议 - 取消
*/
handleAgreementCancle = (() => {
this.setState({
showAgreementDialog: false,
})
})
/**
* 成员管理 - 弹框
*/
toggleMemberManage = (flag: boolean) => {
this.setState({
memberManageVisible: flag,
})
}
/**
* 引导卡片切换
* @param current
*/
carouselChange = (from:number,current:number)=>{
this.setState({
guideStep:current,
})
console.log('carouselChange',current)
}
/**
* 引导卡片 - 不再提醒
*/
notAlert = (e:any)=>{
const checked = e.target.checked
this.setState({
guideNotAlert:checked,
})
console.log('notAlert',checked)
}
carouselProv = ()=>{
this.carouselRef.current.prev()
}
carouselNext = ()=>{
this.carouselRef.current.next()
}
render() {
const { menuDisplay, authUrls } = this.props
const {
showFeedbackDialog,
showAgreementDialog,
second,
memberManageVisible,
showTestWindow,
guideMaskVisible,
guideStep,
showLogDialog,
} = this.state
// 拼接面包屑
const getBreadcrumb = (item: IMenuInfo, props: any) => {
if (item.meta && item.meta.breadcrumb && item.meta.breadcrumb.length > 0) {
const breadcrumb = item.meta.breadcrumb
const breadcrumbUrl = item.meta.breadcrumbUrl || []
return (
<Breadcrumb separator="/" className="line-space">
{
breadcrumb.map((bread, index) => {
const url = breadcrumbUrl[index] || ''
return <Breadcrumb.Item onClick={() => { this.routeUrl(url, props) }} key={index}>{bread}</Breadcrumb.Item>
})
}
</Breadcrumb>
)
}
return null
}
return (
<>
<Router>
<Header />
<div className={`${styles.wrap} contentbox`}>
{menuDisplay && <Aside />}
<div id='router' className={styles.mainContainer}>
<Switch>
{routes.map((item: IMenuInfo) => {
if (item.children && item.children.length > 0) {
if(authUrls.length && authUrls.includes(item.path))
return item.children.map((subItem: IMenuInfo) => {
if(authUrls.length && authUrls.includes(item.path + subItem.path))
return <Route path={item.path + subItem.path} render={(props) => {
return (
<>
{getBreadcrumb(subItem, props)}
<subItem.component {...props} />
</>
)
}} key={item.path + subItem.path} />
})
} else {
if(authUrls.length && authUrls.includes(item.path))
return <Route path={item.path} render={(props) => {
return (
<>
{getBreadcrumb(item, props)}
<item.component {...props} />
</>
)
}} key={item.path} />
}
})}
{/* <Redirect to={routes[0].path} /> */}
{authUrls.length && (
<Redirect to={authUrls[0]} />
)}
</Switch>
</div>
</div>
</Router>
{/* 右下角悬浮按钮 */}
<div className={styles.fixbtns}>
{
this.props.primaryFlag === 1&&(
<div className={styles.fixitem} onClick={()=>{this.toggleMemberManage(true)}}>
<span className={styles.title}>角色管理</span>
<img src={FIX_USER} alt="" />
</div>
)
}
<div className={styles.fixitem} onClick={() => this.fixBtnClick('showLogDialog')}>
<span className={styles.title}>操作日志</span>
<img src={FIX_DOC} alt="" />
</div>
<div className={styles.fixitem} style={{display:'none'}} onClick={() => { this.fixBtnClick('showFeedbackDialog') }}>
<span className={styles.title}>意见反馈</span>
<img src={FIX_COMMENT} alt="" />
</div>
<div className={styles.fixitem}>
<span className={styles.title}>联系我们</span>
<img src={FIX_CONTACT_US} alt="" />
<div className={styles.contactus}>
<div className={`${styles.header} line-space`}>
<img src={CONTACT_US} alt="" className={styles.headerpic} />
<span className={`${styles.contactustitle} ellipsis`}>【机器人一群】商户/供应…</span>
</div>
<img src={contactus_erweima} alt="" className={`${styles.erweima} line-space-sm`} />
<div className={`line-space-sm`}>群号:103384984</div>
<div className={`line-space`}>
使用“豆芽”扫描二维码<br />
在群里@17122346来联系我们吧
</div>
<div>豆芽号:17122346</div>
</div>
</div>
<div className={styles.fixitem} onClick={() => { this.fixBtnClick('showTestWindow') }}>
<span className={styles.title}>测试窗</span>
<img src={FIX_TEST} alt="" />
</div>
</div>
{/* 意见反馈 */}
<Feedback
showDialog={showFeedbackDialog}
submitFeedback={this.submitFeedback}
handleCancel={() => { this.handleCancel('showFeedbackDialog') }}
/>
{/* 用户协议 */}
<Modal
title="苏宁客服机器人定制服务项目知情同意书"
visible={showAgreementDialog}
onCancel={this.handleAgreementCancle}
destroyOnClose
width="640px"
maskClosable={false}
keyboard={false}
closable={false}
maskStyle={{ background: '#858585' }}
footer={
<Button
onClick={this.handleAgreementOk}
type={second > 0 ? 'default' : 'primary'}
disabled={second > 0}
>
{`确定${second > 0 ? `(${second}s)` : ''}`}
</Button>
}
>
<div className={styles.agreement}>
<p>
在您同意本知情同意书之前,请您务必仔细阅读全部内容,特别是以粗体标注的内容条款。
若您对本知情同意书的条款有疑问的,可通过相关服务渠道进行咨询、了解。
若您无法准确理解或不同意本知情同意书的任何内容,请停止使用智能客服并不要进行后续操作。
<br />
<br />
当您通过网络页面点击本知情同意书底部的“同意”按钮或以其他方式选择接受知情同意书,
即表示您已充分阅读、理解并同意接受本知情同意书的全部内容。
本知情同意书自您点击“同意”或以其他方式选择接受知情同意书之时起生效。
<br />
<br />
您作为入驻苏宁云台的商家,现自愿加入苏宁客服机器人产品定制服务项目,
为用户提供客户咨询解答服务。本项目前向您免费开放,后期如须收取费用,
苏宁将另外通知和公示收费标准。届时,您可以选择是否付费使用。
<br />
<br />
您同意基于客服机器人产品升级的需要,向苏宁提供您所开设店铺与用户的历史云信聊天记录信息数据,
苏宁可使用该数据进行机器人语料分析和算法训练,以提升机器人的问题解决能力。
以上信息不单独识别用户个人身份,苏宁将严格按照《苏宁易购隐私政策》的各项规定保护用户个人信息。
<br />
<br />
您理解,您开通使用本服务后,客服机器人可能会自动答复用户咨询。
您认可客服机器人无法完全替代人工客服,在实际应用中可能存在语意理解偏差等情况,
若因此造成用户误解或者产生相应纠纷,由您承担并负责妥善解决客诉。同时,
您愿与苏宁共同就发现的问题积极寻求解决方案,不断优化完善客服机器人产品功能。
<br />
<br />
您承诺对本服务项目及因本服务项目获取的所有相关信息负有保密义务,
承诺不以任何方式在参与项目需要范围外使用、传播、向公众或第三方披露上述信息。
如有违反,将按照《苏宁云台服务协议》及平台相关规则承担相应法律责任。
</p>
</div>
</Modal>
{/* 成员管理 */}
<Drawer
visible={memberManageVisible}
onClose={() => { this.toggleMemberManage(false) }}
width="520px"
title="员工权限配置"
>
<AuthManage />
</Drawer>
{/* 测试窗 */}
{showTestWindow && <TestWindow handleCancel={() => this.handleCancel('showTestWindow')} />}
{/* 引导浮层 */}
{
guideMaskVisible && (
<div className={styles.guidemask}>
<div className={styles.content}>
<Carousel
dotPosition="bottom"
className={styles.carousel}
dots={{className:styles.dots}}
beforeChange={this.carouselChange}
ref={this.carouselRef}
>
{
guideList && guideList.map((item,index:number)=>{
return (
<div key={index}>
{
guideStep === index&&<img src={item.url} alt="" className={styles.guideimg} />
}
{
guideStep !== index&&<img src="" alt="" className={styles.guideimg} />
}
<div className={styles.description}>
<span className={styles.bold}>{item.title}</span>
{
item.content
}
</div>
</div>
)
})
}
</Carousel>
<div className={styles.footer}>
<Button size="small" disabled={guideStep === 0} onClick={()=>{this.carouselProv()}}>上一页</Button>
{
guideStep!==7&&(
<Button size="small" className={styles.btn} onClick={()=>{this.carouselNext()}}>下一页</Button>
)
}
{
guideStep===7&&(
<Button size="small" className={styles.btn} onClick={()=>{this.guideAdd(0)}}>去使用</Button>
)
}
</div>
<img src={FIX_GUIDE_CLOSE} className={styles.guideclose} onClick={() => { this.colseGuide() }} alt="" />
<div className={styles.notalert}>
<Checkbox onChange={this.notAlert} className={styles.cbx} />不再提醒
</div>
</div>
</div>
)
}
<Modal
title="操作日志"
visible={showLogDialog}
onCancel={()=>this.handleCancel('showLogDialog')}
destroyOnClose
width="980px"
footer={null}
>
<OptLog />
</Modal>
</>
)
}
}
export default connect(
(state: any) => {
return {
menuDisplay: state.commonReducer.menuDisplay,
primaryFlag: state.login.primaryFlag,
authUrls: state.login.authUrls,
functions: state.login.functions,
}
},
dispatch => {
return {
actions: bindActionCreators(actions, dispatch),
actionsLogin: bindActionCreators(actionsLogin, dispatch),
actionsAttr: bindActionCreators(actionsAttr, dispatch),
}
}
)(Layout)
react根目录下App中引用构成上左右三部分的页面
最新推荐文章于 2022-11-16 15:20:58 发布