Commit e7ff07bc authored by baixian's avatar baixian

优化

parent 733ae8a3
......@@ -140,6 +140,20 @@ export default {
path: '/sjd/datacenter',
relativePath: ['/sjd/datacenter'],
},
{
id: '60',
name: '潜客管理',
key: 60,
isFather: false,
style: {
width: '20px',
height: '20px',
},
activeurl: `${__IMGCDN__}menu/crmactive.png`,
notactiveurl: `${__IMGCDN__}menu/crm.png`,
path: '/sjd/potential',
relativePath: ['/sjd/potential'],
},
{
id: '30',
name: '在线课堂',
......
......@@ -114,7 +114,7 @@ class SjdMenu extends React.Component {
<UserGuide />
<div>
{/* <div className={collapsed ? SjdMenuStyle.collapsedlogo : SjdMenuStyle.uncollapsedlogo} onClick={this.goHome} /> */}
<div className={SjdMenuStyle.uncollapsedlogo} onClick={this.goHome} >
<div className={SjdMenuStyle.uncollapsedlogo}>
{
collapsed === false &&
<Icon
......
import { routerRedux } from 'dva/router';
import { message } from 'antd';
import { delay } from 'redux-saga';
import moment from 'moment';
import {
LocalStorage,
SessionStorage,
isExpired,
getRandomFilename,
} from '../utils/index';
import * as classRecordAjax from '../services/classrecord';
import errorcode from '../common/errorcode';
import * as schedulemgtAjax from '../services/schedulemgt';
export default {
namespace: 'crm',
state: {
},
subscriptions: {
setup({ dispatch, history }) { // eslint-disable-line
},
},
effects: {
* selectRecord({ payload }, { call, put, select }) {
const { params } = payload;
const { sid, schoolUserInfo } = yield select(state => state.webapp);
const { recordParams, recordTotal } = yield select(state => state.classrecord);
const loading = message.loading('数据加载中...', 0.5);
// let newParams;
const newParams = Object.assign(recordParams, params, {
school_id: sid,
// teacher_id: schoolUserInfo.id,
my_class: LocalStorage.getItem('role') == 1 ? 1 : '',
});
let newTotal = recordTotal;
const data = yield call(classRecordAjax.getRecordList, {
...newParams,
end_time: params.end_time ? params.end_time : moment().format('YYYY-MM-DD HH:mm:ss'),
});
setTimeout(loading);
if (data.code == 200) {
if (data.data.total != undefined) {
newTotal = data.data.total;
}
yield put({
type: 'updateState',
payload: {
recordList: data.data && data.data.list,
recordTotal: newTotal,
recordParams: { ...newParams },
datetime: data.datetime,
},
});
} else {
yield put({
type: 'webapp/errorrequestresolve',
payload: {
data,
datetime: data.datetime,
},
});
}
},
* goAddCrm({ payload }, { put, select }) {
yield put(routerRedux.push({
pathname: '/sjd/addpotential',
}));
},
* pageInit({ payload }, { call, put, select }) {
yield put({
type: 'updateState',
payload: {
teacherList: [],
},
});
},
},
reducers: {
save(state, action) {
return { ...state, ...action.payload };
},
updateState(state, action) {
return { ...state, ...action.payload };
},
},
};
......@@ -51,6 +51,7 @@ import deployschool from './deployschool';
import permissionsetting from './permissionsetting';
import personmanage from './personmanage';
import rolemanage from './rolemanage';
import crm from './crm';
export default {
loginModel,
indexstaicModel,
......@@ -96,4 +97,5 @@ export default {
permissionsetting,
personmanage,
rolemanage,
crm,
};
......@@ -863,6 +863,34 @@ export default {
},
});
}
if (pathname === '/sjd/potential') {
dispatch({
type: 'updateState',
payload: {
breadcrumbList: [{
path: pathname,
name: '潜客管理',
}],
},
});
}
if (pathname === '/sjd/addpotential') {
dispatch({
type: 'updateState',
payload: {
breadcrumbList: [
{
path: 'sjd/potential',
name: '潜客管理',
},
{
path: pathname,
name: '添加潜在学员',
},
],
},
});
}
if (pathname === '/sjd/singleclass') {
dispatch({
type: 'onlineclasses/pageInit',
......
import React from 'react';
import { connect } from 'dva';
import {
Button,
Row,
Col,
Modal,
Icon,
DatePicker,
Divider,
Form,
Input,
Card,
Select,
Table,
Pagination,
Tag,
Badge,
Tooltip,
message,
Radio,
} from 'antd';
import moment from 'moment';
import pageStyle from './AddCrm.less';
import { pageIn, hasBtnPower, DayCount, pricify } from '../../utils/index';
const { Option } = Select;
const FormItem = Form.Item;
const { RangePicker } = DatePicker;
const { TextArea } = Input;
let mobileId = 0;
class AddCrmForm extends React.Component {
constructor(props) {
super(props);
this.state = {
age: 0,
};
}
componentDidMount() { // 挂载
pageIn('添加潜在学员');
const { dispatch } = this.props;
dispatch({
type: 'webapp/querymemberinfo',
});
}
componentDidUpdate() {
}
componentWillUnmount() { // 卸载
}
save = (e) => {
const { dispatch, form } = this.props;
e.preventDefault();
this.props.form.validateFields((err, values) => {
// console.log(values, 'values');
if (!err) {
console.log(values, 'values');
}
});
}
disabledDate = (current) => {
return current && current > moment();
}
saveCallStudents = () => {
const { dispatch } = this.props;
dispatch({
type: 'classrecord/selectRecord',
payload: {
params: {},
},
});
}
addMobile = () => {
const { form } = this.props;
const keys = form.getFieldValue('mobileList');
const nextKeys = keys.concat(mobileId++);
form.setFieldsValue({
mobileList: nextKeys,
});
}
removeMobile = (k) => {
const { form } = this.props;
const keys = form.getFieldValue('mobileList');
form.setFieldsValue({
mobileList: keys.filter(key => key !== k),
});
};
handleChangeAge = (dateString) => {
if (!dateString) {
this.state.age = 0;
return;
}
const toyear = new Date().getFullYear() + 1;
this.state.age = toyear - dateString.format('YYYY') || 0;
}
render() {
const {
form: { getFieldDecorator, getFieldValue },
} = this.props;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 3 },
md: { span: 3 },
lg: { span: 3 },
xl: { span: 2 },
xxl: { span: 2 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 21 },
md: { span: 21 },
lg: { span: 21 },
xl: { span: 22 },
xxl: { span: 21 },
},
};
getFieldDecorator('mobileList', { initialValue: [] });
const mobileList = getFieldValue('mobileList');
const formItems = mobileList.map((k, index) => (
<Form.Item
{...formItemLayout}
label="&nbsp;"
required={false}
key={k}
colon={false}
>
{getFieldDecorator(`add[${k}]`, {
validateTrigger: ['onChange', 'onBlur'],
rules: [
{
required: true,
whitespace: true,
message: '请填写联系方式',
},
],
})(<Input placeholder="请填写联系方式" style={{ width: 260, marginRight: 8 }} />)}
{mobileList.length > 0 ? (
<Icon
className="dynamic-delete-button"
type="minus-circle-o"
onClick={() => this.removeMobile(k)}
/>
) : null}
</Form.Item>
));
const { age } = this.state;
return (
<div className={pageStyle.container}>
<div className={pageStyle.headerWrap}>
<div className={pageStyle.commonTitle}>基本信息</div>
<Form labelAlign="right" onSubmit={this.save}>
<FormItem {...formItemLayout} label="学员姓名">
{getFieldDecorator('status', {
initialValue: '',
rules: [{ required: true, message: '学员姓名不能为空' }],
})(
<Input style={{ width: 260 }} placeholder="请填写学员姓名" />,
)}
</FormItem>
<FormItem {...formItemLayout} label="联系方式">
{getFieldDecorator('mobile', {
initialValue: '',
})(
<Input style={{ width: 260 }} placeholder="请填写联系方式" />,
)}
<span className={pageStyle.addMobile} onClick={this.addMobile}>新增</span>
<p className={pageStyle.mobileTip}>此号码用于接收上课与预约通知相关短信</p>
</FormItem>
{formItems}
<FormItem {...formItemLayout} label="生日日期">
{getFieldDecorator('status', {
initialValue: '',
})(
<DatePicker disabledDate={this.disabledDate} onChange={this.handleChangeAge} allowClear style={{ width: 134 }} />,
)}
<span style={{ marginLeft: 12 }}>年龄:{age}</span>
</FormItem>
<FormItem {...formItemLayout} label="备用电话">
{getFieldDecorator('mobile', {
initialValue: '',
})(
<Input style={{ width: 260 }} placeholder="请输入手机号码" />,
)}
</FormItem>
<FormItem {...formItemLayout} label="备注">
{getFieldDecorator('mobile', {
initialValue: '',
})(
<TextArea rows={4} style={{ width: 260 }} placeholder="请输入备注" />,
)}
</FormItem>
<div className={pageStyle.commonTitle}>售卖信息</div>
<FormItem {...formItemLayout} label="来源渠道">
{getFieldDecorator('status', {
initialValue: '',
})(
<Select style={{ width: 260 }} placeholder="请选择">
<Option value={1}>在线购买</Option>
<Option value={2}>在线招生</Option>
<Option value={3}>来电咨询</Option>
<Option value={4}>在线购买</Option>
<Option value={5}>地推</Option>
<Option value={6}>外呼电话</Option>
<Option value={7}>转介绍</Option>
<Option value={8}>客户直访</Option>
<Option value={9}>其他</Option>
</Select>,
)}
</FormItem>
<FormItem {...formItemLayout}>
<Button>取消</Button>
<Button type="primary" htmlType="submit">确定</Button>
</FormItem>
</Form>
</div>
</div>
);
}
}
const AddCrm = Form.create()(AddCrmForm);
AddCrmForm.propTypes = {
};
function mapStateToProps(state) {
const {
teacherList,
classList,
roomList,
courseList,
recordList,
recordParams,
recordTotal,
callStudentListLoading,
callStudentTotal,
callStudentList,
callDetailShow,
queryCallStudentListParams,
datetime,
} = state.classrecord;
const { schoolUserInfo } = state.webapp;
return {
teacherList,
classList,
roomList,
courseList,
recordList,
recordParams,
recordTotal,
callStudentListLoading,
callStudentTotal,
callStudentList,
callDetailShow,
queryCallStudentListParams,
schoolUserInfo,
datetime,
};
}
export default connect(mapStateToProps)(AddCrm);
@import '../../less/variables.less';
.container {
background-color: #fff;
border-radius: 2px;
padding:11px 20px;
}
.headerWrap {
.commonTitle {
font-size:16px;
font-weight:500;
color:rgba(104,104,104,1);
line-height:24px;
margin-bottom: 10px;
}
:global {
.ant-form-item {
margin-bottom: 10px;
}
}
}
.addMobile {
font-size:13px;
font-family:PingFangSC-Regular,PingFang SC;
font-weight:400;
color:rgba(24,144,255,1);
line-height:18px;
cursor: pointer;
display: inline-block;
margin-left: 10px;
}
.mobileTip {
font-size:12px;
font-weight:400;
color:rgba(153,153,153,1);
line-height:17px;
margin-bottom: 0;
}
This diff is collapsed.
@import '../../less/variables.less';
.container {
background-color: #fff;
border-radius: 2px;
padding: 20px;
}
.headerbox{
background-color: #fff;
.headerbtn {
margin: 0 16px 0 0;
}
padding-bottom: 18px;
}
.searchrow {
.formitem {
display: flex;
align-items: center;
margin-bottom: 16px;
.formitemlabel {
color: #000000;
font-size: 14px;
line-height: 1;
min-width: 70px;
white-space: nowrap;
}
}
}
.selectitem {
display: block;
}
.searchbtnbox {
height: 54px;
display: flex;
align-items: flex-end;
}
.resetbtn {
margin-right: 16px;
}
.tablebox {
background: #fff;
:global {
.ant-table-fixed {
border-bottom: 1px solid #E8E8E8 !important;
}
.ant-table tbody tr:nth-child(2n) {
background-color: #FBFBFB;
}
.ant-table-thead > tr > th, .ant-table-tbody > tr > td {
padding: 11px 10px;
}
.ant-table-bordered .ant-table-tbody tr td {
border-bottom: none;
border-right: none!important;
}
.ant-table-bordered .ant-table-thead tr th {
border-bottom: none;
border-right: none!important;
}
.ant-table-bordered .ant-table-thead tr th:last-child {
border-right: 1px solid #e8e8e8!important;
}
.ant-table-bordered .ant-table-tbody tr td:last-child {
border-right: 1px solid #e8e8e8!important;
}
.ant-table-bordered .ant-table-tbody tr:last-child {
border-bottom: 1px solid #e8e8e8!important;
}
}
}
.divideline {
color: #E9E9E9;
padding: 0 8px;
}
.alink {
color: #1890FF;
}
.classNamebox {
max-width: 250px;
word-break: break-all;
color: #1890FF;
cursor: pointer;
// background-color: #fff;
}
.tableoperatebox {
min-width: 160px;
line-height: 30px;
}
.endsearchcol {
margin-bottom: 18px;
}
.endclassfooter {
display: flex;
align-items: center;
justify-content: space-between;
}
.classroomitem {
margin-bottom: 5px;
.classroom {
color:rgba(0,0,0,0.85);
}
.aLink {
color: #1890FF;
padding-left: 21px;
}
}
.activeamount{
background-color: #cccccc;
color: #666666;
text-align: center;
line-height: 50px;
font-size: 20px;
}
.rightList {
display: flex;
align-items: center;
height: 32px;
.resetIcon {
width: 14px;
height: 14px;
cursor: pointer;
margin-right: 15px;
}
.expend {
font-size:13px;
font-family:PingFangSC-Regular,PingFang SC;
font-weight:400;
color:rgba(102,102,102,1);
display: inline-block;
cursor: pointer;
}
}
.tablefooterbox {
color: rgba(0,0,0,0.6);
font-size: 16px;
line-height: 50px;
display: flex;
background-color: #fff;
align-items: center;
justify-content: space-between;
padding-left: 14px;
padding-right: 14px;
margin-top: 10px;
.tablefooterstatic {
color:rgba(0,0,0,0.65);
font-size: 14px;
}
}
.tabList {
display: flex;
border-bottom: 1px solid #E8E8E8;
margin-bottom: 18px;
.tabItem {
font-size:13px;
font-weight:400;
color:rgba(102,102,102,1);
line-height:18px;
padding: 0 30px 10px;
cursor: pointer;
position: relative;
&:hover {
font-weight:600;
color:#1890FF;
}
}
.tabItemActive {
font-size:13px;
font-weight:600;
color:#1890FF;
line-height:18px;
padding: 0 30px 10px;
cursor: pointer;
position: relative;
font-family:PingFangSC-Semibold,PingFang SC;
&:after {
content: '';
display: block;
width: 30%;
height: 2px;
background-color: #1890FF;
position: absolute;
bottom: 0;
left: 35%;
}
}
}
......@@ -236,6 +236,16 @@ const PermissionSetting = props => (
{ PermissionSetting => (<PermissionSetting {...props} />) }
</Bundle>
);
const Crm = props => (
<Bundle load={() => import(/* webpackChunkName:"Crm" */'./pages/crm/index')}>
{ Crm => (<Crm {...props} />) }
</Bundle>
);
const AddCrm = props => (
<Bundle load={() => import(/* webpackChunkName:"AddCrm" */'./pages/crm/AddCrm')}>
{ AddCrm => (<AddCrm {...props} />) }
</Bundle>
);
function RouterConfig({ history }) {
return (
<ConfigProvider locale={zhCN}>
......@@ -278,6 +288,8 @@ function RouterConfig({ history }) {
<Route path="/sjd/singleclass/detail/:id" exact component={SingleDetail} />
<Route path="/sjd/record" exact component={ClassRecord} />
<Route path="/sjd/permissionsetting/:id" exact component={PermissionSetting} />
<Route path="/sjd/potential" exact component={Crm} />
<Route path="/sjd/addpotential" exact component={AddCrm} />
<Route component={Errorpage} />
</Switch>
</SjdIndex>
......
import qs from 'qs';
import request from '../utils/request';
import api from '../common/api';
export function courseList(params) {
const querystring = qs.stringify(params);
return request({
url: `${api.courses.courses}?${querystring}`,
method: 'GET',
});
}
export function courseAdd(params) {
const data = qs.stringify(params);
return request({
url: `${api.courses.courses}`,
method: 'POST',
data,
});
}
export function courseDetail(params) {
const data = qs.stringify(params);
return request({
url: `${api.courses.courses}/${params.id}?${data}`,
method: 'GET',
data,
});
}
export function coursePut(params) {
const data = qs.stringify(params);
return request({
url: `${api.courses.courses}/${params.id}`,
method: 'PUT',
data,
});
}
export function courseDelete(params) {
const data = qs.stringify(params);
return request({
url: `${api.courses.courses}/${params.id}`,
method: 'DELETE',
data,
});
}
export function coursesBuyOrRenew(params) {
const data = qs.stringify(params);
return request({
url: `${api.courses.coursesBuyOrRenew}`,
method: 'POST',
data,
});
}
export function coursesExit(params) {
const data = qs.stringify(params);
return request({
url: `${api.courses.coursesExit}`,
method: 'POST',
data,
});
}
export function coursesChange(params) {
const data = qs.stringify(params);
return request({
url: `${api.courses.coursesChange}`,
method: 'POST',
data,
});
}
export function coursesEliminate(params) {
const data = qs.stringify(params);
return request({
url: `${api.courses.coursesEliminate}`,
method: 'POST',
data,
});
}
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