Commit cca0d6c9 by guomingpang
parents 64f4b1f6 4bb97c9a
......@@ -22,7 +22,7 @@
"@typescript-eslint/eslint-plugin": "^2.10.0",
"@typescript-eslint/parser": "^2.10.0",
"ali-oss": "^6.12.0",
"antd": "^4.9.4",
"antd": "^4.16.3",
"array-move": "^3.0.1",
"axios": "^0.20.0",
"babel-eslint": "10.1.0",
......@@ -34,7 +34,7 @@
"bizcharts": "^3.3.0",
"camelcase": "^5.3.1",
"case-sensitive-paths-webpack-plugin": "2.3.0",
"classnames": "^2.2.6",
"classnames": "^2.3.1",
"cropper": "^3.1.4",
"cross-env": "^7.0.3",
"css-loader": "3.4.2",
......
......@@ -7,11 +7,12 @@
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosPromise, AxiosError } from 'axios';
import { message } from 'antd';
import { message, Modal } from 'antd';
import { BASIC_HOST, TIME_OUT, USER_TYPE, VERSION, PROJECT } from '@/domains/basic-domain/constants';
import User from './user';
import { content } from 'html2canvas/dist/types/css/property-descriptors/content';
interface FetchParams {
url: string,
......@@ -27,7 +28,8 @@ interface HeadersType{
storeId?:any,
storeUserId?:any,
userId?:any,
xmtoken?:any
xmtoken?:any,
enterpriseId?:string|null
}
class Axios {
static post(
......@@ -51,6 +53,9 @@ class Axios {
if(User.getToken()){
headerObject.xmtoken = User.getToken();
}
if (User.getEnterpriseId()) {
headerObject.enterpriseId = User.getEnterpriseId();
}
const instance: AxiosInstance = axios.create({
timeout: TIME_OUT,
responseType: 'json',
......@@ -84,8 +89,14 @@ class Axios {
})
instance.interceptors.response.use((response: AxiosResponse): AxiosResponse | AxiosPromise => {
const { message: ResMessage, success, resultMsg, resultCode,code} = response.data;
if (success || resultCode === 0) {
const { message: ResMessage, success, resultMsg, code: resultCode } = response.data;
if (resultCode === "CROP_DEPLOY_PAST_BETTER") {
Modal.warning({
title:"服务已到期",
content: "当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买",
okText: "我知道了"
})
} else if (success || resultCode === 0) {
return response;
} else if (!options.reject) {
message.error(ResMessage || resultMsg);
......
......@@ -12,6 +12,10 @@ import { PREFIX, USER_PREFIX } from '@/domains/basic-domain/constants';
declare var window:any;
class User {
getExpirationTime() {
return Storage.get(`${PREFIX}_expiration_time`)
}
getVersion() {
return Storage.getObj(`${PREFIX}_version`)
}
......@@ -56,8 +60,16 @@ class User {
return Storage.get(`${PREFIX}_isAdmin`);
}
setStoreId(value: any) {
return Storage.set(`${PREFIX}_storeId`, value);
setExpirationTime(value:number) {
return Storage.set(`${PREFIX}_expiration_time`,value)
}
setVersion(value:any) {
return Storage.setObj(`${PREFIX}_version`,value)
}
setStoreId(value:any){
return Storage.set(`${PREFIX}_storeId`,value)
}
setEnterpriseId(value: any) {
......
......@@ -11,3 +11,6 @@
padding: 16px;
background-color: #F0F2F5;
}
.ant-tooltip-inner {
max-width: 1000px;
}
\ No newline at end of file
.ant-popover .ant-popover-content .ant-popover-inner {
box-shadow: 0px 2px 20px 0px rgba(0, 0, 0, 0.06);
.ant-popover-inner-content {
padding: 0;
}
}
.contact-widget {
width: 276px;
height: 294px;
overflow: hidden;
background-color: white;
background-image: url(https://image.xiaomaiketang.com/xm/CZ4a752jzi.png);
background-repeat: no-repeat;
background-size: cover;
text-align: center;
font-size: 14px;
font-weight: 400;
color: #333333;
line-height: 22px;
.qrcode {
width: 182px;
height: 204px;
background-color: white;
margin: 28px auto 16px auto;
img {
width: 150px;
height: 150px;
margin: 16px 16px 8px 16px;
}
}
}
\ No newline at end of file
import React, { ReactElement } from "react";
import { Popover } from "antd";
import { TooltipPlacement } from "antd/lib/tooltip";
import { ActionType } from "rc-trigger/lib/interface";
import "./ContactWidget.less"
interface ContactWidgetProps {
placement: TooltipPlacement
children: React.ReactElement
visible?: boolean
trigger: ActionType | ActionType[]
}
function Content() {
return (
<div className="contact-widget">
<div className="qrcode">
<img src="https://cdn.xiaomai5.com/qixueyuankehu.png" alt=""></img>
<div className="des">微信/企业微信扫码咨询</div>
</div>
<div className="phone"><svg style={{position:"relative",top:"2px",marginRight:"4px"}} viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M512.651 3.78c-281.433 0-509.21 228.324-509.21 509.209 0 281.43 228.325 509.203 509.21 509.203 281.427 0 509.202-228.317 509.202-509.203 0.55-280.885-227.775-509.21-509.202-509.21z m198.205 743.553c-36.14 36.136-169.737 1.641-302.24-130.312-131.953-131.959-165.902-266.104-129.768-301.695 31.211-31.21 68.99-85.417 125.939-14.782 56.943 70.629 29.016 90.34-3.291 122.647-22.449 22.448 24.642 79.392 73.37 128.125 49.283 48.73 105.678 95.818 128.126 73.368 32.306-32.305 52.017-60.23 122.646-3.288 71.182 56.949 16.426 95.276-14.782 125.937z" p-id="4409" fill="#999999"></path></svg>
咨询电话:19157875632</div>
</div>
)
}
export default function ContactWidget(props:ContactWidgetProps) {
return <Popover
placement={props.placement}
arrowPointAtCenter
content={Content}
visible={props.visible}
trigger={props.trigger}
>
{props.children}
</Popover>
}
\ No newline at end of file
......@@ -12,6 +12,7 @@
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
min-height: 100%;
overflow: hidden;
.page {
position: fixed;
top: 60px;
......@@ -21,6 +22,7 @@
z-index: 102;
overflow: auto;
margin: 0 16px;
.box {
&:first-child {
margin-bottom: 8px;
......
......@@ -7,7 +7,9 @@
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import Service from "@/common/js/service";
import User from "@/common/js/user";
import axios from 'axios';
export function sendLoginAuthCode(params: object) {
return Service.Hades("anon/hades/sendLoginAuthCode", params);
}
......@@ -47,6 +49,9 @@ export function getEnterpriseUser(params: object) {
export function getWXWorkLoginNoCheck(params: object) {
return Service.Hades('anon/hades/getWXWorkLoginNoCheck', params);
}
export function getLesseeVersionMsg() {
return Service.Hades("public/customerHades/getLesseeVersionMsg",{enterpriseId:User.getEnterpriseId()})
}
export function getYoZoSign(params: object) {
return Service.Apollo('public/apollo/getYoZoSign', params);
......
......@@ -7,6 +7,7 @@
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import Service from "@/common/js/service";
import User from "@/common/js/user"
export function getEmployeeList(params: object) {
return Service.Hades("public/hades/getStoreUserPage", params);
......@@ -76,3 +77,4 @@ export function getStoreDetail(params: object) {
}
......@@ -7,7 +7,7 @@
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import { getUserStore, getUserPermission ,logout,getStoreUser,sendBizAuthCode,editUserPhone,checkBizAuthCode,sendNewPhoneAuthCode,sendLoginAuthCode,login,getLastedVersion, getEnterpriseUser,getWXWorkLoginNoCheck,getYoZoSign,saveYoZoFileVersionId,yoZoUpload} from '@/data-source/base/request-apis';
import { getUserStore, getUserPermission ,logout,getStoreUser,sendBizAuthCode,editUserPhone,checkBizAuthCode,sendNewPhoneAuthCode,sendLoginAuthCode,login,getLastedVersion, getEnterpriseUser,getWXWorkLoginNoCheck,getLesseeVersionMsg,getYoZoSign,saveYoZoFileVersionId,yoZoUpload} from '@/data-source/base/request-apis';
export default class StoreService {
// 获取员工列表
......@@ -55,6 +55,10 @@ export default class StoreService {
static getWXWorkLoginNoCheck(params: any){
return getWXWorkLoginNoCheck(params);
}
//获取企业配置的版本信息
static getLesseeVersionMsg() {
return getLesseeVersionMsg();
}
static getYoZoSign(params:any){
return new Promise((resolve) => {
getYoZoSign(params).then(res => {
......
......@@ -15,6 +15,7 @@ import college from '@/common/lottie/college.json';
import StoreService from "@/domains/store-domain/storeService";
import EmployeeAddOrEditModal from "../store-manage/EmployeeAddOrEditModal";
import User from "@/common/js/user";
import LimitTip from "./LimitTip";
import "./EmployeeManage.less";
import ChooseMembersModal from "./modal/ChooseMembersModal";
......@@ -68,6 +69,7 @@ function EmployeeManage() {
});
const [total, setTotal] = useState(0);
const [realTotal, setRealTotal] = useState(0)
const [model, setModel] = useState<React.ReactNode>(null);
const [employeeModal, setEmployeeModal] = useState(false);
const [choosedItem, setChooseItem] = useState<ChoosedItemType>({
......@@ -85,6 +87,17 @@ function EmployeeManage() {
if (!User.getEnterpriseId()) {
window.RCHistory.replace('/employees-manage');
}
//获取员工数
const _query = {
current: 0,
size: 10,
nickName: "",
phone: "",
roleCodes: [],
}
StoreService.getEmployeeList(_query).then((res: any) => {
setRealTotal(res.result.total);
});
}, [])
useEffect(() => {
......@@ -362,6 +375,7 @@ function EmployeeManage() {
</Button>
}
</div>
<LimitTip type="员工" total={realTotal} tip={()=>{return (<div>数据为当前学院的员工数,若员工存在多个学院,企业人数只统计为1人</div>)}}/>
<div className="box-body">
<XMTable
renderEmpty={{
......
.limit-tip {
background: #E9EFFF;
border-radius: 2px;
margin-bottom: 13px;
.always {
display: inline-block;
font-size: 14px;
line-height: 32px;
font-weight: 400;
color: #666666;
margin-left: 16px;
.renew-text {
display: inline-block;
color: #2966FF;
cursor: pointer;
}
}
}
\ No newline at end of file
import React, { useContext, useEffect, useState } from "react";
import { Tooltip } from "antd"
import { VersionContext } from "@/store/context";
import ContactWidget from "@/components/ContactWidget";
import "./LimitTip.less"
export default function LimitTip(props:{total:number,type:string,tip:() => React.ReactNode}) {
const [isOver, setIsOver] = useState(false)
const [limitUser, setLimitUser] = useState("0")
const versionInfo = useContext(VersionContext)
useEffect(()=> {
if (versionInfo) {
setIsOver(versionInfo.userNum === -1 ? false : versionInfo.whetherReachUserNum)
setLimitUser(versionInfo.userNum === -1 ? "不限人数" : String(versionInfo.userNum))
}
},[versionInfo])
return (
<div className="limit-tip">
<div className="always">本学院{props.type}<span style={{color:"#333333",fontWeight:"bold"}}>{props.total}</span>
<Tooltip overlayStyle={{maxWidth:"587px",width:"fit-content"}} placement="topLeft" arrowPointAtCenter title={props.tip}>
<span className="icon iconfont" style={{cursor:"pointer",marginLeft:"4px",color:"#bfbfbf"}}>&#59449;</span>
</Tooltip>
{
isOver ? (
<>
<div style={{marginLeft:"14px",display:"inline-block"}}>当前企业使用人数已达到上限 (<span style={{color:"#333333",fontWeight:"bold"}}>{limitUser}</span>人),将无法添加新员工、新学员,如需增加人数限制,请联系小麦企学院服务平台。</div>
<ContactWidget trigger="hover" placement="bottom">
<div className="renew-text">立即续费<span className="icon iconfont" style={{fontSize:"10px"}}>&#59291;</span></div>
</ContactWidget>
</>
) : ("")
}
</div>
</div>
)
}
\ No newline at end of file
......@@ -17,4 +17,5 @@
margin-left: 4px;
}
}
}
\ No newline at end of file
......@@ -15,6 +15,8 @@ import { Input, DatePicker, Select, Button, message } from "antd";
import StoreService from "@/domains/store-domain/storeService";
import User from "@/common/js/user";
import ChooseMembersModal from "./modal/ChooseMembersModal";
import LimitTip from "./LimitTip"
import { XMTable } from '@/components';
import college from '@/common/lottie/college.json';
import "./UserManagePage.less";
......@@ -26,6 +28,8 @@ const { RangePicker } = DatePicker;
declare var window: any;
function UserManagePage() {
const [userList, setUserList] = useState([]);
const [model, setModel] = useState<React.ReactNode>(null);
......@@ -39,11 +43,25 @@ function UserManagePage() {
sourceEnum: undefined,
});
const [total, setTotal] = useState(0);
const [realTotal, setRealTotal] = useState(0)
useEffect(() => {
if (!User.getEnterpriseId()) {
window.RCHistory.replace('/user-manage');
}
//获取学员数
const _query = {
current: 0,
size: 10,
nickName: "",
phone: "",
registerBegin: null,
registerEnd: null,
sourceEnum: undefined,
}
StoreService.getUserList(_query).then((res: any) => {
setRealTotal(res.result.total);
});
}, [])
useEffect(() => {
......@@ -125,9 +143,6 @@ function UserManagePage() {
<div className="header-item">
<span className="item-name">搜索学员:</span>
<Search
style={{
width: 300,
}}
placeholder="搜索学员姓名/手机号"
onSearch={(value) => {
const _query = { ...query };
......@@ -199,6 +214,7 @@ function UserManagePage() {
}}
>添加学员</Button>
}
<LimitTip type="学员" total={realTotal} tip={()=>{ return (<div><div>1、数据为当前学院的员工数,若学员存在多个学院,企业人数只统计为1人;</div><div>2、若一个学员既用「企业微信」登录学习又用「微信」登录学习,企业人数将统计为2人。</div></div>)}}/>
<div className="box-body">
<XMTable
renderEmpty={{
......
......@@ -285,6 +285,10 @@ class ChooseMembersModal extends React.Component {
visible={isOpen}
onCancel={() => this.handleClose()}
onOk={() => {
if (User.getVersion() && User.getVersion().whetherReachUserNum) {
message.error("添加失败,企业使用人数超出限制")
return
}
if (_.isEmpty(selectUserList)) {
message.warning(type === 'USER' ? '请选择员工' : '请选择学员')
return null;
......
......@@ -269,6 +269,15 @@ handleChangeBasicInfo = (field, value) => {
// 完成创建/编辑
handleSubmit = () => {
//过期判断
if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) {
Modal.warning({
title:"服务已到期",
content: "当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买",
okText: "我知道了"
})
return
}
const { addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo, id, isEdit, type } = this.state;
const {liveDate, timeHorizonStart} = addLiveClassInfo;
const _liveDate = moment(liveDate).format("YYYY-MM-DD");
......
......@@ -22,6 +22,13 @@ class LiveCoursePage extends React.Component {
componentWillMount() {
this.handleFetchLiveList(this.state.query);
}
changeShelfState = (index, shelfState) => {
const { courseList } = this.state;
courseList[index].shelfState = shelfState;
this.setState({
courseList,
});
};
// 获取直播课列表
handleFetchLiveList = (_query) => {
const { query } = this.state;
......@@ -54,6 +61,7 @@ class LiveCoursePage extends React.Component {
total={total}
courseList={courseList}
onChange={this.handleFetchLiveList}
changeShelfState={this.changeShelfState}
/>
</div>
</div>
......
......@@ -93,15 +93,13 @@ class LiveCourseList extends React.Component {
};
//改变上架状态
changeShelfState = (item) => {
let _shelfState = item.shelfState;
if (_shelfState === 'NO') {
_shelfState = 'YES';
item.shelfState = 'YES';
} else {
_shelfState = 'NO';
item.shelfState = 'NO';
}
changeShelfState = (index,item,checked) => {
let _shelfState = checked ? "YES" : "NO"
// if (_shelfState === 'NO') {
// _shelfState = 'YES';
// } else {
// _shelfState = 'NO';
// }
const params = {
liveCourseId: item.liveCourseId,
shelfState: _shelfState,
......@@ -113,6 +111,7 @@ class LiveCourseList extends React.Component {
} else {
message.success('已取消展示');
}
this.props.changeShelfState(index,_shelfState)
}
});
};
......@@ -288,7 +287,10 @@ class LiveCourseList extends React.Component {
key: 'shelfState',
dataIndex: 'shelfState',
render: (val, item, index) => {
return <Switch defaultChecked={item.shelfState === 'YES' ? true : false} onChange={() => this.changeShelfState(item)} />;
return <Switch
checked={item.shelfState === "YES"}
defaultChecked={item.shelfState === 'YES' ? true : false}
onChange={(checked) => this.changeShelfState(index,item,checked)} />;
},
},
{
......
......@@ -10,7 +10,7 @@
import React from 'react';
import { Button, Input, Radio, message, Modal, Cascader } from 'antd';
import $ from 'jquery';
import moment from 'moment';
import { DISK_MAP, FileTypeIcon, FileVerifyMap } from '@/common/constants/academic/lessonEnum';
import { ImgCutModalNew } from '@/components';
import ShowTips from '@/components/ShowTips';
......@@ -390,6 +390,15 @@ class AddGraphicsCourse extends React.Component {
// 保存
handleSubmit = () => {
//过期判断
if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) {
Modal.warning({
title:"服务已到期",
content: "当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买",
okText: "我知道了"
})
return
}
const { id, coverId, pageType, courseName, courseMedia, introduce, categoryId, shelfState, whetherVisitorsJoin } = this.state;
const commonParams = {
......
......@@ -144,7 +144,12 @@ class GraphicsCourseList extends React.Component {
width: 120,
dataIndex: 'courseware',
render: (val, item, index) => {
return <Switch defaultChecked={item.shelfState === 'YES' ? true : false} onChange={() => this.changeShelfState(item)} />;
return (
<Switch
checked={item.shelfState === "YES"}
defaultChecked={item.shelfState==="YES"?true:false}
onChange={(checked)=>this.changeShelfState(index,item,checked)}/>
)
},
},
{
......@@ -300,19 +305,17 @@ class GraphicsCourseList extends React.Component {
删除
</div>
</div>
);
};
//改变上架状态
changeShelfState = (item) => {
let _shelfState = item.shelfState;
if (_shelfState === 'NO') {
_shelfState = 'YES';
item.shelfState = 'YES';
} else {
_shelfState = 'NO';
item.shelfState = 'NO';
)
}
const params = {
//改变上架状态
changeShelfState = (index,item,checked) =>{
let _shelfState = checked ? "YES" : "NO"
// if(_shelfState==='NO'){
// _shelfState = "YES";
// }else{
// _shelfState = "NO"
// }
const params={
courseId: item.id,
shelfState: _shelfState,
};
......@@ -323,6 +326,7 @@ class GraphicsCourseList extends React.Component {
} else {
message.success('已取消展示');
}
this.props.changeShelfState(index,_shelfState)
}
});
};
......
......@@ -27,6 +27,13 @@ class GraphicsCourse extends React.Component {
this.handleFetchScheduleList();
}
changeShelfState = (index, shelfState) => {
const { dataSource } = this.state;
dataSource[index].shelfState = shelfState;
this.setState({
dataSource,
});
};
// 获取视频课列表
handleFetchScheduleList = (_query = {}) => {
const query = {
......@@ -75,6 +82,7 @@ class GraphicsCourse extends React.Component {
dataSource={dataSource}
totalCount={totalCount}
onChange={this.handleFetchScheduleList}
changeShelfState={this.changeShelfState}
/>
</div>
</div>
......
......@@ -466,6 +466,15 @@ class AddOfflineCourse extends React.Component {
};
preSubmit = () => {
//过期判断
if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) {
Modal.warning({
title:"服务已到期",
content: "当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买",
okText: "我知道了"
})
return
}
const { courseId } = this.state;
if (courseId) {
this.checkDetail(courseId).then(bool => bool ? this.handleSubmit() : message.warning('课程已开始,无法继续编辑'))
......
......@@ -14,7 +14,7 @@ import { DISK_MAP, FileTypeIcon, FileVerifyMap } from '@/common/constants/academ
import { ImgCutModalNew } from '@/components'
import ShowTips from '@/components/ShowTips'
import Breadcrumbs from '@/components/Breadcrumbs'
import moment from 'moment'
import AddVideoIntro from './components/AddVideoIntro'
import SelectStudent from '../modal/select-student'
import SelectPrepareFileModal from '../../prepare-lesson/modal/SelectPrepareFileModal'
......@@ -321,6 +321,15 @@ class AddVideoCourse extends React.Component {
// 保存
handleSubmit = () => {
//过期判断
if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) {
Modal.warning({
title:"服务已到期",
content: "当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买",
okText: "我知道了"
})
return
}
const { instId, adminId } = window.currentUserInstInfo
const {
......
......@@ -2,34 +2,35 @@
// padding: 0 16px 16px;
min-width: 1100px;
position: relative;
z-index:3;
z-index: 3;
.g2-tooltip-marker {
border-radius: 50% !important;
}
.home-title {
color: #333;
padding-left:28px;
font-size:16px;
font-weight:bold;
padding-left: 28px;
font-size: 16px;
font-weight: bold;
position: relative;
padding-top:16px;
&::before{
width:4px;
height:12px;
content:'';
background-image: linear-gradient(#2966FF 83.5%, #0ACCA4 16.5%);
display:inline-block;
padding-top: 16px;
&::before {
width: 4px;
height: 12px;
content: '';
background-image: linear-gradient(#2966ff 83.5%, #0acca4 16.5%);
display: inline-block;
position: absolute;
left:16px;
top:22px;
left: 16px;
top: 22px;
}
}
@font-face {
font-family: 'number';
src: url('https://image.xiaomaiketang.com/xm/n2sADd2jY6.TTF');
}
.data-wrap{
background: #FFF;
.data-wrap {
background: #fff;
padding-bottom: 25px;
.data-box {
display: flex;
justify-content: space-between;
......@@ -41,7 +42,7 @@
width: ~'calc(16.67% - 8px)';
padding: 16px;
&.course-data {
width: ~'calc(50% - 24px)';
width: 33%;
}
.header {
display: flex;
......@@ -55,7 +56,7 @@
font-size: 14px;
line-height: 18px;
color: #333;
font-weight:500;
font-weight: 500;
}
}
.data-number {
......@@ -74,7 +75,7 @@
.iconfont {
font-size: 12px;
margin-right: 4px;
color: #EC4B35;
color: #ec4b35;
}
.footer-number {
font-size: 12px;
......@@ -83,17 +84,17 @@
}
.course-box {
border-radius: 4px;
background: #FAFAFA;
background: #fafafa;
height: 124px;
width: 66%;
width: 60%;
position: absolute;
right: 16px;
top: 16px;
padding: 8px 24px 0;
top: 28px;
padding: 8px 0 0;
.course-item {
display: inline-block;
width: 50%;
padding: 4px 0 12px;
padding: 4px 24px 12px;
.course-title {
font-size: 12px;
color: #999;
......@@ -105,6 +106,7 @@
font-size: 16px;
font-family: 'number';
margin-right: 16px;
white-space: nowrap;
}
.course-word {
font-size: 12px;
......@@ -114,11 +116,12 @@
.iconfont {
font-size: 12px;
margin-right: 4px;
color: #EC4B35;
color: #ec4b35;
}
.add-number {
font-size: 12px;
color: #999;
white-space: nowrap;
}
}
}
......@@ -126,9 +129,9 @@
}
}
}
.study-wrap{
background: #FFF;
margin-top:16px;
.study-wrap {
background: #fff;
margin-top: 16px;
.study-box {
display: flex;
justify-content: space-between;
......@@ -148,7 +151,7 @@
margin-bottom: 12px;
.iconfont {
font-size: 14px;
color: #BFBFBF;
color: #bfbfbf;
margin-left: 4px;
}
.tip {
......@@ -169,13 +172,13 @@
color: #666;
cursor: pointer;
&.selected {
color: #2966FF;
color: #2966ff;
&::after {
position: absolute;
width: 24px;
height: 2px;
content: '';
background: #2966FF;
background: #2966ff;
border-radius: 1px;
left: 9px;
bottom: -4px;
......@@ -196,7 +199,7 @@
display: flex;
align-items: center;
&.odd {
background: #FAFAFA;
background: #fafafa;
}
.table-image {
width: 24px;
......@@ -257,7 +260,7 @@
width: 204px;
height: 204px;
border-radius: 102px;
background: #F1F3F6;
background: #f1f3f6;
margin-top: -20px;
.small-circle {
display: flex;
......@@ -280,13 +283,13 @@
&.unfinished {
top: 152px;
.spot {
background: #2966FF;
background: #2966ff;
}
}
&.finished {
top: 232px;
.spot {
background: #FFBB54;
background: #ffbb54;
}
}
.spot {
......@@ -352,14 +355,14 @@
align-items: center;
color: #666;
.student-dot {
background: #2966FF;
background: #2966ff;
height: 8px;
width: 8px;
border-radius: 50%;
margin-right: 8px;
}
.time-dot {
background: #FEB613;
background: #feb613;
height: 8px;
width: 8px;
border-radius: 50%;
......@@ -382,7 +385,8 @@
font-size: 14px;
}
.g2-tooltip-list {
li,span {
li,
span {
display: flex;
align-items: center;
font-size: 14px;
......
.home-tip {
.tip {
height: 40px;
background: #FFE7E7;
margin-bottom: 16px;
.content {
font-size: 14px;
color: #666666;
font-weight: 400;
line-height: 40px;
padding-left: 16px;
.renew-btn {
display: inline-block;
width: 80px;
height: 28px;
background: #FF4F4F;
border-radius: 2px;
color: #ffffff;
font-size: 14px;
font-weight: 400;
line-height: 28px;
text-align: center;
cursor: pointer;
margin-left: 8px;
}
}
}
}
\ No newline at end of file
import React, { useContext, useEffect, useState, version } from "react";
import "./HomeTip.less"
import { VersionContext } from "@/store/context";
import ContactWidget from '@/components/ContactWidget';
import { Carousel } from "antd";
import moment from "moment";
export default function HomeTip() {
const [isOverNum, setIsOverNum] = useState<boolean>(false)
const [tipType, setTipType] = useState(0) //0不显示1即将过期2已过期
const [expirationTime, setExpirationTime] = useState("")
const [surplusDay, setSurplusDay] = useState(0)
const versionInfo = useContext(VersionContext)
useEffect(()=> {
if (versionInfo) {
setIsOverNum(versionInfo.userNum === -1 ? false : versionInfo.whetherReachUserNum)
setSurplusDay(versionInfo.surplusDayTime)
setExpirationTime(versionInfo.validEndTime)
if (versionInfo.stateEnum === "NO") {
setTipType(2)
} else if (versionInfo.surplusDayTime === 30 || versionInfo.surplusDayTime <= 7) {
setTipType(1)
}
}
},[versionInfo])
return (
<div className="home-tip">
{
(isOverNum || tipType !== 0) &&
<div className="tip">
<Carousel dotPosition="left" dots={false} autoplay={true} autoplaySpeed={5000}>
{
isOverNum && (
<div className="content">
<span className="icon iconfont" style={{color:"#FF4F4F",marginRight:"8px"}}>&#xe61d;</span>温馨提示:企业使用人数已达上限,将无法新增员工、学员,如需增加人数限制,请联系小麦企学院服务平台。
<ContactWidget placement="bottom" trigger="hover"><div className="renew-btn">立即续费</div></ContactWidget>
</div>
)
}
{
tipType === 2 && (
<div className="content">
<span className="icon iconfont" style={{color:"#FF4F4F",marginRight:"8px"}}>&#xe61d;</span>版本到期提醒:当前企业购买的小麦企学院服务已于{moment(versionInfo?.validEndTimeST).format("YYYY-MM-DD HH:mm:ss")}到期,到期后仍可访问,但功能不可使用,建议尽快续费购买哦~
<ContactWidget placement="bottom" trigger="hover"><div className="renew-btn">立即续费</div></ContactWidget>
</div>
)
}
{
tipType === 1 && (
<div className="content">
<span className="icon iconfont" style={{color:"#FF4F4F",marginRight:"8px"}}>&#xe61d;</span>当前企业购买的小麦企学院服务仅剩{surplusDay}天(于{expirationTime}到期),为了不影响使用,建议尽快续费购买哦~
<ContactWidget placement="bottom" trigger="hover"><div className="renew-btn">立即续费</div></ContactWidget>
</div>
)
}
</Carousel>
</div>
}
</div>
)
}
\ No newline at end of file
......@@ -14,7 +14,8 @@ import Main from './Main'
import zhCN from 'antd/es/locale/zh_CN'
import User from '@/common/js/user';
import BaseService from "@/domains/basic-domain/baseService";
import { XMContext } from '@/store/context';
import moment from 'moment';
import { VersionContext, VersionInfo, XMContext } from '@/store/context';
import { setStoreGroupPermission, setStorePermission, setStoreGroupList, setStoreList } from '@/store/actions/index';
import Service from "@/common/js/service";
import Bus from '@/core/tbus';
......@@ -27,6 +28,7 @@ declare var window: any;
const App: React.FC = (props: any) => {
const [storeUserId, setStoreUserId] = useState('')
const ctx: any = useContext(XMContext);
const [versionInfo, setVersionInfo] = useState<VersionInfo|null>(null)
const userId = User.getUserId();
const [menuType, setMenuType] = useState(true);
const enterpriseId = User.getEnterpriseId();
......@@ -35,6 +37,7 @@ const App: React.FC = (props: any) => {
useEffect(() => {
getStoreAndUserInfo();
getVersion();
if (window.location.hash === "#/") {
window.RCHistory.replace({
pathname: '/home',
......@@ -58,6 +61,26 @@ const App: React.FC = (props: any) => {
}
});
}
function getVersion() {
BaseService.getLesseeVersionMsg().then((res) => {
let version = res.result;
User.setVersion(version);
User.setExpirationTime(res.result.validEndTime)
let versioninfo:VersionInfo = {
dayTime: version.dayTime,
stateEnum: version.stateEnum,
userNum: version.userNum === -1 ? '不限人数' : version.userNum,
surplusUserNum: version.userNum === -1 ? '不限人数' : version.surplusUserNum,
surplusDayTime: version.surplusDayTime,
validEndTime: moment(version.validEndTime).format('YYYY-MM-DD'),
validStartTime: moment(version.validStartTime).format('YYYY-MM-DD'),
validEndTimeST: version.validEndTime,
validStartTimeST: version.validStartTime,
whetherReachUserNum: version.whetherReachUserNum,
};
setVersionInfo(versioninfo)
});
}
async function getStoreAndUserInfo() {
await (enterpriseId ? getStoreInfo() : getStoreGroupAndStoreList());
......@@ -147,10 +170,13 @@ const App: React.FC = (props: any) => {
</Layout>
</Layout> */}
<Header id="app" handleMenuType={handleMenuType} menuType={menuType} />
<VersionContext.Provider value={versionInfo}>
<ConfigProvider locale={zhCN} autoInsertSpaceInButton={false}>
<Main menuType={menuType} />
</ConfigProvider>
<Menu menuType={menuType} handleMenuType={handleMenuType} />
</VersionContext.Provider>
</div>
)
}
......
import React from 'react';
import React, { useEffect, useState } from 'react';
import moment from "moment"
import Service from "@/common/js/service";
import BaseService from "@/domains/basic-domain/baseService";
import User from "@/common/js/user";
import { LIVE_SHARE } from "@/domains/course-domain/constants";
import moment from 'moment';
import { Modal, message } from 'antd';
import './CollegeManagePage.less';
import storage from '@/common/js/storage';
const roleMap = {
CloudManager: "管理员",
......@@ -14,6 +15,97 @@ const roleMap = {
CloudOperator: '运营师',
};
function ExpirationPopover(props) {
const [showType, setShowType] = useState(0); //0不显示,1剩余30天,2小于等于7天,3已过期
useEffect(()=> {
if (props.surplusDayTime === 0 ) {
//已过期
let loginflag = storage.get("expiration_tip_login")
if (loginflag === null || loginflag === "true") {
//只有登陆进来的时候提示一次
setShowType(3)
}
return
}
//即将过期
if (props.surplusDayTime === 30 || props.surplusDayTime <= 7) {
let daysflag = storage.get("expiration_tip"+User.getUserId()+"_days")
if (daysflag === null || daysflag !== moment().format("YYYYMMDD")) {
setShowType(2)
}
}
// if (props.surplusDayTime === 30) {
// if (storage.get("expiration_tip"+User.getUserId()+"_thirty") == null || storage.get("expiration_tip"+User.getUserId()+"_thirty") === "true") {
// setShowType(1)
// }
// return
// }
// if (props.surplusDayTime <= 7) {
// let daysflag = storage.getObj("expiration_tip"+User.getUserId()+"_7day");
// if (!daysflag) {
// setShowType(2)
// return
// }
// if (daysflag[props.surplusDayTime - 1] === 0) {
// setShowType(2)
// }
// }
},[props.endTime,props.surplusDayTime])
function iknow() {
storage.set("expiration_tip_login",false)
storage.set("expiration_tip"+User.getUserId()+"_days",moment().format("YYYYMMDD"))
/*
if (props.surplusDayTime === 0 ) {
//已过期
storage.set("expiration_tip_login",false)
} else if (props.surplusDayTime === 30) {
storage.set("expiration_tip"+User.getUserId()+"_thirty",false)
} else if (props.surplusDayTime <= 7) {
let daysflag = [0,0,0,0,0,0,0]
daysflag[props.surplusDayTime - 1] = 1
storage.setObj("expiration_tip"+User.getUserId()+"_7day",daysflag)
}
*/
setShowType(0)
}
if (props.surplusDayTime > 30) {
return ("")
}
return (
<>
{
showType === 0 ? ("") :(
<div className="expirationpopover">
<div className="dialog">
<div className="title">{props.surplusDayTime === 0 ? "服务已到期":"服务到期提醒"}</div>
{
showType === 3 ? (
<div className="tip-text">当前企业购买的小麦企学院服务已于<span style={{color:"#FF4F4F"}}>{moment(props.endTime).format("YYYY-MM-DD HH:mm:ss")}</span>到期,到期后仍可访问,但功能不可使用,建议尽快续费购买哦~</div>
) : (
<div className="tip-text">当前企业购买的小麦企学院服务 <span style={{color:"#FF4F4F"}}>仅剩{props.surplusDayTime}</span>(于<span>{moment(props.endTime).format("YYYY-MM-DD")}</span>到期),为了不影响使用,建议尽快续费购买哦~</div>
)
}
<div className="qrcode">
<img src="https://cdn.xiaomai5.com/qixueyuankehu.png" alt=""></img>
<div className="des">微信/企业微信扫码咨询</div>
</div>
<div className="phone"><svg style={{position:"relative",top:"2px",marginRight:"4px"}} viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M512.651 3.78c-281.433 0-509.21 228.324-509.21 509.209 0 281.43 228.325 509.203 509.21 509.203 281.427 0 509.202-228.317 509.202-509.203 0.55-280.885-227.775-509.21-509.202-509.21z m198.205 743.553c-36.14 36.136-169.737 1.641-302.24-130.312-131.953-131.959-165.902-266.104-129.768-301.695 31.211-31.21 68.99-85.417 125.939-14.782 56.943 70.629 29.016 90.34-3.291 122.647-22.449 22.448 24.642 79.392 73.37 128.125 49.283 48.73 105.678 95.818 128.126 73.368 32.306-32.305 52.017-60.23 122.646-3.288 71.182 56.949 16.426 95.276-14.782 125.937z" p-id="4409" fill="#999999"></path></svg>
咨询电话:19157875632</div>
<div className="button" onClick={iknow}>我知道了</div>
</div>
</div>
)
}
</>
)
}
export default class CollegeManagePage extends React.Component {
constructor(props) {
super(props);
......@@ -24,13 +116,16 @@ export default class CollegeManagePage extends React.Component {
enterpriseId: User.getEnterpriseId(),
isAdmin: false,
createStoreList:[],
joinStoreList:[]
joinStoreList:[],
surplusDayTime:365, //剩余天数
endTime: 0, //有效截至时间
};
}
componentDidMount() {
this.getStoreList();
this.getEnterpriseUser();
this.getVersion()
}
getEnterpriseUser() {
......@@ -45,6 +140,18 @@ export default class CollegeManagePage extends React.Component {
});
}
getVersion() {
BaseService.getLesseeVersionMsg()
.then(res=> {
User.setVersion(res.result)
User.setExpirationTime(res.result.validEndTime)
this.setState({
surplusDayTime: res.result.stateEnum === "NO" ? 0 : res.result.surplusDayTime,
endTime: res.result.validEndTime
})
})
}
getStoreList() {
const { enterpriseId } = this.state;
if (!enterpriseId) return null;
......@@ -132,6 +239,7 @@ export default class CollegeManagePage extends React.Component {
} = this.state;
return (
<div className="college-manage-page">
<ExpirationPopover surplusDayTime={this.state.surplusDayTime} endTime={this.state.endTime}/>
<div className="college-header">
<div className="box">
<img className="box-image" src="https://image.xiaomaiketang.com/xm/fe4NCjr7XF.png" />
......
......@@ -183,4 +183,78 @@
}
}
}
.expirationpopover {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0,0,0,0.7);
z-index: 1000;
.dialog {
width: 560px;
height: 486px;
background: #FFFFFF;
border-radius: 4px;
.title {
text-align: center;
font-size: 20px;
color: #333333;
font-weight: 500;
margin-top: 40px;
}
.tip-text {
font-size: 16px;
color: #666666;
font-weight: 400;
margin-top: 16px;
margin-right: 40px;
margin-left: 40px;
}
.qrcode {
width: 182px;
height: 204px;
background: #F1F3F6;
border-radius: 2px;
margin-top: 16px;
margin-left: auto;
margin-right: auto;
img {
width: 150px;
height: 150px;
margin: 16px 16px 8px 16px;
}
.des {
text-align: center;
font-size: 14px;
color: #333333;
font-weight: 400;
}
}
.phone {
text-align: center;
font-size: 14px;
color: #333333;
font-weight: 400;
margin-top: 16px;
}
.button {
width: 80px;
height: 32px;
background: #2966FF;
cursor: pointer;
margin-left: auto;
margin-right: auto;
margin-top: 24px;
font-size: 14px;
font-weight: 400;
color: #ffffff;
line-height: 32px;
text-align: center;
}
}
}
}
\ No newline at end of file
......@@ -346,7 +346,7 @@
width: 216px;
height: 260px;
top: 49px;
left: 0;
left: -50px;
background-color: #fff;
flex-wrap: wrap;
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
......
......@@ -7,6 +7,7 @@ import User from '@/common/js/user';
import WechatLogin from './WechatLogin';
import BaseService from '@/domains/basic-domain/baseService';
import axios from 'axios';
import storage from '@/common/js/storage';
import _ from 'underscore';
import user from '@/common/js/user';
const { TabPane } = Tabs;
......@@ -34,6 +35,7 @@ function Login(props) {
User.removeUserId();
User.removeToken();
User.removeEnterpriseId();
storage.set("expiration_tip_login",true)
}, []);
function getWXWorkLoginNoCheck(enterpriseId, userId) {
const params = {
......
......@@ -5,7 +5,7 @@
@active-color: #2966ff;
.left-container {
position: absolute;
z-index: 2;
z-index: 10;
top: @top-height;
left: 0;
bottom: 0;
......@@ -51,12 +51,13 @@
display: -webkit-flex;
flex-direction: column;
-webkit-flex-direction: column;
height: calc(~'100% - 60px');
.nav {
-webkit-flex: 1;
cursor: default;
font-size: 16px;
height: calc(~'100% - 72px');
height: calc(~'100% - 84px');
overflow: auto;
&::-webkit-scrollbar {
display: none;
......@@ -134,6 +135,121 @@
color: #5e606a;
}
}
.version-info {
// position: absolute;
height: 84px;
bottom: 0;
width: 180px;
cursor: pointer;
// z-index: 10;
padding-top: 10px;
background-color: white;
.row-1 {
width: fit-content;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 20px;
margin: 0 auto;
.version-name {
display: inline-block;
width: 58px;
text-align: center;
color: #333333;
margin: 0 auto;
border-radius: 2px;
border: 1px solid #E8E8E8;
}
.renew {
display: inline-block;
width: 58px;
color: #2966FF;
margin-left: 8px;
}
}
.expiration-time {
height: 24px;
text-align: center;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 22px;
margin: 6px auto 0 auto;
}
.popover {
display: none;
position: absolute;
z-index: 100;
padding: 16px 22px;
bottom: 22px;
width: 352px;
height: 198px;
right: -342px;
background: #FFFFFF;
box-shadow: 0px 2px 15px 0px rgba(0, 0, 0, 0.06);
.title {
display: inline-block;
width: 68px;
height: 22px;
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 22px;
margin-right: 8px;
}
.expiration-tag {
display: inline-block;
width: 52px;
height: 18px;
background: #EEEEEE;
border-radius: 2px;
text-align: center;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 17px;
}
&::before {
position: absolute;
content: "";
width: 16px;
height: 16px;
left: -8px;
top: 80%;
border: 8px solid transparent;
box-shadow: 0px 2px 15px 0px rgba(0, 0, 0, 0.06);
}
.content {
margin-top: 24px;
.widget {
display: inline-block;
}
.lable {
font-size: 14px;
font-weight: 400;
color: #999999;
line-height: 22px;
}
.lable-text {
margin-top: 4px;
font-size: 16px;
font-weight: 500;
color: #333333;
line-height: 22px;
}
}
}
.popover-show {
display: block;
}
}
}
&.left-container-vertical {
......@@ -155,7 +271,9 @@
// display:inline-block;
// }
// }
}
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
background: @active-color !important;
}
......
import User from '@/common/js/user';
import React, { Key, useContext, useEffect, useRef, useState, useMemo } from 'react';
import {
withRouter,
} from 'react-router-dom';
import { Menu, Popover, Tooltip } from 'antd';
import { RightOutlined } from '@ant-design/icons';
import { menuList } from '../../routes//config/menuList';
import { XMContext, VersionContext } from '../../store/context';
import BaseService from '@/domains/basic-domain/baseService';
import StoreService from '@/domains/store-domain/storeService';
import { Menu } from 'antd';
import React, { Key, useContext, useEffect, useMemo, useState } from 'react';
import { withRouter } from 'react-router-dom';
import classNames from 'classnames';
import User from "@/common/js/user"
import _ from 'underscore';
import { menuList } from '../../routes//config/menuList';
import { XMContext } from '../../store/context';
import './Menu.less';
import "./Menu.less";
import { display } from 'html2canvas/dist/types/css/property-descriptors/display';
import ContactWidget from '@/components/ContactWidget';
const { SubMenu } = Menu;
function VersionPanel(props: any) {
const [versionName, setVersionName] = useState('标准版');
const [showVersionPopover, setShowVersionPopover] = useState(false);
const [showRenewPopover, setShowRenewPopover] = useState(false);
const versionInfo = useContext(VersionContext);
function onVersionEnter() {
setShowVersionPopover(true)
}
function onVersionLeave() {
setShowVersionPopover(false)
setShowRenewPopover(false)
}
function onRenewClick() {
setShowRenewPopover(true)
setShowVersionPopover(false)
}
const versionPopoverClass = classNames({
'popover':true,
'popover-show':showVersionPopover
})
return (
<div className='version-info' onMouseEnter={onVersionEnter} onMouseLeave={onVersionLeave}>
<div className='row-1'>
<div className='version-name'>{versionName}</div>
<ContactWidget trigger='click' placement='rightBottom' visible={showRenewPopover}>
<div className='renew' onClick={onRenewClick}>
去续费
<span className='icon iconfont' style={{ fontSize: '10px' }}>
&#59291;
</span>
</div>
</ContactWidget>
</div>
<div className='expiration-time'>
有效期至{versionInfo?.validEndTime}
{versionInfo?.stateEnum === "NO" ? '(已过期)' : ''}
</div>
<div className={versionPopoverClass}>
<div className='title'>版本信息</div>
{versionInfo?.stateEnum === "NO" ? <div className='expiration-tag'>已过期</div> : ''}
<div className='content'>
<div className='widget' style={{ marginRight: '26px', marginBottom: '16px', width: '70px' }}>
<div className='lable'>剩余天数</div>
<div className='lable-text'>{versionInfo?.surplusDayTime}</div>
</div>
<div className='widget' style={{ marginBottom: '16px', width:"212px"}}>
<div className='lable'>有效起止日期</div>
<div className='lable-text'>
{versionInfo?.validStartTimeST === versionInfo?.validEndTimeST ? "-" : `${versionInfo?.validStartTime}~${versionInfo?.validEndTime}`}
</div>
</div>
<div className='widget' style={{ marginRight: '26px', marginBottom: '8px', width: '70px' }}>
<div className='lable'>剩余人数</div>
<div className='lable-text'>{versionInfo?.surplusUserNum}</div>
</div>
<div className='widget' style={{ marginBottom: '8px' }}>
<div className='lable' style={{ display: 'inline-block' }}>
人数限制
</div>
<Tooltip
overlayStyle={{ maxWidth: '587px', width: '587px' }}
placement='topLeft'
arrowPointAtCenter
title={() => {
return (
<div>
<div>1、若员工/学员存在多个学院,企业人数只统计为1人;</div>
<div>2、若一个学员既用「企业微信」登录学习又用「微信」登录学习,企业人数将统计为2人。</div>
</div>
);
}}>
<div style={{ display: 'inline-block', position: 'relative', top: '2px', marginLeft: '4px' }}>
<span>
<svg viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='1026' width='14' height='14'>
<path
d='M512 68.266667c245.111467 0 443.733333 198.656 443.733333 443.733333s-198.621867 443.733333-443.733333 443.733333C266.922667 955.733333 68.266667 757.077333 68.266667 512S266.922667 68.266667 512 68.266667z m29.320533 596.548266c0.477867-27.989333 2.4576-48.196267 5.802667-60.654933 3.413333-12.458667 8.567467-23.4496 15.633067-33.0752 6.997333-9.557333 21.9136-24.507733 44.714666-44.714667 33.928533-30.037333 56.797867-55.637333 68.437334-76.5952a139.1616 139.1616 0 0 0 17.5104-68.846933c0-43.008-16.5888-79.701333-49.800534-110.011733-33.1776-30.378667-77.6192-45.533867-133.358933-45.533867-52.6336 0-94.958933 14.1312-126.976 42.3936-31.9488 28.2624-51.268267 68.949333-57.685333 122.094933l71.8848 8.533334c6.212267-39.6288 19.3536-68.778667 39.3216-87.483734 19.933867-18.6368 44.817067-27.989333 74.6496-27.989333 30.788267 0 56.900267 10.308267 78.165333 30.9248 21.265067 20.5824 31.880533 44.544 31.880533 71.748267 0 15.018667-3.618133 28.910933-10.922666 41.608533-7.168 12.731733-22.971733 29.764267-47.240534 51.131733-24.200533 21.367467-41.028267 37.649067-50.346666 48.810667-12.6976 15.291733-21.9136 30.481067-27.613867 45.533867-7.748267 19.933867-11.639467 43.690667-11.639467 71.133866 0 4.676267 0.1024 11.707733 0.341334 21.026134h67.242666z m8.192 140.3904v-79.735466h-79.701333v79.735466h79.701333z'
fill='#bfbfbf'
p-id='1027'></path>
</svg>
</span>
</div>
</Tooltip>
<div className='lable-text'>{versionInfo?.userNum}</div>
</div>
</div>
</div>
</div>
)
}
function Aside(props: any) {
const { menuType, handleMenuType } = props;
const ctx: any = useContext(XMContext);
......@@ -178,7 +281,11 @@ function Aside(props: any) {
})}
</Menu>
</div>
{
menuType ? <VersionPanel /> : ""
}
</div>
</div>
);
}
......
......@@ -7,6 +7,8 @@
.table-style {
border: 1px solid #f0f0f0 !important;
margin-bottom: 70px;
max-height: calc(~'100vh - 465px');
overflow: scroll;
}
.ant-tabs {
color: #666666;
......
.batchscore {
.content {
.item {
display: flex;
width: 612px;
height: 48px;
background: #F7F8F9;
font-size: 14px;
line-height: 48px;
font-weight: 400;
color: #333333;
padding-left: 16px;
.type {
width: 112px;
}
.score {
margin-left: 8px;
margin-right: 29px;
width: 258px;
}
}
.item:not(:last-of-type) {
margin-bottom: 8px;
}
}
}
\ No newline at end of file
import React, { useEffect, useState} from "react";
import { Modal, Button, InputNumber, message } from 'antd';
import "./BatchScore.less"
import _ from "underscore";
interface Rule {
typeKey: "GAP_FILLING"|"INDEFINITE_CHOICE"| "JUDGE"|"MULTI_CHOICE"|"SINGLE_CHOICE",
score: number,
portionScore: number,
totalQuestion: number,
}
interface BatchScoreProps {
visible:boolean,
rules: Rule[],
onOK: (rules:Rule[]) => void,
onCancel: () => void,
}
export default function BatchScore(props:BatchScoreProps) {
const [rules, setRules] = useState<Rule[]>(_.sortBy(props.rules,"typeKey"))
const [singleCount, setSingleCount] = useState<number[]>([0])
const [multiCount, setMultiCount] = useState<number[]>([0])
const [judgeCount, setJudgeCount] = useState<number[]>([0])
const [gapCount, setgapCount] = useState<number[]>([0])
const [indefiniteCount, setIndefiniteCount] = useState<number[]>([0])
useEffect(()=> {
_.map(props.rules,(item)=> {
//更新分数统计
switch(item.typeKey) {
case "SINGLE_CHOICE":
setSingleCount([item.totalQuestion,item.totalQuestion*item.score])
break;
case "MULTI_CHOICE":
setMultiCount([item.totalQuestion,item.totalQuestion*item.score])
break;
case "JUDGE":
setJudgeCount([item.totalQuestion,item.totalQuestion*item.score])
break;
case "GAP_FILLING":
setgapCount([item.totalQuestion,item.totalQuestion*item.score])
break;
case "INDEFINITE_CHOICE":
setIndefiniteCount([item.totalQuestion,item.totalQuestion*item.score])
break;
default:
break;
}
})
},[props.rules,rules])
if (!props.visible) {
return ("")
}
function onOk() {
for (let i = 0;i < rules.length;++i) {
if (rules[i].score <= 0 || rules[i].score > 100) {
message.error("分值设置错误")
return;
}
}
props.onOK(rules)
}
function onCancel() {
props.onCancel()
}
const inputNumberStyle = {width:"57px",margin:"0 8px"}
return (
<Modal
className="batchscore"
title="批量设置分数"
onCancel={onCancel}
onOk={onOk}
visible={props.visible}
maskClosable={false}
width={660}
>
<div className="content">
<div className="item">
<span className="type">【单选题】</span>
<span className="score">每题
<InputNumber
min={1}
max={100}
value={rules[4].score}
defaultValue={rules[4].score}
style={inputNumberStyle}
formatter={(value:number|undefined) => String(value)}
parser={(value:string|undefined) => parseInt(String(value))}
onChange={(v)=> {
v = Math.round(v)
let _rules = [...rules]
rules[4].score = v
setRules(_rules)
}}
/>
</span>
<span className="total"><span style={{color:"#2966FF"}}>{singleCount[0]}</span>题,合计<span style={{color:"#2966FF"}}>{singleCount[1]}</span></span>
</div>
<div className="item">
<span className="type">【多选题】</span>
<span className="score">每题
<InputNumber
min={1}
max={100}
defaultValue={rules[3].score}
value={rules[3].score}
style={inputNumberStyle}
formatter={(value:number|undefined) => String(value)}
parser={(value:string|undefined) => parseInt(String(value))}
onChange={(v)=> {
v = Math.round(v)
if (v <= rules[3].portionScore) {
return
}
let _r = [...rules]
_r[3].score = v
setRules(_r)
}}
/>
分,漏选得
<InputNumber
min={0}
max={rules[3].score <= 0 ? 0 : rules[3].score-1}
defaultValue={rules[3].portionScore}
value={rules[3].portionScore}
style={inputNumberStyle}
formatter={(value:number|undefined) => String(value)}
parser={(value:string|undefined) => parseInt(String(value))}
onChange={(v)=> {
v = Math.round(v)
let _r = [...rules]
_r[3].portionScore = v
setRules(_r)
}}
/>
</span>
<span className="total"><span style={{color:"#2966FF"}}>{multiCount[0]}</span>题,合计<span style={{color:"#2966FF"}}>{multiCount[1]}</span></span>
</div>
<div className="item">
<span className="type">【不定项选择题】</span>
<span className="score">每题
<InputNumber
min={1}
max={100}
defaultValue={rules[1].score}
value={rules[1].score}
style={inputNumberStyle}
formatter={(value:number|undefined) => String(value)}
parser={(value:string|undefined) => parseInt(String(value))}
onChange={(v)=> {
v = Math.round(v)
if (v <= rules[1].portionScore) {
return
}
let _r = [...rules]
_r[1].score = v
setRules(_r)
}}
/>
分,漏选得
<InputNumber
min={0}
max={rules[1].score <= 0 ? 0 : rules[1].score-1}
defaultValue={rules[1].portionScore}
value={rules[1].portionScore}
style={inputNumberStyle}
formatter={(value:number|undefined) => String(value)}
parser={(value:string|undefined) => parseInt(String(value))}
onChange={(v)=> {
v = Math.round(v)
let _r = [...rules]
_r[1].portionScore = v
setRules(_r)
}}
/></span>
<span className="total"><span style={{color:"#2966FF"}}>{indefiniteCount[0]}</span>题,合计<span style={{color:"#2966FF"}}>{indefiniteCount[1]}</span></span>
</div>
<div className="item">
<span className="type">【判断题】</span>
<span className="score">每题
<InputNumber
min={1}
max={100}
defaultValue={rules[2].score}
value={rules[2].score}
style={inputNumberStyle}
formatter={(value:number|undefined) => String(value)}
parser={(value:string|undefined) => parseInt(String(value))}
onChange={(v)=> {
v = Math.round(v)
let _r = [...rules]
_r[2].score = v
setRules(_r)
}}
/>
</span>
<span className="total"><span style={{color:"#2966FF"}}>{judgeCount[0]}</span>题,合计<span style={{color:"#2966FF"}}>{judgeCount[1]}</span></span>
</div>
<div className="item">
<span className="type">【填空题】</span>
<span className="score">每题
<InputNumber
min={1}
max={100}
defaultValue={rules[0].score}
value={rules[0].score}
style={inputNumberStyle}
formatter={(value:number|undefined) => String(value)}
parser={(value:string|undefined) => parseInt(String(value))}
onChange={(v)=> {
v = Math.round(v)
if (v <= rules[0].portionScore) {
return
}
let _r = [...rules]
_r[0].score = v
setRules(_r)
}}
/>
分,半对得<InputNumber
min={0}
max={rules[0].score <= 0 ? 0 : rules[0].score-1}
defaultValue={rules[0].portionScore}
value={rules[0].portionScore}
style={inputNumberStyle}
formatter={(value:number|undefined) => String(value)}
parser={(value:string|undefined) => parseInt(String(value))}
onChange={(v)=> {
v = Math.round(v)
let _r = [...rules]
_r[0].portionScore = v
setRules(_r)
}}
/>
</span>
<span className="total"><span style={{color:"#2966FF"}}>{gapCount[0]}</span>题,合计<span style={{color:"#2966FF"}}>{gapCount[1]}</span></span>
</div>
</div>
</Modal>
)
}
\ No newline at end of file
......@@ -11,6 +11,7 @@ import { Modal, Button, message, Spin } from "antd";
import "./BatchImportQuestionModal.less";
import SelectPrepareFileModal from "@/modules/prepare-lesson/modal/SelectPrepareFileModal";
import User from "@/common/js/user";
import moment from "moment";
import AidToolService from "@/domains/aid-tool-domain/AidToolService";
import { LoadingOutlined } from "@ant-design/icons";
class BatchImportQuestionModal extends Component {
......@@ -48,6 +49,15 @@ class BatchImportQuestionModal extends Component {
// 导入
handleImport = async () => {
//过期判断
if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) {
Modal.warning({
title:"服务已到期",
content: "当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买",
okText: "我知道了"
})
return
}
const { uploadFile } = this.state;
if (!uploadFile) {
message.warning("请选择要导入的文件");
......
......@@ -7,4 +7,19 @@
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
export interface VersionInfo {
dayTime: number; //有效时长
stateEnum: string; //"NO"已过期,"YES"未过期
surplusDayTime: number; //剩余有效天数
surplusUserNum: number; //剩余限制人数
userNum: number; //限制人数
validEndTime: string;
validStartTime: string;
validEndTimeST: number;
validStartTimeST: number;
whetherReachUserNum: boolean;
}
export const XMContext: any = React.createContext(null);
export const VersionContext = React.createContext<VersionInfo|null>(null)
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment