Commit a34a356e by wufan

Merge branch 'master' into refactor/wufan/20210702/teach-tool-empty-picture

parents 6f9accee 63a84197
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
"@typescript-eslint/eslint-plugin": "^2.10.0", "@typescript-eslint/eslint-plugin": "^2.10.0",
"@typescript-eslint/parser": "^2.10.0", "@typescript-eslint/parser": "^2.10.0",
"ali-oss": "^6.12.0", "ali-oss": "^6.12.0",
"antd": "^4.9.4", "antd": "^4.16.3",
"array-move": "^3.0.1", "array-move": "^3.0.1",
"axios": "^0.20.0", "axios": "^0.20.0",
"babel-eslint": "10.1.0", "babel-eslint": "10.1.0",
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
"bizcharts": "^3.3.0", "bizcharts": "^3.3.0",
"camelcase": "^5.3.1", "camelcase": "^5.3.1",
"case-sensitive-paths-webpack-plugin": "2.3.0", "case-sensitive-paths-webpack-plugin": "2.3.0",
"classnames": "^2.2.6", "classnames": "^2.3.1",
"cropper": "^3.1.4", "cropper": "^3.1.4",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"css-loader": "3.4.2", "css-loader": "3.4.2",
...@@ -80,6 +80,7 @@ ...@@ -80,6 +80,7 @@
"react": "^16.13.1", "react": "^16.13.1",
"react-app-polyfill": "^1.0.6", "react-app-polyfill": "^1.0.6",
"react-async-component": "^2.0.0", "react-async-component": "^2.0.0",
"react-cropper": "^2.1.8",
"react-dev-utils": "^10.2.1", "react-dev-utils": "^10.2.1",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-infinite-scroller": "^1.2.4", "react-infinite-scroller": "^1.2.4",
......
...@@ -7,11 +7,12 @@ ...@@ -7,11 +7,12 @@
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
*/ */
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosPromise, AxiosError } from 'axios'; 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 { BASIC_HOST, TIME_OUT, USER_TYPE, VERSION, PROJECT } from '@/domains/basic-domain/constants';
import User from './user'; import User from './user';
import { content } from 'html2canvas/dist/types/css/property-descriptors/content';
interface FetchParams { interface FetchParams {
url: string, url: string,
...@@ -27,7 +28,8 @@ interface HeadersType{ ...@@ -27,7 +28,8 @@ interface HeadersType{
storeId?:any, storeId?:any,
storeUserId?:any, storeUserId?:any,
userId?:any, userId?:any,
xmtoken?:any xmtoken?:any,
enterpriseId?:string|null
} }
class Axios { class Axios {
static post( static post(
...@@ -51,6 +53,9 @@ class Axios { ...@@ -51,6 +53,9 @@ class Axios {
if(User.getToken()){ if(User.getToken()){
headerObject.xmtoken = User.getToken(); headerObject.xmtoken = User.getToken();
} }
if (User.getEnterpriseId()) {
headerObject.enterpriseId = User.getEnterpriseId();
}
const instance: AxiosInstance = axios.create({ const instance: AxiosInstance = axios.create({
timeout: TIME_OUT, timeout: TIME_OUT,
responseType: 'json', responseType: 'json',
...@@ -84,8 +89,14 @@ class Axios { ...@@ -84,8 +89,14 @@ class Axios {
}) })
instance.interceptors.response.use((response: AxiosResponse): AxiosResponse | AxiosPromise => { instance.interceptors.response.use((response: AxiosResponse): AxiosResponse | AxiosPromise => {
const { message: ResMessage, success, resultMsg, resultCode,code} = response.data; const { message: ResMessage, success, resultMsg, code: resultCode } = response.data;
if (success || resultCode === 0) { if (resultCode === "CROP_DEPLOY_PAST_BETTER") {
Modal.warning({
title:"服务已到期",
content: "当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买",
okText: "我知道了"
})
} else if (success || resultCode === 0) {
return response; return response;
} else if (!options.reject) { } else if (!options.reject) {
message.error(ResMessage || resultMsg); message.error(ResMessage || resultMsg);
......
...@@ -12,6 +12,10 @@ import { PREFIX, USER_PREFIX } from '@/domains/basic-domain/constants'; ...@@ -12,6 +12,10 @@ import { PREFIX, USER_PREFIX } from '@/domains/basic-domain/constants';
declare var window:any; declare var window:any;
class User { class User {
getExpirationTime() {
return Storage.get(`${PREFIX}_expiration_time`)
}
getVersion() { getVersion() {
return Storage.getObj(`${PREFIX}_version`) return Storage.getObj(`${PREFIX}_version`)
} }
...@@ -56,8 +60,16 @@ class User { ...@@ -56,8 +60,16 @@ class User {
return Storage.get(`${PREFIX}_isAdmin`); return Storage.get(`${PREFIX}_isAdmin`);
} }
setStoreId(value: any) { setExpirationTime(value:number) {
return Storage.set(`${PREFIX}_storeId`, value); 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) { setEnterpriseId(value: any) {
......
...@@ -11,3 +11,6 @@ ...@@ -11,3 +11,6 @@
padding: 16px; padding: 16px;
background-color: #F0F2F5; 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
import React from 'react';
import { Modal, Button } from 'antd';
import Cropper from 'react-cropper';
import 'cropperjs/dist/cropper.css';
import './ImgClipModal.less';
import { isNull } from 'underscore';
class ImgClipModal extends React.Component {
constructor(props) {
super(props);
this.state = {
hasImgReady: false, // 图片是否上传成功
cropperInstace:null,
}
}
render() {
const {
imgUrl,
title = "设置图片",
modalWidth=1080,
visible,
clipContentWidth='500px',
clipContentHeight='430px',
aspectRatio=16/9,
cropBoxWidth=500,
cropBoxHeight=282,
previewBoxWidth='500px',
previewBoxHeight='282px',
onConfirm,
onClose,
} = this.props;
const { hasImgReady,cropperInstace} = this.state;
return (
<Modal
className="img-clip-modal"
title={title}
width={modalWidth}
visible={visible}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
onCancel={() => {
onClose();
}}
zIndex={10001}
footer={[
<Button
key="back"
onClick={() => {
onClose();
}}
>
重新上传
</Button>,
<Button
key="submit"
type="primary"
disabled={!hasImgReady}
onClick={() => {
const cutImg = this.state.cropperInstace.getCroppedCanvas().toDataURL();
const cutImageBlob = window.convertBase64ToBlob(cutImg);
onConfirm(cutImageBlob);
}}
>
确定
</Button>,
]}
>
<div className="clip-box">
<div
style={{
width:clipContentWidth,
height:clipContentHeight,
marginBottom: 0,
}}
>
<Cropper
style={{ height:clipContentHeight, width:clipContentWidth}}
className="cropper__box"
zoomTo={2}
aspectRatio={aspectRatio}
preview=".preview-url-box"
src={imgUrl}
viewMode={1}
guides={true}
background={false}
responsive={true}
autoCropArea={1}
checkOrientation={false}
cropBoxResizable={false}
center={true}
cropBoxMovable={false}
dragMode='move'
onInitialized={(instance) => {
this.setState({
cropperInstace:instance
})
}}
ready={()=>{
this.setState({
hasImgReady:true
})
this.state.cropperInstace.setCropBoxData({width:Number(cropBoxWidth),height:Number(cropBoxHeight),top:(215 - cropBoxHeight/2)});
console.log("height++++",this.state.cropperInstace.getImageData().height);
console.log("width++++",this.state.cropperInstace.getImageData().width);
const ratio = (this.state.cropperInstace.getImageData().width/2)/500;
console.log("ratio++++",ratio);
this.state.cropperInstace.setCanvasData({width:500});
const that = this;
document.querySelector('.cropper__box').addEventListener('dblclick', function (e) {
that.state.cropperInstace.rotate(90)
});
}}
/>
</div>
<div id="clipBtn" style={{ display: "none" }} ref="hiddenBtn"></div>
<div className="preview-img">
<div className="title">效果预览</div>
<div id="preview-url-box" style={{width:previewBoxWidth,height:previewBoxHeight}} className="preview-url-box">
</div>
<div className="tip-box">
<div className="tip">温馨提示</div>
<div className="tip">①预览效果图时可能存在延迟,单击左侧图片刷新即可</div>
<div className="tip">②设置图片时双击可旋转图片,滚动可放大或缩小图片</div>
</div>
</div>
</div>
</Modal>
)
}
}
export default ImgClipModal;
\ No newline at end of file
.img-clip-modal{
.preview-url-box{
overflow: hidden;
}
}
/*
* @Author: your name
* @Date: 2021-07-02 17:15:50
* @LastEditTime: 2021-07-04 18:10:34
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: /xiaomai-cloud-class-web/src/core/downLoad.js
*/
window.download = function download(data, strFileName, strMimeType) {
var self = window, // this script is only for browsers anyway...
defaultMime = 'application/octet-stream', // this default mime also triggers iframe downloads
mimeType = strMimeType || defaultMime,
payload = data,
url = !strFileName && !strMimeType && payload,
anchor = document.createElement('a'),
toString = function (a) {
return String(a);
},
myBlob = self.Blob || self.MozBlob || self.WebKitBlob || toString,
fileName = strFileName || 'download',
blob,
reader;
myBlob = myBlob.call ? myBlob.bind(self) : Blob;
if (String(this) === 'true') {
//reverse arguments, allowing download.bind(true, "text/xml", "export.xml") to act as a callback
payload = [payload, mimeType];
mimeType = payload[0];
payload = payload[1];
}
if (url && url.length < 2048) {
// if no filename and no mime, assume a url was passed as the only argument
fileName = url.split('/').pop().split('?')[0];
anchor.href = url; // assign href prop to temp anchor
if (anchor.href.indexOf(url) !== -1) {
// if the browser determines that it's a potentially valid url path:
var ajax = new XMLHttpRequest();
ajax.open('GET', url, true);
ajax.responseType = 'blob';
ajax.onload = function (e) {
download(e.target.response, fileName, defaultMime);
};
setTimeout(function () {
ajax.send();
}, 0); // allows setting custom ajax headers using the return:
return ajax;
} // end if valid url?
} // end if url?
//go ahead and download dataURLs right away
if (/^data\:[\w+\-]+\/[\w+\-]+[,;]/.test(payload)) {
if (payload.length > 1024 * 1024 * 1.999 && myBlob !== toString) {
payload = dataUrlToBlob(payload);
mimeType = payload.type || defaultMime;
} else {
return navigator.msSaveBlob // IE10 can't do a[download], only Blobs:
? navigator.msSaveBlob(dataUrlToBlob(payload), fileName)
: saver(payload); // everyone else can save dataURLs un-processed
}
} //end if dataURL passed?
blob = payload instanceof myBlob ? payload : new myBlob([payload], { type: mimeType });
function dataUrlToBlob(strUrl) {
var parts = strUrl.split(/[:;,]/),
type = parts[1],
decoder = parts[2] == 'base64' ? atob : decodeURIComponent,
binData = decoder(parts.pop()),
mx = binData.length,
i = 0,
uiArr = new Uint8Array(mx);
for (i; i < mx; ++i) uiArr[i] = binData.charCodeAt(i);
return new myBlob([uiArr], { type: type });
}
function saver(url, winMode) {
if ('download' in anchor) {
//html5 A[download]
anchor.href = url;
anchor.setAttribute('download', fileName);
anchor.className = 'download-js-link';
anchor.innerHTML = 'downloading...';
anchor.style.display = 'none';
document.body.appendChild(anchor);
setTimeout(function () {
anchor.click();
document.body.removeChild(anchor);
if (winMode === true) {
setTimeout(function () {
self.URL.revokeObjectURL(anchor.href);
}, 250);
}
}, 66);
return true;
}
// handle non-a[download] safari as best we can:
if (/(Version)\/(\d+)\.(\d+)(?:\.(\d+))?.*Safari\//.test(navigator.userAgent)) {
url = url.replace(/^data:([\w\/\-\+]+)/, defaultMime);
if (!window.open(url)) {
// popup blocked, offer direct download:
if (window.confirm('Displaying New Document\n\nUse Save As... to download, then click back to return to this page.')) {
window.location.href = url;
}
}
return true;
}
//do iframe dataURL download (old ch+FF):
var f = document.createElement('iframe');
document.body.appendChild(f);
if (!winMode) {
// force a mime that will download:
url = 'data:' + url.replace(/^data:([\w\/\-\+]+)/, defaultMime);
}
f.src = url;
setTimeout(function () {
document.body.removeChild(f);
}, 333);
} //end saver
if (navigator.msSaveBlob) {
// IE10+ : (has Blob, but not a[download] or URL)
return navigator.msSaveBlob(blob, fileName);
}
if (self.URL) {
// simple fast and modern way using Blob and URL:
saver(self.URL.createObjectURL(blob), true);
} else {
// handle non-Blob()+non-URL browsers:
if (typeof blob === 'string' || blob.constructor === toString) {
try {
return saver('data:' + mimeType + ';base64,' + self.btoa(blob));
} catch (y) {
return saver('data:' + mimeType + ',' + encodeURIComponent(blob));
}
}
// Blob but not URL support:
reader = new FileReader();
reader.onload = function (e) {
saver(this.result);
};
reader.readAsDataURL(blob);
}
return true;
}; /* end download() */
\ No newline at end of file
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
min-height: 100%; min-height: 100%;
overflow: hidden;
.page { .page {
position: fixed; position: fixed;
top: 60px; top: 60px;
...@@ -21,6 +22,7 @@ ...@@ -21,6 +22,7 @@
z-index: 102; z-index: 102;
overflow: auto; overflow: auto;
margin: 0 16px; margin: 0 16px;
.box { .box {
&:first-child { &:first-child {
margin-bottom: 8px; margin-bottom: 8px;
......
...@@ -7,7 +7,9 @@ ...@@ -7,7 +7,9 @@
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import Service from "@/common/js/service"; import Service from "@/common/js/service";
import User from "@/common/js/user";
import axios from 'axios'; import axios from 'axios';
export function sendLoginAuthCode(params: object) { export function sendLoginAuthCode(params: object) {
return Service.Hades("anon/hades/sendLoginAuthCode", params); return Service.Hades("anon/hades/sendLoginAuthCode", params);
} }
...@@ -47,6 +49,9 @@ export function getEnterpriseUser(params: object) { ...@@ -47,6 +49,9 @@ export function getEnterpriseUser(params: object) {
export function getWXWorkLoginNoCheck(params: object) { export function getWXWorkLoginNoCheck(params: object) {
return Service.Hades('anon/hades/getWXWorkLoginNoCheck', params); return Service.Hades('anon/hades/getWXWorkLoginNoCheck', params);
} }
export function getLesseeVersionMsg() {
return Service.Hades("public/customerHades/getLesseeVersionMsg",{enterpriseId:User.getEnterpriseId()})
}
export function getYoZoSign(params: object) { export function getYoZoSign(params: object) {
return Service.Apollo('public/apollo/getYoZoSign', params); return Service.Apollo('public/apollo/getYoZoSign', params);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import Service from "@/common/js/service"; import Service from "@/common/js/service";
import User from "@/common/js/user"
export function getEmployeeList(params: object) { export function getEmployeeList(params: object) {
return Service.Hades("public/hades/getStoreUserPage", params); return Service.Hades("public/hades/getStoreUserPage", params);
...@@ -76,3 +77,4 @@ export function getStoreDetail(params: object) { ...@@ -76,3 +77,4 @@ export function getStoreDetail(params: object) {
} }
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@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 { export default class StoreService {
// 获取员工列表 // 获取员工列表
...@@ -55,6 +55,10 @@ export default class StoreService { ...@@ -55,6 +55,10 @@ export default class StoreService {
static getWXWorkLoginNoCheck(params: any){ static getWXWorkLoginNoCheck(params: any){
return getWXWorkLoginNoCheck(params); return getWXWorkLoginNoCheck(params);
} }
//获取企业配置的版本信息
static getLesseeVersionMsg() {
return getLesseeVersionMsg();
}
static getYoZoSign(params:any){ static getYoZoSign(params:any){
return new Promise((resolve) => { return new Promise((resolve) => {
getYoZoSign(params).then(res => { getYoZoSign(params).then(res => {
......
<!-- <!--
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-24 12:20:57 * @Date: 2020-08-24 12:20:57
* @LastEditors: fusanqiasng * @LastEditors: Please set LastEditors
* @LastEditTime: 2021-06-21 15:06:27 * @LastEditTime: 2021-07-08 19:38:52
* @Description: * @Description:
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
--> -->
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div> <div id="root"></div>
<!-- <!--
This HTML file is a template. This HTML file is a template.
If you open it directly in the browser, you will see an empty page. If you open it directly in the browser, you will see an empty page.
...@@ -61,166 +62,6 @@ ...@@ -61,166 +62,6 @@
To begin the development, run `npm start` or `yarn start`. To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`. To create a production bundle, use `npm run build` or `yarn build`.
--> -->
<script>
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
} else {
// Browser globals (root is window)
root.download = factory();
}
})(this, function () {
return function download(data, strFileName, strMimeType) {
var self = window, // this script is only for browsers anyway...
defaultMime = 'application/octet-stream', // this default mime also triggers iframe downloads
mimeType = strMimeType || defaultMime,
payload = data,
url = !strFileName && !strMimeType && payload,
anchor = document.createElement('a'),
toString = function (a) {
return String(a);
},
myBlob = self.Blob || self.MozBlob || self.WebKitBlob || toString,
fileName = strFileName || 'download',
blob,
reader;
myBlob = myBlob.call ? myBlob.bind(self) : Blob;
if (String(this) === 'true') {
//reverse arguments, allowing download.bind(true, "text/xml", "export.xml") to act as a callback
payload = [payload, mimeType];
mimeType = payload[0];
payload = payload[1];
}
if (url && url.length < 2048) {
// if no filename and no mime, assume a url was passed as the only argument
fileName = url.split('/').pop().split('?')[0];
anchor.href = url; // assign href prop to temp anchor
if (anchor.href.indexOf(url) !== -1) {
// if the browser determines that it's a potentially valid url path:
var ajax = new XMLHttpRequest();
ajax.open('GET', url, true);
ajax.responseType = 'blob';
ajax.onload = function (e) {
download(e.target.response, fileName, defaultMime);
};
setTimeout(function () {
ajax.send();
}, 0); // allows setting custom ajax headers using the return:
return ajax;
} // end if valid url?
} // end if url?
//go ahead and download dataURLs right away
if (/^data\:[\w+\-]+\/[\w+\-]+[,;]/.test(payload)) {
if (payload.length > 1024 * 1024 * 1.999 && myBlob !== toString) {
payload = dataUrlToBlob(payload);
mimeType = payload.type || defaultMime;
} else {
return navigator.msSaveBlob // IE10 can't do a[download], only Blobs:
? navigator.msSaveBlob(dataUrlToBlob(payload), fileName)
: saver(payload); // everyone else can save dataURLs un-processed
}
} //end if dataURL passed?
blob = payload instanceof myBlob ? payload : new myBlob([payload], { type: mimeType });
function dataUrlToBlob(strUrl) {
var parts = strUrl.split(/[:;,]/),
type = parts[1],
decoder = parts[2] == 'base64' ? atob : decodeURIComponent,
binData = decoder(parts.pop()),
mx = binData.length,
i = 0,
uiArr = new Uint8Array(mx);
for (i; i < mx; ++i) uiArr[i] = binData.charCodeAt(i);
return new myBlob([uiArr], { type: type });
}
function saver(url, winMode) {
if ('download' in anchor) {
//html5 A[download]
anchor.href = url;
anchor.setAttribute('download', fileName);
anchor.className = 'download-js-link';
anchor.innerHTML = 'downloading...';
anchor.style.display = 'none';
document.body.appendChild(anchor);
setTimeout(function () {
anchor.click();
document.body.removeChild(anchor);
if (winMode === true) {
setTimeout(function () {
self.URL.revokeObjectURL(anchor.href);
}, 250);
}
}, 66);
return true;
}
// handle non-a[download] safari as best we can:
if (/(Version)\/(\d+)\.(\d+)(?:\.(\d+))?.*Safari\//.test(navigator.userAgent)) {
url = url.replace(/^data:([\w\/\-\+]+)/, defaultMime);
if (!window.open(url)) {
// popup blocked, offer direct download:
if (confirm('Displaying New Document\n\nUse Save As... to download, then click back to return to this page.')) {
location.href = url;
}
}
return true;
}
//do iframe dataURL download (old ch+FF):
var f = document.createElement('iframe');
document.body.appendChild(f);
if (!winMode) {
// force a mime that will download:
url = 'data:' + url.replace(/^data:([\w\/\-\+]+)/, defaultMime);
}
f.src = url;
setTimeout(function () {
document.body.removeChild(f);
}, 333);
} //end saver
if (navigator.msSaveBlob) {
// IE10+ : (has Blob, but not a[download] or URL)
return navigator.msSaveBlob(blob, fileName);
}
if (self.URL) {
// simple fast and modern way using Blob and URL:
saver(self.URL.createObjectURL(blob), true);
} else {
// handle non-Blob()+non-URL browsers:
if (typeof blob === 'string' || blob.constructor === toString) {
try {
return saver('data:' + mimeType + ';base64,' + self.btoa(blob));
} catch (y) {
return saver('data:' + mimeType + ',' + encodeURIComponent(blob));
}
}
// Blob but not URL support:
reader = new FileReader();
reader.onload = function (e) {
saver(this.result);
};
reader.readAsDataURL(blob);
}
return true;
}; /* end download() */
});
</script>
</body> </body>
</html> </html>
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-04-27 20:35:34 * @Date: 2020-04-27 20:35:34
* @LastEditors: Please set LastEditors * @LastEditors: Please set LastEditors
* @LastEditTime: 2021-06-28 16:15:59 * @LastEditTime: 2021-07-08 19:22:18
* @Description: * @Description:
*/ */
...@@ -17,6 +17,7 @@ import { RootRouter } from './routes/index'; ...@@ -17,6 +17,7 @@ import { RootRouter } from './routes/index';
import 'antd/dist/antd.less'; import 'antd/dist/antd.less';
import 'video-react/dist/video-react.css'; import 'video-react/dist/video-react.css';
import '@/common/less/index.less'; import '@/common/less/index.less';
import '@/core/downloads';
import '@/core/function'; import '@/core/function';
import '@/core/xmTD'; import '@/core/xmTD';
import User from '@/common/js/user'; import User from '@/common/js/user';
......
...@@ -6,6 +6,7 @@ import Upload from '@/core/upload'; ...@@ -6,6 +6,7 @@ import Upload from '@/core/upload';
import StoreService from "@/domains/store-domain/storeService"; import StoreService from "@/domains/store-domain/storeService";
import User from "@/common/js/user"; import User from "@/common/js/user";
import Bus from '@/core/tbus'; import Bus from '@/core/tbus';
import ImgClipModal from '@/components/ImgClipModal'
import "./CollegeInfoPage.less"; import "./CollegeInfoPage.less";
let cutFlag = false; let cutFlag = false;
class CollegeInfoPage extends React.Component { class CollegeInfoPage extends React.Component {
...@@ -17,6 +18,7 @@ class CollegeInfoPage extends React.Component { ...@@ -17,6 +18,7 @@ class CollegeInfoPage extends React.Component {
logo:'', logo:'',
showSelectFileModal:false, showSelectFileModal:false,
cutImageBlob: null, cutImageBlob: null,
imageFile: null, // 需要被截取的图片
} }
} }
componentWillMount() { componentWillMount() {
...@@ -51,84 +53,12 @@ class CollegeInfoPage extends React.Component { ...@@ -51,84 +53,12 @@ class CollegeInfoPage extends React.Component {
}) })
} }
handleSelectCover = (file)=> { handleSelectCover = (file)=> {
this.uploadImage(file);
}
//上传图片
uploadImage = (imageFile) => {
const { folderName } = imageFile;
const fileName = window.random_string(16) + folderName.slice(folderName.lastIndexOf("."));
const self = this;
this.setState(
{
visible: true,
},
() => {
setTimeout(() => {
const okBtnDom = document.querySelector("#headPicModal");
const options = {
size: [500, 128],
ok: okBtnDom,
maxZoom: 3,
style: {
jpgFillColor: "transparent",
},
done: function (dataUrl) {
clearTimeout(self.timer);
self.timer = setTimeout(() => {
if ((self.state.rotate != this.rotate()) || (self.state.scale != this.scale())) {
console.log(this.scale(), 'scale')
const _dataUrl = this.clip()
const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl);
self.setState({
cutImageBlob,
dataUrl: _dataUrl,
rotate: this.rotate(),
scale: this.scale()
})
}
}, 500)
const cutImageBlob = self.convertBase64UrlToBlob(dataUrl);
self.setState({
cutImageBlob,
dataUrl
})
setTimeout(() => {
cutFlag = false;
}, 2000);
},
fail: (failInfo) => {
message.error("图片上传失败了,请重新上传");
},
loadComplete: function (img) {
setTimeout(() => {
const _dataUrl = this.clip()
self.setState({
dataUrl: _dataUrl,
hasImgReady: true
})
}, 100)
},
};
const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}`
if (!this.state.photoclip) {
const _photoclip = new PhotoClip("#headPicModal", options);
_photoclip.load(imgUrl);
this.setState({ this.setState({
photoclip: _photoclip, visible: true,
imageFile:file
}); });
} else {
this.state.photoclip.clear();
this.state.photoclip.load(imgUrl);
} }
}, 200);
}
);
};
//获取resourceId //获取resourceId
getSignature = (blob, fileName) => { getSignature = (blob, fileName) => {
...@@ -150,16 +80,7 @@ class CollegeInfoPage extends React.Component { ...@@ -150,16 +80,7 @@ class CollegeInfoPage extends React.Component {
logo:coverClicpPath logo:coverClicpPath
}) })
} }
// base64转换成blob
convertBase64UrlToBlob = (urlData) => {
const bytes = window.atob(urlData.split(",")[1]);
const ab = new ArrayBuffer(bytes.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: "image/png" });
};
updateInfo=()=>{ updateInfo=()=>{
const { storeName, logo } = this.state; const { storeName, logo } = this.state;
if(!storeName){ if(!storeName){
...@@ -175,6 +96,7 @@ class CollegeInfoPage extends React.Component { ...@@ -175,6 +96,7 @@ class CollegeInfoPage extends React.Component {
User.setStoreName(storeName); User.setStoreName(storeName);
Bus.trigger('storeNameChange',storeName); Bus.trigger('storeNameChange',storeName);
message.success('保存成功'); message.success('保存成功');
Bus.trigger("updateCollegeInfo")
}); });
} }
render() { render() {
...@@ -185,6 +107,7 @@ class CollegeInfoPage extends React.Component { ...@@ -185,6 +107,7 @@ class CollegeInfoPage extends React.Component {
hasImgReady, hasImgReady,
logo, logo,
cutImageBlob, cutImageBlob,
imageFile
} = this.state; } = this.state;
return ( return (
<div className="page college-info-page"> <div className="page college-info-page">
...@@ -242,65 +165,9 @@ class CollegeInfoPage extends React.Component { ...@@ -242,65 +165,9 @@ class CollegeInfoPage extends React.Component {
onSelect={this.handleSelectCover} onSelect={this.handleSelectCover}
/> />
} }
<Modal { visible &&
title="设置图片" <ImgClipModal visible={visible} imgUrl={imageFile.ossUrl} aspectRatio='125/32' cropBoxHeight='128' onConfirm={this.getSignature} onClose={()=>{this.setState({ visible: false });}}/>
width={1080}
visible={visible}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
onCancel={() => {
this.setState({ visible: false });
}}
zIndex={10001}
footer={[
<Button
key="back"
onClick={() => {
this.setState({ visible: false });
}}
>
重新上传
</Button>,
<Button
key="submit"
type="primary"
disabled={!hasImgReady}
onClick={() => {
if (!cutFlag) {
cutFlag = true;
this.refs.hiddenBtn.click();
} }
this.getSignature(cutImageBlob);
}}
>
确定
</Button>,
]}
>
<div className="clip-box">
<div
id="headPicModal"
ref="headPicModal"
style={{
width: "500px",
height: "430px",
marginBottom: 0,
}}
></div>
<div id="clipBtn" style={{ display: "none" }} ref="hiddenBtn"></div>
<div className="preview-img">
<div className="title">效果预览</div>
<div id="preview-url-box" style={{width:500,height:128}}>
<img src={this.state.dataUrl} style={{ width: '100%' }} alt="" />
</div>
<div className="tip-box">
<div className="tip">温馨提示</div>
<div className="tip">①预览效果图时可能存在延迟,单击左侧图片刷新即可</div>
<div className="tip">②设置图片时双击可旋转图片,滚动可放大或缩小图片</div>
</div>
</div>
</div>
</Modal>
<div><Button type="primary" onClick={this.updateInfo} htmlType="submit" className="submit-btn">更新信息</Button></div> <div><Button type="primary" onClick={this.updateInfo} htmlType="submit" className="submit-btn">更新信息</Button></div>
</div> </div>
......
...@@ -15,6 +15,7 @@ import college from '@/common/lottie/college.json'; ...@@ -15,6 +15,7 @@ import college from '@/common/lottie/college.json';
import StoreService from "@/domains/store-domain/storeService"; import StoreService from "@/domains/store-domain/storeService";
import EmployeeAddOrEditModal from "../store-manage/EmployeeAddOrEditModal"; import EmployeeAddOrEditModal from "../store-manage/EmployeeAddOrEditModal";
import User from "@/common/js/user"; import User from "@/common/js/user";
import LimitTip from "./LimitTip";
import "./EmployeeManage.less"; import "./EmployeeManage.less";
import ChooseMembersModal from "./modal/ChooseMembersModal"; import ChooseMembersModal from "./modal/ChooseMembersModal";
...@@ -68,6 +69,7 @@ function EmployeeManage() { ...@@ -68,6 +69,7 @@ function EmployeeManage() {
}); });
const [total, setTotal] = useState(0); const [total, setTotal] = useState(0);
const [realTotal, setRealTotal] = useState(0)
const [model, setModel] = useState<React.ReactNode>(null); const [model, setModel] = useState<React.ReactNode>(null);
const [employeeModal, setEmployeeModal] = useState(false); const [employeeModal, setEmployeeModal] = useState(false);
const [choosedItem, setChooseItem] = useState<ChoosedItemType>({ const [choosedItem, setChooseItem] = useState<ChoosedItemType>({
...@@ -85,6 +87,17 @@ function EmployeeManage() { ...@@ -85,6 +87,17 @@ function EmployeeManage() {
if (!User.getEnterpriseId()) { if (!User.getEnterpriseId()) {
window.RCHistory.replace('/employees-manage'); 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(() => { useEffect(() => {
...@@ -362,6 +375,7 @@ function EmployeeManage() { ...@@ -362,6 +375,7 @@ function EmployeeManage() {
</Button> </Button>
} }
</div> </div>
<LimitTip type="员工" total={realTotal} tip={()=>{return (<div>数据为当前学院的员工数,若员工存在多个学院,企业人数只统计为1人</div>)}}/>
<div className="box-body"> <div className="box-body">
<XMTable <XMTable
renderEmpty={{ 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 @@ ...@@ -17,4 +17,5 @@
margin-left: 4px; margin-left: 4px;
} }
} }
} }
\ No newline at end of file
...@@ -15,6 +15,8 @@ import { Input, DatePicker, Select, Button, message } from "antd"; ...@@ -15,6 +15,8 @@ import { Input, DatePicker, Select, Button, message } from "antd";
import StoreService from "@/domains/store-domain/storeService"; import StoreService from "@/domains/store-domain/storeService";
import User from "@/common/js/user"; import User from "@/common/js/user";
import ChooseMembersModal from "./modal/ChooseMembersModal"; import ChooseMembersModal from "./modal/ChooseMembersModal";
import LimitTip from "./LimitTip"
import { XMTable } from '@/components'; import { XMTable } from '@/components';
import college from '@/common/lottie/college.json'; import college from '@/common/lottie/college.json';
import "./UserManagePage.less"; import "./UserManagePage.less";
...@@ -26,6 +28,8 @@ const { RangePicker } = DatePicker; ...@@ -26,6 +28,8 @@ const { RangePicker } = DatePicker;
declare var window: any; declare var window: any;
function UserManagePage() { function UserManagePage() {
const [userList, setUserList] = useState([]); const [userList, setUserList] = useState([]);
const [model, setModel] = useState<React.ReactNode>(null); const [model, setModel] = useState<React.ReactNode>(null);
...@@ -39,11 +43,25 @@ function UserManagePage() { ...@@ -39,11 +43,25 @@ function UserManagePage() {
sourceEnum: undefined, sourceEnum: undefined,
}); });
const [total, setTotal] = useState(0); const [total, setTotal] = useState(0);
const [realTotal, setRealTotal] = useState(0)
useEffect(() => { useEffect(() => {
if (!User.getEnterpriseId()) { if (!User.getEnterpriseId()) {
window.RCHistory.replace('/user-manage'); 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(() => { useEffect(() => {
...@@ -125,9 +143,6 @@ function UserManagePage() { ...@@ -125,9 +143,6 @@ function UserManagePage() {
<div className="header-item"> <div className="header-item">
<span className="item-name">搜索学员:</span> <span className="item-name">搜索学员:</span>
<Search <Search
style={{
width: 300,
}}
placeholder="搜索学员姓名/手机号" placeholder="搜索学员姓名/手机号"
onSearch={(value) => { onSearch={(value) => {
const _query = { ...query }; const _query = { ...query };
...@@ -199,6 +214,7 @@ function UserManagePage() { ...@@ -199,6 +214,7 @@ function UserManagePage() {
}} }}
>添加学员</Button> >添加学员</Button>
} }
<LimitTip type="学员" total={realTotal} tip={()=>{ return (<div><div>1、数据为当前学院的员工数,若学员存在多个学院,企业人数只统计为1人;</div><div>2、若一个学员既用「企业微信」登录学习又用「微信」登录学习,企业人数将统计为2人。</div></div>)}}/>
<div className="box-body"> <div className="box-body">
<XMTable <XMTable
renderEmpty={{ renderEmpty={{
......
...@@ -285,6 +285,10 @@ class ChooseMembersModal extends React.Component { ...@@ -285,6 +285,10 @@ class ChooseMembersModal extends React.Component {
visible={isOpen} visible={isOpen}
onCancel={() => this.handleClose()} onCancel={() => this.handleClose()}
onOk={() => { onOk={() => {
if (User.getVersion() && User.getVersion().whetherReachUserNum) {
message.error("添加失败,企业使用人数超出限制")
return
}
if (_.isEmpty(selectUserList)) { if (_.isEmpty(selectUserList)) {
message.warning(type === 'USER' ? '请选择员工' : '请选择学员') message.warning(type === 'USER' ? '请选择员工' : '请选择学员')
return null; return null;
......
...@@ -269,6 +269,15 @@ handleChangeBasicInfo = (field, value) => { ...@@ -269,6 +269,15 @@ handleChangeBasicInfo = (field, value) => {
// 完成创建/编辑 // 完成创建/编辑
handleSubmit = () => { 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 { addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo, id, isEdit, type } = this.state;
const {liveDate, timeHorizonStart} = addLiveClassInfo; const {liveDate, timeHorizonStart} = addLiveClassInfo;
const _liveDate = moment(liveDate).format("YYYY-MM-DD"); const _liveDate = moment(liveDate).format("YYYY-MM-DD");
......
...@@ -22,6 +22,13 @@ class LiveCoursePage extends React.Component { ...@@ -22,6 +22,13 @@ class LiveCoursePage extends React.Component {
componentWillMount() { componentWillMount() {
this.handleFetchLiveList(this.state.query); this.handleFetchLiveList(this.state.query);
} }
changeShelfState = (index, shelfState) => {
const { courseList } = this.state;
courseList[index].shelfState = shelfState;
this.setState({
courseList,
});
};
// 获取直播课列表 // 获取直播课列表
handleFetchLiveList = (_query) => { handleFetchLiveList = (_query) => {
const { query } = this.state; const { query } = this.state;
...@@ -54,6 +61,7 @@ class LiveCoursePage extends React.Component { ...@@ -54,6 +61,7 @@ class LiveCoursePage extends React.Component {
total={total} total={total}
courseList={courseList} courseList={courseList}
onChange={this.handleFetchLiveList} onChange={this.handleFetchLiveList}
changeShelfState={this.changeShelfState}
/> />
</div> </div>
</div> </div>
......
...@@ -13,8 +13,9 @@ import { ImgCutModalNew } from '@/components'; ...@@ -13,8 +13,9 @@ import { ImgCutModalNew } from '@/components';
import StoreService from "@/domains/store-domain/storeService"; import StoreService from "@/domains/store-domain/storeService";
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal'; import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
import Upload from '@/core/upload'; import Upload from '@/core/upload';
// import PhotoClip from 'photoclip' import Cropper from 'react-cropper';
import ImgClipModal from '@/components/ImgClipModal'
import 'cropperjs/dist/cropper.css';
import './AddLiveBasic.less'; import './AddLiveBasic.less';
const defaultCover = 'https://image.xiaomaiketang.com/xm/Yip2YtFDwH.png'; const defaultCover = 'https://image.xiaomaiketang.com/xm/Yip2YtFDwH.png';
...@@ -31,7 +32,8 @@ class AddLiveBasic extends React.Component { ...@@ -31,7 +32,8 @@ class AddLiveBasic extends React.Component {
courseCatalogList:[], courseCatalogList:[],
showSelectFileModal: false, showSelectFileModal: false,
cutImageBlob: null, cutImageBlob: null,
hasImgReady: false // 图片是否上传成功 hasImgReady: false, // 图片是否上传成功
cropperInstace:null
} }
} }
componentWillUnmount() { componentWillUnmount() {
...@@ -49,16 +51,6 @@ class AddLiveBasic extends React.Component { ...@@ -49,16 +51,6 @@ class AddLiveBasic extends React.Component {
}); });
} }
// 上传封面图
handleShowImgCutModal = (event) => {
const imageFile = event.target.files[0];
if (!imageFile) return;
this.setState({
imageFile,
showCutModal: true,
});
}
// 使用默认封面图 // 使用默认封面图
handleResetCoverUrl = () => { handleResetCoverUrl = () => {
const { data: { coverUrl } } = this.props; const { data: { coverUrl } } = this.props;
...@@ -87,85 +79,13 @@ class AddLiveBasic extends React.Component { ...@@ -87,85 +79,13 @@ class AddLiveBasic extends React.Component {
} }
} }
handleSelectCover = (file) => { handleSelectCover = (file) => {
this.uploadImage(file);
}
//上传图片
uploadImage = (imageFile) => {
const { folderName } = imageFile;
const fileName =
window.random_string(16) + folderName.slice(folderName.lastIndexOf("."));
const self = this;
this.setState(
{
visible: true,
},
() => {
setTimeout(() => {
const okBtnDom = document.querySelector("#headPicModal");
const options = {
size: [500, 282],
ok: okBtnDom,
maxZoom: 3,
style: {
jpgFillColor: "transparent",
},
done: function (dataUrl) {
clearTimeout(self.timer);
self.timer = setTimeout(() => {
if ((self.state.rotate != this.rotate()) || (self.state.scale != this.scale())) {
console.log(this.scale(), 'scale')
const _dataUrl = this.clip()
const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl);
self.setState({
cutImageBlob,
dataUrl: _dataUrl,
rotate: this.rotate(),
scale: this.scale()
})
}
}, 500)
const cutImageBlob = self.convertBase64UrlToBlob(dataUrl);
self.setState({
cutImageBlob,
dataUrl
})
setTimeout(() => {
cutFlag = false;
}, 2000);
},
fail: (failInfo) => {
message.error("图片上传失败了,请重新上传");
},
loadComplete: function (img) {
setTimeout(() => {
const _dataUrl = this.clip()
self.setState({
dataUrl: _dataUrl,
hasImgReady: true
})
}, 100)
},
};
const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}`
if (!this.state.photoclip) {
const _photoclip = new PhotoClip("#headPicModal", options);
_photoclip.load(imgUrl);
this.setState({ this.setState({
photoclip: _photoclip, visible: true,
imageFile:file
}); });
} else {
this.state.photoclip.clear();
this.state.photoclip.load(imgUrl);
} }
}, 200);
}
);
};
//获取resourceId //获取resourceId
getSignature = (blob, fileName) => { getSignature = (blob, fileName) => {
...@@ -179,16 +99,6 @@ class AddLiveBasic extends React.Component { ...@@ -179,16 +99,6 @@ class AddLiveBasic extends React.Component {
}); });
}; };
// base64转换成blob
convertBase64UrlToBlob = (urlData) => {
const bytes = window.atob(urlData.split(",")[1]);
const ab = new ArrayBuffer(bytes.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: "image/png" });
};
updateCover = () =>{ updateCover = () =>{
const {coverClicpPath,coverId} = this.state const {coverClicpPath,coverId} = this.state
this.setState({ this.setState({
...@@ -254,24 +164,6 @@ class AddLiveBasic extends React.Component { ...@@ -254,24 +164,6 @@ class AddLiveBasic extends React.Component {
<Cascader disabled={!isEdit ? true: false} defaultValue={[categoryName]} options={courseCatalogList} displayRender={ label => label.join('-')} fieldNames={fieldNames} onChange={this.catalogChange} style={{ width: 240 }} placeholder="请选择课程分类" suffixIcon={<span className="icon iconfont" style={{fontSize:'12px',color:'#BFBFBF'}}>&#xe835;</span>} /> <Cascader disabled={!isEdit ? true: false} defaultValue={[categoryName]} options={courseCatalogList} displayRender={ label => label.join('-')} fieldNames={fieldNames} onChange={this.catalogChange} style={{ width: 240 }} placeholder="请选择课程分类" suffixIcon={<span className="icon iconfont" style={{fontSize:'12px',color:'#BFBFBF'}}>&#xe835;</span>} />
} }
</div> </div>
<ImgCutModalNew
title="裁剪"
width={530}
cutWidth={500}
cutHeight={282}
cutContentWidth={500}
cutContentHeight={300}
visible={showCutModal}
imageFile={imageFile}
bizCode='LIVE_COURSE_MEDIA'
onOk={(urlStr, resourceId) => {
this.setState({ showCutModal: false });
this.props.onChange('coverId', resourceId, urlStr);
this.state.currentInputFile.value = '';
}}
onClose={() => this.setState({ showCutModal: false })}
reUpload={() => { this.state.currentInputFile.click() }}
/>
{showSelectFileModal && {showSelectFileModal &&
<SelectPrepareFileModal <SelectPrepareFileModal
key="basic" key="basic"
...@@ -287,66 +179,9 @@ class AddLiveBasic extends React.Component { ...@@ -287,66 +179,9 @@ class AddLiveBasic extends React.Component {
onSelect={this.handleSelectCover} onSelect={this.handleSelectCover}
/> />
} }
<Modal { visible &&
title="设置图片" <ImgClipModal visible={visible} imgUrl={imageFile.ossUrl} onConfirm={this.getSignature} onClose={()=>{this.setState({ visible: false });}}/>
width={1080}
visible={visible}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
onCancel={() => {
this.setState({ visible: false });
}}
zIndex={10001}
footer={[
<Button
key="back"
onClick={() => {
this.setState({ visible: false });
}}
>
重新上传
</Button>,
<Button
key="submit"
type="primary"
disabled={!hasImgReady}
onClick={() => {
if (!cutFlag) {
cutFlag = true;
this.refs.hiddenBtn.click();
} }
this.getSignature(cutImageBlob);
}}
>
确定
</Button>,
]}
>
<div className="clip-box">
<div
id="headPicModal"
ref="headPicModal"
style={{
width: "500px",
height: "430px",
marginBottom: 0,
}}
></div>
<div id="clipBtn" style={{ display: "none" }} ref="hiddenBtn"></div>
<div className="preview-img">
<div className="title">效果预览</div>
{/* <img src={previewUrl} alt="图片预览" className="preview-url" /> */}
<div id="preview-url-box" style={{width:500,height:282}}>
<img src={this.state.dataUrl} style={{ width: '100%' }} alt="" />
</div>
<div className="tip-box">
<div className="tip">温馨提示</div>
<div className="tip">①预览效果图时可能存在延迟,单击左侧图片刷新即可</div>
<div className="tip">②设置图片时双击可旋转图片,滚动可放大或缩小图片</div>
</div>
</div>
</div>
</Modal>
</div> </div>
) )
} }
......
...@@ -82,3 +82,6 @@ ...@@ -82,3 +82,6 @@
width: 500px; width: 500px;
height: 300px; height: 300px;
} }
.preview-url-box{
overflow: hidden;
}
\ No newline at end of file
.live-course-list { .live-course-list {
margin-top: 12px; margin-top: 12px;
.record__item { .record__item {
overflow: hidden;
display: flex; display: flex;
align-items: center; align-items: center;
.course-cover { .course-cover {
...@@ -11,63 +11,61 @@ ...@@ -11,63 +11,61 @@
border-radius: 2px; border-radius: 2px;
margin-right: 8px; margin-right: 8px;
} }
.course-name{ .course-name {
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 500;
color: #333333; color: #333333;
line-height: 20px; line-height: 20px;
font-weight: bold; font-weight: bold;
max-width:244px; max-width: 200px;
overflow: hidden; overflow: hidden;
text-overflow:ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.course-time{ .course-time {
font-size: 12px; font-size: 12px;
font-weight: 400; font-weight: 400;
color: #666666; color: #666666;
line-height: 20px; line-height: 20px;
} }
.course-status { .course-status {
font-size:12px; font-size: 12px;
line-height:18px; line-height: 18px;
display:inline-block; display: inline-block;
border-radius:2px; border-radius: 2px;
padding:0 8px; padding: 0 8px;
margin-left:4px; margin-left: 4px;
} }
.teacher-assistant{ .teacher-assistant {
display:flex; display: flex;
.teacher{ .teacher {
font-size: 12px; font-size: 12px;
color: #666666; color: #666666;
max-width: 96px; max-width: 96px;
overflow: hidden; overflow: hidden;
text-overflow:ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
display:inline-block; display: inline-block;
padding-top:2px; padding-top: 2px;
} }
.assistant{ .assistant {
font-size: 12px; font-size: 12px;
color: #666666; color: #666666;
max-width: 96px; max-width: 96px;
overflow: hidden; overflow: hidden;
text-overflow:ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
display:inline-block; display: inline-block;
padding-top:2px; padding-top: 2px;
} }
.split { .split {
margin: 0 4px; margin: 0 4px;
color: #BFBFBF; color: #bfbfbf;
display: inline-blcok; display: inline-blcok;
} }
} }
} }
.related-task{ .related-task {
text-overflow: -o-ellipsis-lastline; text-overflow: -o-ellipsis-lastline;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
...@@ -76,37 +74,37 @@ ...@@ -76,37 +74,37 @@
line-clamp: 2; line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }
.categoryName{ .categoryName {
font-size: 14px; font-size: 14px;
color: #666666; color: #666666;
line-height: 20px; line-height: 20px;
} }
.courseware{ .courseware {
font-size: 14px; font-size: 14px;
color: #2966FF; color: #2966ff;
line-height: 20px; line-height: 20px;
text-align:right; text-align: right;
cursor:pointer; cursor: pointer;
} }
.quota-icon{ .quota-icon {
color:#2966FF; color: #2966ff;
cursor:pointer; cursor: pointer;
} }
.operate { .operate {
display: flex; display: flex;
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
.operate__item { .operate__item {
color: #2966FF; color: #2966ff;
cursor: pointer; cursor: pointer;
&.split { &.split {
margin: 0 8px; margin: 0 8px;
color: #BFBFBF; color: #bfbfbf;
} }
} }
} }
.operate-text { .operate-text {
color: #2966FF; color: #2966ff;
cursor: pointer; cursor: pointer;
} }
.course-start-end { .course-start-end {
...@@ -135,7 +133,27 @@ ...@@ -135,7 +133,27 @@
font-size: 12px; font-size: 12px;
} }
} }
tbody {
tr {
&:nth-child(even) {
background: transparent !important;
td {
background: #fff !important;
}
}
&:nth-child(odd) {
background: #fafafa !important;
td {
background: #fafafa !important;
}
}
&:hover {
td {
background: #f3f6fa !important;
}
}
}
}
} }
.live-course-more-menu { .live-course-more-menu {
background: white; background: white;
......
...@@ -53,7 +53,7 @@ class LiveCourseOpt extends React.Component { ...@@ -53,7 +53,7 @@ class LiveCourseOpt extends React.Component {
{ userRole !== "CloudLecturer" && { userRole !== "CloudLecturer" &&
<Button type="primary" onClick={this.handleCreateLiveCouese}>新建直播课</Button> <Button type="primary" onClick={this.handleCreateLiveCouese}>新建直播课</Button>
} }
{!this.state.isMac && <Button onClick={this.handleDownloadClient}>下载直播客户端</Button>} <Button onClick={this.handleDownloadClient}>下载直播客户端</Button>
</div> </div>
</div> </div>
) )
......
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-05 10:07:47 * @Date: 2020-08-05 10:07:47
* @LastEditors: fusanqiasng * @LastEditors: Please set LastEditors
* @LastEditTime: 2021-06-16 18:16:14 * @LastEditTime: 2021-07-08 19:32:50
* @Description: 图文课新增/编辑页 * @Description: 图文课新增/编辑页
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
import React from 'react'; import React from 'react';
import { Button, Input, Radio, message, Modal, Cascader } from 'antd'; import { Button, Input, Radio, message, Modal, Cascader } from 'antd';
import $ from 'jquery'; import $ from 'jquery';
import moment from 'moment';
import { DISK_MAP, FileTypeIcon, FileVerifyMap } from '@/common/constants/academic/lessonEnum'; import { DISK_MAP, FileTypeIcon, FileVerifyMap } from '@/common/constants/academic/lessonEnum';
import { ImgCutModalNew } from '@/components'; import { ImgCutModalNew } from '@/components';
import ShowTips from '@/components/ShowTips'; import ShowTips from '@/components/ShowTips';
...@@ -25,6 +25,7 @@ import { randomString } from '@/domains/basic-domain/utils'; ...@@ -25,6 +25,7 @@ import { randomString } from '@/domains/basic-domain/utils';
import User from '@/common/js/user'; import User from '@/common/js/user';
import _ from 'underscore'; import _ from 'underscore';
import Upload from '@/core/upload'; import Upload from '@/core/upload';
import ImgClipModal from '@/components/ImgClipModal'
import './AddGraphicsCourse.less'; import './AddGraphicsCourse.less';
const EDIT_BOX_KEY = Math.random(); const EDIT_BOX_KEY = Math.random();
...@@ -279,79 +280,10 @@ class AddGraphicsCourse extends React.Component { ...@@ -279,79 +280,10 @@ class AddGraphicsCourse extends React.Component {
}; };
handleSelectCover = (file) => { handleSelectCover = (file) => {
this.uploadCoverImage(file);
};
//上传图片
uploadCoverImage = (imageFile) => {
const { folderName } = imageFile;
const fileName = window.random_string(16) + folderName.slice(folderName.lastIndexOf('.'));
const self = this;
this.setState(
{
visible: true,
},
() => {
setTimeout(() => {
const okBtnDom = document.querySelector('#headPicModal');
const options = {
size: [500, 282],
ok: okBtnDom,
maxZoom: 3,
style: {
jpgFillColor: 'transparent',
},
done: function (dataUrl) {
clearTimeout(self.timer);
self.timer = setTimeout(() => {
if (self.state.rotate != this.rotate() || self.state.scale != this.scale()) {
const _dataUrl = this.clip();
const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl);
self.setState({
cutImageBlob,
dataUrl: _dataUrl,
rotate: this.rotate(),
scale: this.scale(),
});
}
}, 500);
const cutImageBlob = self.convertBase64UrlToBlob(dataUrl);
self.setState({
cutImageBlob,
dataUrl,
});
setTimeout(() => {
cutFlag = false;
}, 2000);
},
fail: (failInfo) => {
message.error('图片上传失败了,请重新上传');
},
loadComplete: function (img) {
setTimeout(() => {
const _dataUrl = this.clip();
self.setState({
dataUrl: _dataUrl,
hasImgReady: true,
});
}, 100);
},
};
const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}`;
if (!this.state.photoclip) {
const _photoclip = new PhotoClip('#headPicModal', options);
_photoclip.load(imgUrl);
this.setState({ this.setState({
photoclip: _photoclip, visible: true,
imageFile:file
}); });
} else {
this.state.photoclip.clear();
this.state.photoclip.load(imgUrl);
}
}, 200);
}
);
}; };
//获取resourceId //获取resourceId
...@@ -377,19 +309,17 @@ class AddGraphicsCourse extends React.Component { ...@@ -377,19 +309,17 @@ class AddGraphicsCourse extends React.Component {
}); });
}; };
// base64转换成blob
convertBase64UrlToBlob = (urlData) => {
const bytes = window.atob(urlData.split(',')[1]);
const ab = new ArrayBuffer(bytes.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: 'image/png' });
};
// 保存 // 保存
handleSubmit = () => { 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 { id, coverId, pageType, courseName, courseMedia, introduce, categoryId, shelfState, whetherVisitorsJoin } = this.state;
const commonParams = { const commonParams = {
...@@ -610,61 +540,9 @@ class AddGraphicsCourse extends React.Component { ...@@ -610,61 +540,9 @@ class AddGraphicsCourse extends React.Component {
onSelect={this.handleSelectCover} onSelect={this.handleSelectCover}
/> />
)} )}
<Modal { visible &&
title='设置图片' <ImgClipModal visible={visible} imgUrl={imageFile.ossUrl} onConfirm={this.getSignature} onClose={()=>{this.setState({ visible: false });}}/>
width={1080}
visible={visible}
maskClosable={false}
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
onCancel={() => {
this.setState({ visible: false });
}}
zIndex={10001}
footer={[
<Button
key='back'
onClick={() => {
this.setState({ visible: false });
}}>
重新上传
</Button>,
<Button
key='submit'
type='primary'
disabled={!hasImgReady}
onClick={() => {
if (!cutFlag) {
cutFlag = true;
this.refs.hiddenBtn.click();
} }
this.getSignature(cutImageBlob);
}}>
确定
</Button>,
]}>
<div className='clip-box'>
<div
id='headPicModal'
ref='headPicModal'
style={{
width: '500px',
height: '430px',
marginBottom: 0,
}}></div>
<div id='clipBtn' style={{ display: 'none' }} ref='hiddenBtn'></div>
<div className='preview-img'>
<div className='title'>效果预览</div>
<div id='preview-url-box' style={{ width: 500, height: 282 }}>
<img src={this.state.dataUrl} style={{ width: '100%' }} alt='' />
</div>
<div className='tip-box'>
<div className='tip'>温馨提示</div>
<div className='tip'>①预览效果图时可能存在延迟,单击左侧图片刷新即可</div>
<div className='tip'>②设置图片时双击可旋转图片,滚动可放大或缩小图片</div>
</div>
</div>
</div>
</Modal>
{this.state.previewGraphicsModal} {this.state.previewGraphicsModal}
</div> </div>
); );
......
...@@ -144,7 +144,12 @@ class GraphicsCourseList extends React.Component { ...@@ -144,7 +144,12 @@ class GraphicsCourseList extends React.Component {
width: 120, width: 120,
dataIndex: 'courseware', dataIndex: 'courseware',
render: (val, item, index) => { 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 { ...@@ -300,19 +305,17 @@ class GraphicsCourseList extends React.Component {
删除 删除
</div> </div>
</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, courseId: item.id,
shelfState: _shelfState, shelfState: _shelfState,
}; };
...@@ -323,6 +326,7 @@ class GraphicsCourseList extends React.Component { ...@@ -323,6 +326,7 @@ class GraphicsCourseList extends React.Component {
} else { } else {
message.success('已取消展示'); message.success('已取消展示');
} }
this.props.changeShelfState(index,_shelfState)
} }
}); });
}; };
......
...@@ -27,6 +27,13 @@ class GraphicsCourse extends React.Component { ...@@ -27,6 +27,13 @@ class GraphicsCourse extends React.Component {
this.handleFetchScheduleList(); this.handleFetchScheduleList();
} }
changeShelfState = (index, shelfState) => {
const { dataSource } = this.state;
dataSource[index].shelfState = shelfState;
this.setState({
dataSource,
});
};
// 获取视频课列表 // 获取视频课列表
handleFetchScheduleList = (_query = {}) => { handleFetchScheduleList = (_query = {}) => {
const query = { const query = {
...@@ -75,6 +82,7 @@ class GraphicsCourse extends React.Component { ...@@ -75,6 +82,7 @@ class GraphicsCourse extends React.Component {
dataSource={dataSource} dataSource={dataSource}
totalCount={totalCount} totalCount={totalCount}
onChange={this.handleFetchScheduleList} onChange={this.handleFetchScheduleList}
changeShelfState={this.changeShelfState}
/> />
</div> </div>
</div> </div>
......
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-05 10:07:47 * @Date: 2020-08-05 10:07:47
* @LastEditors: yuananting * @LastEditors: Please set LastEditors
* @LastEditTime: 2021-06-07 15:06:26 * @LastEditTime: 2021-07-06 14:47:23
* @Description: 线下课新增/编辑页 * @Description: 线下课新增/编辑页
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -38,6 +38,7 @@ import moment from 'moment'; ...@@ -38,6 +38,7 @@ import moment from 'moment';
import Upload from '@/core/upload'; import Upload from '@/core/upload';
import GraphicsEditor from '../components/GraphicsEditor'; import GraphicsEditor from '../components/GraphicsEditor';
import MultipleDatePicker from '@/components/MultipleDatePicker'; import MultipleDatePicker from '@/components/MultipleDatePicker';
import ImgClipModal from '@/components/ImgClipModal'
import './AddOfflineCourse.less'; import './AddOfflineCourse.less';
const { Option } = Select; const { Option } = Select;
...@@ -355,83 +356,12 @@ class AddOfflineCourse extends React.Component { ...@@ -355,83 +356,12 @@ class AddOfflineCourse extends React.Component {
} }
handleSelectCover = (file)=> { handleSelectCover = (file)=> {
this.uploadCoverImage(file);
}
//上传图片
uploadCoverImage = (imageFile) => {
const { folderName } = imageFile;
const fileName = window.random_string(16) + folderName.slice(folderName.lastIndexOf("."));
const self = this;
this.setState(
{
visible: true,
},
() => {
setTimeout(() => {
const okBtnDom = document.querySelector("#headPicModal");
const options = {
size: [500, 282],
ok: okBtnDom,
maxZoom: 3,
style: {
jpgFillColor: "transparent",
},
done: function (dataUrl) {
clearTimeout(self.timer);
self.timer = setTimeout(() => {
if ((self.state.rotate != this.rotate()) || (self.state.scale != this.scale())) {
const _dataUrl = this.clip()
const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl);
self.setState({
cutImageBlob,
dataUrl: _dataUrl,
rotate: this.rotate(),
scale: this.scale()
})
}
}, 500)
const cutImageBlob = self.convertBase64UrlToBlob(dataUrl);
self.setState({
cutImageBlob,
dataUrl
})
setTimeout(() => {
cutFlag = false;
}, 2000);
},
fail: (failInfo) => {
message.error("图片上传失败了,请重新上传");
},
loadComplete: function (img) {
setTimeout(() => {
const _dataUrl = this.clip()
self.setState({
dataUrl: _dataUrl,
hasImgReady: true
})
}, 100)
},
};
const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}`
if (!this.state.photoclip) {
const _photoclip = new PhotoClip("#headPicModal", options);
_photoclip.load(imgUrl);
this.setState({ this.setState({
photoclip: _photoclip, visible: true,
imageFile:file
}); });
} else {
this.state.photoclip.clear();
this.state.photoclip.load(imgUrl);
} }
}, 200);
}
);
};
//获取resourceId //获取resourceId
getSignature = (blob, fileName) => { getSignature = (blob, fileName) => {
...@@ -454,18 +384,16 @@ class AddOfflineCourse extends React.Component { ...@@ -454,18 +384,16 @@ class AddOfflineCourse extends React.Component {
}) })
} }
// base64转换成blob
convertBase64UrlToBlob = (urlData) => {
const bytes = window.atob(urlData.split(",")[1]);
const ab = new ArrayBuffer(bytes.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: "image/png" });
};
preSubmit = () => { preSubmit = () => {
//过期判断
if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) {
Modal.warning({
title:"服务已到期",
content: "当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买",
okText: "我知道了"
})
return
}
const { courseId } = this.state; const { courseId } = this.state;
if (courseId) { if (courseId) {
this.checkDetail(courseId).then(bool => bool ? this.handleSubmit() : message.warning('课程已开始,无法继续编辑')) this.checkDetail(courseId).then(bool => bool ? this.handleSubmit() : message.warning('课程已开始,无法继续编辑'))
...@@ -777,6 +705,7 @@ class AddOfflineCourse extends React.Component { ...@@ -777,6 +705,7 @@ class AddOfflineCourse extends React.Component {
quota, quota,
offlinePlace, offlinePlace,
isEditDisablie, isEditDisablie,
imageFile,
} = this.state; } = this.state;
const isDefaultCover = coverUrl === defaultCoverUrl; const isDefaultCover = coverUrl === defaultCoverUrl;
return ( return (
...@@ -1292,65 +1221,9 @@ class AddOfflineCourse extends React.Component { ...@@ -1292,65 +1221,9 @@ class AddOfflineCourse extends React.Component {
onSelect={this.handleSelectCover} onSelect={this.handleSelectCover}
/> />
} }
<Modal { visible &&
title="设置图片" <ImgClipModal visible={visible} imgUrl={imageFile.ossUrl} onConfirm={this.getSignature} onClose={()=>{this.setState({ visible: false });}}/>
width={1080} }
visible={visible}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
onCancel={() => {
this.setState({ visible: false });
}}
zIndex={10001}
footer={[
<Button
key="back"
onClick={() => {
this.setState({ visible: false });
}}
>
重新上传
</Button>,
<Button
key="submit"
type="primary"
disabled={!hasImgReady}
onClick={() => {
if (!cutFlag) {
cutFlag = true;
this.refs.hiddenBtn.click();
}
this.getSignature(cutImageBlob);
}}
>
确定
</Button>,
]}
>
<div className="clip-box">
<div
id="headPicModal"
ref="headPicModal"
style={{
width: "500px",
height: "430px",
marginBottom: 0,
}}
></div>
<div id="clipBtn" style={{ display: "none" }} ref="hiddenBtn"></div>
<div className="preview-img">
<div className="title">效果预览</div>
<div id="preview-url-box" style={{width:500,height:282}}>
<img src={this.state.dataUrl} style={{ width: '100%' }} alt="" />
</div>
<div className="tip-box">
<div className="tip">温馨提示</div>
<div className="tip">①预览效果图时可能存在延迟,单击左侧图片刷新即可</div>
<div className="tip">②设置图片时双击可旋转图片,滚动可放大或缩小图片</div>
</div>
</div>
</div>
</Modal>
{ this.state.previewOfflineModal } { this.state.previewOfflineModal }
</div> </div>
) )
......
...@@ -10,7 +10,7 @@ import Service from '@/common/js/service'; ...@@ -10,7 +10,7 @@ import Service from '@/common/js/service';
import User from '@/common/js/user'; import User from '@/common/js/user';
import college from '@/common/lottie/college'; import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components'; import { PageControl, XMTable } from '@/components';
import { appId, LIVE_SHARE } from '@/domains/course-domain/constants'; import { LIVE_SHARE } from '@/domains/course-domain/constants';
import CourseService from '@/domains/course-domain/CourseService'; import CourseService from '@/domains/course-domain/CourseService';
import ShareLiveModal from '@/modules/course-manage/modal/ShareLiveModal'; import ShareLiveModal from '@/modules/course-manage/modal/ShareLiveModal';
import OfflineCourseData from '@/modules/course-manage/offline-course/OfflineCourseData'; import OfflineCourseData from '@/modules/course-manage/offline-course/OfflineCourseData';
...@@ -78,7 +78,7 @@ class OfflineCourseList extends React.Component { ...@@ -78,7 +78,7 @@ class OfflineCourseList extends React.Component {
} ${moment(startTime).format('HH:mm')} ~ ${moment(endTime).format('HH:mm')}`; } ${moment(startTime).format('HH:mm')} ~ ${moment(endTime).format('HH:mm')}`;
return ( return (
<div className='record__item'> <div className='record__item'>
<img className='course-cover' src={coverUrl || defaultCoverUrl} /> <img className='course-cover' src={coverUrl || defaultCoverUrl} alt='' />
<div style={{ width: 175 }}> <div style={{ width: 175 }}>
<Tooltip title={courseName}> <Tooltip title={courseName}>
<div className='course-name'>{courseName}</div> <div className='course-name'>{courseName}</div>
...@@ -152,14 +152,14 @@ class OfflineCourseList extends React.Component { ...@@ -152,14 +152,14 @@ class OfflineCourseList extends React.Component {
}, },
{ {
title: '报名时间', title: '报名时间',
width: 181, width: 200,
key: 'apply', key: 'apply',
dataIndex: 'apply', dataIndex: 'apply',
sorter: true, sorter: true,
render: (val, item) => { render: (val, item) => {
return ( return (
<div> <div style={{ whiteSpace: 'nowrap' }}>
{item.startTimeApply ? `${formatDate('MM-DD H:i', item.startTimeApply)} ~ ${formatDate('MM-DD H:i', item.endTimeApply)}` : '-'} {item.startTimeApply ? `${window.formatDate('MM-DD H:i', item.startTimeApply)} ~ ${window.formatDate('MM-DD H:i', item.endTimeApply)}` : '-'}
{item.whetherApplyFull === 'YES' && ( {item.whetherApplyFull === 'YES' && (
<span <span
style={{ style={{
...@@ -185,7 +185,7 @@ class OfflineCourseList extends React.Component { ...@@ -185,7 +185,7 @@ class OfflineCourseList extends React.Component {
dataIndex: 'created', dataIndex: 'created',
sorter: true, sorter: true,
render: (val) => { render: (val) => {
return formatDate('YYYY-MM-DD H:i', val); return <span style={{ whiteSpace: 'nowrap' }}>{window.formatDate('YYYY-MM-DD H:i', val)}</span>;
}, },
}, },
{ {
...@@ -360,8 +360,6 @@ class OfflineCourseList extends React.Component { ...@@ -360,8 +360,6 @@ class OfflineCourseList extends React.Component {
// 显示分享弹窗 // 显示分享弹窗
handleShowShareModal = (record, needStr = false) => { handleShowShareModal = (record, needStr = false) => {
const { courseId } = record; const { courseId } = record;
const _appId = appId;
const htmlUrl = `${LIVE_SHARE}offline_detail/${courseId}?id=${User.getStoreId()}`; const htmlUrl = `${LIVE_SHARE}offline_detail/${courseId}?id=${User.getStoreId()}`;
const longUrl = htmlUrl; const longUrl = htmlUrl;
const { courseName, courseMediaVOS } = record; const { courseName, courseMediaVOS } = record;
...@@ -437,7 +435,7 @@ class OfflineCourseList extends React.Component { ...@@ -437,7 +435,7 @@ class OfflineCourseList extends React.Component {
columns={this.parseColumns()} columns={this.parseColumns()}
onChange={this.handleChangeTable} onChange={this.handleChangeTable}
pagination={false} pagination={false}
scroll={{ x: 1500 }} scroll={{ x: 1300 }}
bordered bordered
className='offline-list-table' className='offline-list-table'
/> />
......
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-05 10:07:47 * @Date: 2020-08-05 10:07:47
* @LastEditors: wufan * @LastEditors: Please set LastEditors
* @LastEditTime: 2021-05-30 20:35:49 * @LastEditTime: 2021-07-08 19:34:10
* @Description: 视频课新增/编辑页 * @Description: 视频课新增/编辑页
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -11,10 +11,9 @@ import React from 'react' ...@@ -11,10 +11,9 @@ import React from 'react'
import { Button, Input, Radio, message, Modal, Cascader } from 'antd' import { Button, Input, Radio, message, Modal, Cascader } from 'antd'
import { DISK_MAP, FileTypeIcon, FileVerifyMap } from '@/common/constants/academic/lessonEnum' import { DISK_MAP, FileTypeIcon, FileVerifyMap } from '@/common/constants/academic/lessonEnum'
import { ImgCutModalNew } from '@/components'
import ShowTips from '@/components/ShowTips' import ShowTips from '@/components/ShowTips'
import Breadcrumbs from '@/components/Breadcrumbs' import Breadcrumbs from '@/components/Breadcrumbs'
import moment from 'moment'
import AddVideoIntro from './components/AddVideoIntro' import AddVideoIntro from './components/AddVideoIntro'
import SelectStudent from '../modal/select-student' import SelectStudent from '../modal/select-student'
import SelectPrepareFileModal from '../../prepare-lesson/modal/SelectPrepareFileModal' import SelectPrepareFileModal from '../../prepare-lesson/modal/SelectPrepareFileModal'
...@@ -26,8 +25,8 @@ import User from '@/common/js/user' ...@@ -26,8 +25,8 @@ import User from '@/common/js/user'
import _ from 'underscore' import _ from 'underscore'
import Upload from '@/core/upload' import Upload from '@/core/upload'
import { randomString } from '@/domains/basic-domain/utils' import { randomString } from '@/domains/basic-domain/utils'
import ImgClipModal from '@/components/ImgClipModal'
import $ from 'jquery' import $ from 'jquery'
// import PhotoClip from 'photoclip';
import './AddVideoCourse.less' import './AddVideoCourse.less'
const EDIT_BOX_KEY = Math.random() const EDIT_BOX_KEY = Math.random()
...@@ -262,7 +261,6 @@ class AddVideoCourse extends React.Component { ...@@ -262,7 +261,6 @@ class AddVideoCourse extends React.Component {
_.each(studentIds, (item) => { _.each(studentIds, (item) => {
studentList.push({ studentId: item }) studentList.push({ studentId: item })
}) })
// this.setState({ studentModal: null });
this.setState({ studentList }) this.setState({ studentList })
this.setState({ studentModal: false }) this.setState({ studentModal: false })
} }
...@@ -321,6 +319,15 @@ class AddVideoCourse extends React.Component { ...@@ -321,6 +319,15 @@ class AddVideoCourse extends React.Component {
// 保存 // 保存
handleSubmit = () => { handleSubmit = () => {
//过期判断
if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) {
Modal.warning({
title:"服务已到期",
content: "当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买",
okText: "我知道了"
})
return
}
const { instId, adminId } = window.currentUserInstInfo const { instId, adminId } = window.currentUserInstInfo
const { const {
...@@ -419,81 +426,11 @@ class AddVideoCourse extends React.Component { ...@@ -419,81 +426,11 @@ class AddVideoCourse extends React.Component {
}) })
} }
handleSelectCover = (file) => { handleSelectCover = (file) => {
this.uploadImage(file)
}
//上传图片
uploadImage = (imageFile) => {
const { folderName } = imageFile
const fileName = window.random_string(16) + folderName.slice(folderName.lastIndexOf('.'))
const self = this
this.setState(
{
visible: true
},
() => {
setTimeout(() => {
const okBtnDom = document.querySelector('#headPicModal')
const options = {
size: [500, 282],
ok: okBtnDom,
maxZoom: 3,
style: {
jpgFillColor: 'transparent'
},
done: function (dataUrl) {
clearTimeout(self.timer)
self.timer = setTimeout(() => {
if (self.state.rotate != this.rotate() || self.state.scale != this.scale()) {
const _dataUrl = this.clip()
const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl)
self.setState({
cutImageBlob,
dataUrl: _dataUrl,
rotate: this.rotate(),
scale: this.scale()
})
}
}, 500)
const cutImageBlob = self.convertBase64UrlToBlob(dataUrl)
self.setState({
cutImageBlob,
dataUrl
})
setTimeout(() => {
cutFlag = false
}, 2000)
},
fail: (failInfo) => {
message.error('图片上传失败了,请重新上传')
},
loadComplete: function (img) {
setTimeout(() => {
const _dataUrl = this.clip()
self.setState({
dataUrl: _dataUrl,
hasImgReady: true
})
}, 100)
}
}
const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}`
if (!this.state.photoclip) {
const _photoclip = new PhotoClip('#headPicModal', options)
_photoclip.load(imgUrl)
this.setState({ this.setState({
photoclip: _photoclip visible: true,
}) imageFile:file
} else { });
this.state.photoclip.clear()
this.state.photoclip.load(imgUrl)
}
}, 200)
}
)
} }
//获取resourceId //获取resourceId
getSignature = (blob, fileName) => { getSignature = (blob, fileName) => {
Upload.uploadBlobToOSS(blob, 'cover' + new Date().valueOf(), null, 'signInfo').then((signInfo) => { Upload.uploadBlobToOSS(blob, 'cover' + new Date().valueOf(), null, 'signInfo').then((signInfo) => {
...@@ -515,16 +452,6 @@ class AddVideoCourse extends React.Component { ...@@ -515,16 +452,6 @@ class AddVideoCourse extends React.Component {
coverId: coverId coverId: coverId
}) })
} }
// base64转换成blob
convertBase64UrlToBlob = (urlData) => {
const bytes = window.atob(urlData.split(',')[1])
const ab = new ArrayBuffer(bytes.length)
const ia = new Uint8Array(ab)
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i)
}
return new Blob([ab], { type: 'image/png' })
}
render() { render() {
const { const {
pageType, pageType,
...@@ -727,61 +654,9 @@ class AddVideoCourse extends React.Component { ...@@ -727,61 +654,9 @@ class AddVideoCourse extends React.Component {
onSelect={this.handleSelectCover} onSelect={this.handleSelectCover}
/> />
)} )}
<Modal { visible &&
title='设置图片' <ImgClipModal visible={visible} imgUrl={imageFile.ossUrl} onConfirm={this.getSignature} onClose={()=>{this.setState({ visible: false });}}/>
width={1080}
visible={visible}
maskClosable={false}
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
onCancel={() => {
this.setState({ visible: false })
}}
zIndex={10001}
footer={[
<Button
key='back'
onClick={() => {
this.setState({ visible: false })
}}>
重新上传
</Button>,
<Button
key='submit'
type='primary'
disabled={!hasImgReady}
onClick={() => {
if (!cutFlag) {
cutFlag = true
this.refs.hiddenBtn.click()
} }
this.getSignature(cutImageBlob)
}}>
确定
</Button>
]}>
<div className='clip-box'>
<div
id='headPicModal'
ref='headPicModal'
style={{
width: '500px',
height: '430px',
marginBottom: 0
}}></div>
<div id='clipBtn' style={{ display: 'none' }} ref='hiddenBtn'></div>
<div className='preview-img'>
<div className='title'>效果预览</div>
<div id='preview-url-box' style={{ width: 500, height: 282 }}>
<img src={this.state.dataUrl} style={{ width: '100%' }} alt='' />
</div>
<div className='tip-box'>
<div className='tip'>温馨提示</div>
<div className='tip'>①预览效果图时可能存在延迟,单击左侧图片刷新即可</div>
<div className='tip'>②设置图片时双击可旋转图片,滚动可放大或缩小图片</div>
</div>
</div>
</div>
</Modal>
{this.state.previewCourseModal} {this.state.previewCourseModal}
</div> </div>
) )
......
import { Dropdown, message, Modal, Switch, Table, Tooltip } from 'antd'; import { Dropdown, message, Modal, Switch, Tooltip } from 'antd';
import User from '@/common/js/user'; import User from '@/common/js/user';
import { PageControl } from '@/components'; import { PageControl } from '@/components';
import { LIVE_SHARE } from '@/domains/course-domain/constants'; import { LIVE_SHARE } from '@/domains/course-domain/constants';
...@@ -185,7 +185,7 @@ class VideoCourseList extends React.Component { ...@@ -185,7 +185,7 @@ class VideoCourseList extends React.Component {
dataIndex: 'created', dataIndex: 'created',
sorter: true, sorter: true,
render: (val) => { render: (val) => {
return formatDate('YYYY-MM-DD H:i', val); return window.formatDate('YYYY-MM-DD H:i', val);
}, },
}, },
{ {
...@@ -195,7 +195,7 @@ class VideoCourseList extends React.Component { ...@@ -195,7 +195,7 @@ class VideoCourseList extends React.Component {
dataIndex: 'updated', dataIndex: 'updated',
sorter: true, sorter: true,
render: (val) => { render: (val) => {
return formatDate('YYYY-MM-DD H:i', val); return window.formatDate('YYYY-MM-DD H:i', val);
}, },
}, },
{ {
...@@ -211,7 +211,7 @@ class VideoCourseList extends React.Component { ...@@ -211,7 +211,7 @@ class VideoCourseList extends React.Component {
<Tooltip title={this.handlePlanName(record.relatedPlanList)} placement='top' arrowPointAtCenter> <Tooltip title={this.handlePlanName(record.relatedPlanList)} placement='top' arrowPointAtCenter>
{record.relatedPlanList.map((item, index) => { {record.relatedPlanList.map((item, index) => {
return ( return (
<span> <span key={item.planId}>
{item.planName} {index < record.relatedPlanList.length - 1 && <span></span>} {item.planName} {index < record.relatedPlanList.length - 1 && <span></span>}
</span> </span>
); );
...@@ -296,7 +296,7 @@ class VideoCourseList extends React.Component { ...@@ -296,7 +296,7 @@ class VideoCourseList extends React.Component {
handlePlanName = (planArray) => { handlePlanName = (planArray) => {
let planStr = ''; let planStr = '';
planArray.map((item, index) => { planArray.forEach((item, index) => {
if (index < planArray.length - 1) { if (index < planArray.length - 1) {
planStr = planStr + item.planName + '、'; planStr = planStr + item.planName + '、';
} else { } else {
......
...@@ -2,34 +2,35 @@ ...@@ -2,34 +2,35 @@
// padding: 0 16px 16px; // padding: 0 16px 16px;
min-width: 1100px; min-width: 1100px;
position: relative; position: relative;
z-index:3; z-index: 3;
.g2-tooltip-marker { .g2-tooltip-marker {
border-radius: 50% !important; border-radius: 50% !important;
} }
.home-title { .home-title {
color: #333; color: #333;
padding-left:28px; padding-left: 28px;
font-size:16px; font-size: 16px;
font-weight:bold; font-weight: bold;
position: relative; position: relative;
padding-top:16px; padding-top: 16px;
&::before{ &::before {
width:4px; width: 4px;
height:12px; height: 12px;
content:''; content: '';
background-image: linear-gradient(#2966FF 83.5%, #0ACCA4 16.5%); background-image: linear-gradient(#2966ff 83.5%, #0acca4 16.5%);
display:inline-block; display: inline-block;
position: absolute; position: absolute;
left:16px; left: 16px;
top:22px; top: 22px;
} }
} }
@font-face { @font-face {
font-family: 'number'; font-family: 'number';
src: url('https://image.xiaomaiketang.com/xm/n2sADd2jY6.TTF'); src: url('https://image.xiaomaiketang.com/xm/n2sADd2jY6.TTF');
} }
.data-wrap{ .data-wrap {
background: #FFF; background: #fff;
padding-bottom: 25px;
.data-box { .data-box {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
...@@ -41,7 +42,7 @@ ...@@ -41,7 +42,7 @@
width: ~'calc(16.67% - 8px)'; width: ~'calc(16.67% - 8px)';
padding: 16px; padding: 16px;
&.course-data { &.course-data {
width: ~'calc(50% - 24px)'; width: 33%;
} }
.header { .header {
display: flex; display: flex;
...@@ -55,7 +56,7 @@ ...@@ -55,7 +56,7 @@
font-size: 14px; font-size: 14px;
line-height: 18px; line-height: 18px;
color: #333; color: #333;
font-weight:500; font-weight: 500;
} }
} }
.data-number { .data-number {
...@@ -74,7 +75,7 @@ ...@@ -74,7 +75,7 @@
.iconfont { .iconfont {
font-size: 12px; font-size: 12px;
margin-right: 4px; margin-right: 4px;
color: #EC4B35; color: #ec4b35;
} }
.footer-number { .footer-number {
font-size: 12px; font-size: 12px;
...@@ -83,17 +84,17 @@ ...@@ -83,17 +84,17 @@
} }
.course-box { .course-box {
border-radius: 4px; border-radius: 4px;
background: #FAFAFA; background: #fafafa;
height: 124px; height: 124px;
width: 66%; width: 60%;
position: absolute; position: absolute;
right: 16px; right: 16px;
top: 16px; top: 28px;
padding: 8px 24px 0; padding: 8px 0 0;
.course-item { .course-item {
display: inline-block; display: inline-block;
width: 50%; width: 50%;
padding: 4px 0 12px; padding: 4px 24px 12px;
.course-title { .course-title {
font-size: 12px; font-size: 12px;
color: #999; color: #999;
...@@ -105,6 +106,7 @@ ...@@ -105,6 +106,7 @@
font-size: 16px; font-size: 16px;
font-family: 'number'; font-family: 'number';
margin-right: 16px; margin-right: 16px;
white-space: nowrap;
} }
.course-word { .course-word {
font-size: 12px; font-size: 12px;
...@@ -114,11 +116,12 @@ ...@@ -114,11 +116,12 @@
.iconfont { .iconfont {
font-size: 12px; font-size: 12px;
margin-right: 4px; margin-right: 4px;
color: #EC4B35; color: #ec4b35;
} }
.add-number { .add-number {
font-size: 12px; font-size: 12px;
color: #999; color: #999;
white-space: nowrap;
} }
} }
} }
...@@ -126,9 +129,9 @@ ...@@ -126,9 +129,9 @@
} }
} }
} }
.study-wrap{ .study-wrap {
background: #FFF; background: #fff;
margin-top:16px; margin-top: 16px;
.study-box { .study-box {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
...@@ -148,7 +151,7 @@ ...@@ -148,7 +151,7 @@
margin-bottom: 12px; margin-bottom: 12px;
.iconfont { .iconfont {
font-size: 14px; font-size: 14px;
color: #BFBFBF; color: #bfbfbf;
margin-left: 4px; margin-left: 4px;
} }
.tip { .tip {
...@@ -169,13 +172,13 @@ ...@@ -169,13 +172,13 @@
color: #666; color: #666;
cursor: pointer; cursor: pointer;
&.selected { &.selected {
color: #2966FF; color: #2966ff;
&::after { &::after {
position: absolute; position: absolute;
width: 24px; width: 24px;
height: 2px; height: 2px;
content: ''; content: '';
background: #2966FF; background: #2966ff;
border-radius: 1px; border-radius: 1px;
left: 9px; left: 9px;
bottom: -4px; bottom: -4px;
...@@ -196,7 +199,7 @@ ...@@ -196,7 +199,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
&.odd { &.odd {
background: #FAFAFA; background: #fafafa;
} }
.table-image { .table-image {
width: 24px; width: 24px;
...@@ -257,7 +260,7 @@ ...@@ -257,7 +260,7 @@
width: 204px; width: 204px;
height: 204px; height: 204px;
border-radius: 102px; border-radius: 102px;
background: #F1F3F6; background: #f1f3f6;
margin-top: -20px; margin-top: -20px;
.small-circle { .small-circle {
display: flex; display: flex;
...@@ -280,13 +283,13 @@ ...@@ -280,13 +283,13 @@
&.unfinished { &.unfinished {
top: 152px; top: 152px;
.spot { .spot {
background: #2966FF; background: #2966ff;
} }
} }
&.finished { &.finished {
top: 232px; top: 232px;
.spot { .spot {
background: #FFBB54; background: #ffbb54;
} }
} }
.spot { .spot {
...@@ -352,14 +355,14 @@ ...@@ -352,14 +355,14 @@
align-items: center; align-items: center;
color: #666; color: #666;
.student-dot { .student-dot {
background: #2966FF; background: #2966ff;
height: 8px; height: 8px;
width: 8px; width: 8px;
border-radius: 50%; border-radius: 50%;
margin-right: 8px; margin-right: 8px;
} }
.time-dot { .time-dot {
background: #FEB613; background: #feb613;
height: 8px; height: 8px;
width: 8px; width: 8px;
border-radius: 50%; border-radius: 50%;
...@@ -382,7 +385,8 @@ ...@@ -382,7 +385,8 @@
font-size: 14px; font-size: 14px;
} }
.g2-tooltip-list { .g2-tooltip-list {
li,span { li,
span {
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 14px; 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
/* /*
* @Author: zhangleyuan * @Author: zhangleyuan
* @Date: 2021-02-20 16:45:51 * @Date: 2021-02-20 16:45:51
* @LastEditors: fusanqiasng * @LastEditors: Please set LastEditors
* @LastEditTime: 2021-06-01 15:20:33 * @LastEditTime: 2021-07-06 14:48:54
* @Description: 描述一下 * @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -12,7 +12,7 @@ import { withRouter } from 'react-router-dom'; ...@@ -12,7 +12,7 @@ import { withRouter } from 'react-router-dom';
import SelectOperatorModal from '../modal/SelectOperatorModal'; import SelectOperatorModal from '../modal/SelectOperatorModal';
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal'; import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
import Upload from '@/core/upload'; import Upload from '@/core/upload';
// import PhotoClip from 'photoclip' import ImgClipModal from '@/components/ImgClipModal'
import './BasicInfo.less'; import './BasicInfo.less';
const { TextArea } = Input; const { TextArea } = Input;
...@@ -22,6 +22,7 @@ class BasicInfo extends React.Component { ...@@ -22,6 +22,7 @@ class BasicInfo extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
imageFile: null, // 需要被截取的图片
operatorModalVisible: false, operatorModalVisible: false,
showSelectFileModal: false, showSelectFileModal: false,
cutImageBlob: null, cutImageBlob: null,
...@@ -71,80 +72,11 @@ class BasicInfo extends React.Component { ...@@ -71,80 +72,11 @@ class BasicInfo extends React.Component {
}, 1000); }, 1000);
}; };
handleSelectCover = (file) => { handleSelectCover = (file) => {
this.uploadImage(file);
};
//上传图片
uploadImage = (imageFile) => {
const self = this;
this.setState(
{
visible: true,
},
() => {
setTimeout(() => {
const okBtnDom = document.querySelector('#headPicModal');
const options = {
size: [500, 282],
ok: okBtnDom,
maxZoom: 3,
style: {
jpgFillColor: 'transparent',
},
done: function (dataUrl) {
clearTimeout(self.timer);
self.timer = setTimeout(() => {
if (self.state.rotate !== this.rotate() || self.state.scale !== this.scale()) {
console.log(this.scale(), 'scale');
const _dataUrl = this.clip();
const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl);
self.setState({
cutImageBlob,
dataUrl: _dataUrl,
rotate: this.rotate(),
scale: this.scale(),
});
}
}, 500);
const cutImageBlob = self.convertBase64UrlToBlob(dataUrl);
self.setState({
cutImageBlob,
dataUrl,
});
setTimeout(() => {
cutFlag = false;
}, 2000);
},
fail: (failInfo) => {
message.error('图片上传失败了,请重新上传');
},
loadComplete: function (img) {
setTimeout(() => {
const _dataUrl = this.clip();
self.setState({
dataUrl: _dataUrl,
hasImgReady: true,
});
}, 100);
},
};
const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}`;
if (!this.state.photoclip) {
const _photoclip = new PhotoClip('#headPicModal', options);
_photoclip.load(imgUrl);
this.setState({ this.setState({
photoclip: _photoclip, visible: true,
imageFile:file
}); });
} else {
this.state.photoclip.clear();
this.state.photoclip.load(imgUrl);
}
}, 200);
}
);
}; };
//获取resourceId //获取resourceId
getSignature = (blob, fileName) => { getSignature = (blob, fileName) => {
Upload.uploadBlobToOSS(blob, 'cover' + new Date().valueOf(), null, 'signInfo').then((signInfo) => { Upload.uploadBlobToOSS(blob, 'cover' + new Date().valueOf(), null, 'signInfo').then((signInfo) => {
...@@ -168,16 +100,6 @@ class BasicInfo extends React.Component { ...@@ -168,16 +100,6 @@ class BasicInfo extends React.Component {
this.props.onChange('coverId', coverId); this.props.onChange('coverId', coverId);
}, 1000); }, 1000);
}; };
// base64转换成blob
convertBase64UrlToBlob = (urlData) => {
const bytes = window.atob(urlData.split(',')[1]);
const ab = new ArrayBuffer(bytes.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: 'image/png' });
};
limitNumber = (value) => { limitNumber = (value) => {
if (typeof value === 'string') { if (typeof value === 'string') {
return !isNaN(Number(value)) ? value.replace(/^(0+)|[^\d]/g, '') : ''; return !isNaN(Number(value)) ? value.replace(/^(0+)|[^\d]/g, '') : '';
...@@ -203,7 +125,7 @@ class BasicInfo extends React.Component { ...@@ -203,7 +125,7 @@ class BasicInfo extends React.Component {
}; };
render() { render() {
const { operatorModalVisible, showSelectFileModal, visible, hasImgReady, cutImageBlob } = this.state; const { operatorModalVisible, showSelectFileModal, visible, hasImgReady, cutImageBlob,imageFile} = this.state;
const { data } = this.props; const { data } = this.props;
const { planName, coverUrl, instro, enableState, operateType, selectOperatorList, percentCompleteLive, percentCompleteVideo, percentCompletePicture } = const { planName, coverUrl, instro, enableState, operateType, selectOperatorList, percentCompleteLive, percentCompleteVideo, percentCompletePicture } =
data; data;
...@@ -403,61 +325,9 @@ class BasicInfo extends React.Component { ...@@ -403,61 +325,9 @@ class BasicInfo extends React.Component {
onSelect={this.handleSelectCover} onSelect={this.handleSelectCover}
/> />
)} )}
<Modal { visible &&
title='设置图片' <ImgClipModal visible={visible} imgUrl={imageFile.ossUrl} onConfirm={this.getSignature} onClose={()=>{this.setState({ visible: false });}}/>
width={1080}
visible={visible}
maskClosable={false}
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
onCancel={() => {
this.setState({ visible: false });
}}
zIndex={10001}
footer={[
<Button
key='back'
onClick={() => {
this.setState({ visible: false });
}}>
重新上传
</Button>,
<Button
key='submit'
type='primary'
disabled={!hasImgReady}
onClick={() => {
if (!cutFlag) {
cutFlag = true;
this.refs.hiddenBtn.click();
} }
this.getSignature(cutImageBlob);
}}>
确定
</Button>,
]}>
<div className='clip-box'>
<div
id='headPicModal'
ref='headPicModal'
style={{
width: '500px',
height: '430px',
marginBottom: 0,
}}></div>
<div id='clipBtn' style={{ display: 'none' }} ref='hiddenBtn'></div>
<div className='preview-img'>
<div className='title'>效果预览</div>
<div id='preview-url-box' style={{ width: 500, height: 282 }}>
<img src={this.state.dataUrl} style={{ width: '100%' }} alt='' />
</div>
<div className='tip-box'>
<div className='tip'>温馨提示</div>
<div className='tip'>①预览效果图时可能存在延迟,单击左侧图片刷新即可</div>
<div className='tip'>②设置图片时双击可旋转图片,滚动可放大或缩小图片</div>
</div>
</div>
</div>
</Modal>
</div> </div>
); );
} }
......
...@@ -8,9 +8,9 @@ ...@@ -8,9 +8,9 @@
*/ */
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Table, Modal, message, Tooltip, Switch, Dropdown } from 'antd'; import { Modal, message, Tooltip, Switch, Dropdown } from 'antd';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import { PageControl, XMTable } from "@/components"; import { PageControl, XMTable } from '@/components';
import PlanService from '@/domains/plan-domain/planService'; import PlanService from '@/domains/plan-domain/planService';
import SharePlanModal from '../modal/SharePlanModal'; import SharePlanModal from '../modal/SharePlanModal';
import { LIVE_SHARE } from '@/domains/course-domain/constants'; import { LIVE_SHARE } from '@/domains/course-domain/constants';
...@@ -29,6 +29,7 @@ function PlanList(props) { ...@@ -29,6 +29,7 @@ function PlanList(props) {
key: 'planName', key: 'planName',
dataIndex: 'planName', dataIndex: 'planName',
width: '18%', width: '18%',
fixed: 'left',
render: (val, record) => { render: (val, record) => {
return ( return (
<div className='plan_name_item'> <div className='plan_name_item'>
...@@ -86,7 +87,7 @@ function PlanList(props) { ...@@ -86,7 +87,7 @@ function PlanList(props) {
dataIndex: 'created', dataIndex: 'created',
sorter: true, sorter: true,
render: (val) => { render: (val) => {
return window.formatDate('YYYY-MM-DD H:i', val); return <span style={{ whiteSpace: 'nowrap' }}>{window.formatDate('YYYY-MM-DD H:i', val)}</span>;
}, },
}, },
{ {
...@@ -96,12 +97,12 @@ function PlanList(props) { ...@@ -96,12 +97,12 @@ function PlanList(props) {
dataIndex: 'updated', dataIndex: 'updated',
sorter: true, sorter: true,
render: (val) => { render: (val) => {
return window.formatDate('YYYY-MM-DD H:i', val); return <span style={{ whiteSpace: 'nowrap' }}>{window.formatDate('YYYY-MM-DD H:i', val)}</span>;
}, },
}, },
{ {
title: '参培人数', title: '参培人数',
width: 76, width: '10%',
key: 'cultureCustomerNum', key: 'cultureCustomerNum',
dataIndex: 'cultureCustomerNum', dataIndex: 'cultureCustomerNum',
sorter: true, sorter: true,
...@@ -114,7 +115,7 @@ function PlanList(props) { ...@@ -114,7 +115,7 @@ function PlanList(props) {
key: 'operate', key: 'operate',
dataIndex: 'operate', dataIndex: 'operate',
fixed: 'right', fixed: 'right',
width: 176, width: '14.5%',
render: (val, record) => { render: (val, record) => {
return ( return (
<div className='operate'> <div className='operate'>
...@@ -317,7 +318,7 @@ function PlanList(props) { ...@@ -317,7 +318,7 @@ function PlanList(props) {
scroll={{ x: 1400 }} scroll={{ x: 1400 }}
className='plan-list-table' className='plan-list-table'
renderEmpty={{ renderEmpty={{
description: <span style={{ display: 'block', paddingBottom: 24 }}>暂无数据</span> description: <span style={{ display: 'block', paddingBottom: 24 }}>暂无数据</span>,
}} }}
/> />
<div className='box-footer'> <div className='box-footer'>
......
.training-task{ .training-task {
thead{ thead {
display:none; display: none;
} }
.ant-form-item{ .ant-form-item {
margin-bottom:0 !important; margin-bottom: 0 !important;
} }
.add-task-con{ .add-task-con {
height: 52px; height: 52px;
background: #F7F8F9; background: #f7f8f9;
border-radius: 2px; border-radius: 2px;
padding:16px; padding: 16px;
margin-top:16px; margin-top: 16px;
.add-task-btn-disabled{ .add-task-btn-disabled {
color:#CCCCCC; color: #cccccc;
font-size:14px; font-size: 14px;
} }
.add-task-btn{ .add-task-btn {
color: #2966FF; color: #2966ff;
font-size:14px; font-size: 14px;
} }
} }
} }
.plan-sort-task-item{ .plan-sort-task-item {
.task-con{ .task-con {
display:flex; display: flex;
padding:16px; padding: 16px;
background: #F7F8F9; background: #f7f8f9;
border-radius: 2px; border-radius: 2px;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
.task-instro{ .task-instro {
display:flex; display: flex;
align-items: center; align-items: center;
.open-icon{ .open-icon {
color:#999999; color: #999999;
font-size:10px; font-size: 10px;
} }
.task-name-con{ .task-name-con {
display:flex; display: flex;
align-items: center; align-items: center;
color:#333333; color: #333333;
font-size:14px; font-size: 14px;
.number{ .number {
margin-right:10px; margin-right: 10px;
margin-left:10px; margin-left: 10px;
} }
.task-name-input{ .task-name-input {
width: 300px; width: 300px;
height: 32px; height: 32px;
background: #FFFFFF; background: #ffffff;
border-radius: 4px; border-radius: 4px;
} }
} }
} }
.operate{ .operate {
display: none; display: none;
.operate__item{ .operate__item {
cursor:pointer; cursor: pointer;
margin-left:16px; margin-left: 16px;
color:#666666; color: #666666;
font-size:14px; font-size: 14px;
.icon{ .icon {
font-size:14px; font-size: 14px;
color:#999; color: #999;
} }
.text{ .text {
margin-left:8px; margin-left: 8px;
} }
} }
} }
&:hover{ &:hover {
.operate{ .operate {
display:block; display: block;
} }
} }
} }
.course-box{ .course-box {
.add-course-con{ .add-course-con {
padding:16px 51px; padding: 16px 51px;
color: #2966FF; color: #2966ff;
font-size:14px; font-size: 14px;
.add-course-btn-disabled{ .add-course-btn-disabled {
font-size:14px; font-size: 14px;
color:#ccc; color: #ccc;
pointer-events: none;
} }
} }
} }
} }
.plan-course-sort-item{ .plan-course-sort-item {
display:flex; display: flex;
padding:16px 16px 16px 0px; padding: 16px 16px 16px 0px;
margin-left:51px; margin-left: 51px;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
border-bottom:1px dotted #E8E8E8; border-bottom: 1px dotted #e8e8e8;
&:hover{ &:hover {
.course-operate{ .course-operate {
display:block; display: block;
} }
} }
.course-operate{ .course-operate {
display: none; display: none;
.operate__item{ .operate__item {
cursor:pointer; cursor: pointer;
margin-left:16px; margin-left: 16px;
color:#666666; color: #666666;
font-size:14px; font-size: 14px;
.icon{ .icon {
font-size:14px; font-size: 14px;
color:#999; color: #999;
} }
.text{ .text {
margin-left:8px; margin-left: 8px;
} }
} }
} }
.course-info{ .course-info {
.ant-form{ .ant-form {
display:inline-block; display: inline-block;
} }
.course-name-input{ .course-name-input {
margin-right:8px; margin-right: 8px;
} }
.course-type{ .course-type {
font-size:11px; font-size: 11px;
color:#666666; color: #666666;
padding:0px 6px; padding: 0px 6px;
border: 1px solid #999999; border: 1px solid #999999;
margin-right:4px; margin-right: 4px;
border-radius: 2px; border-radius: 2px;
line-height: 16px; line-height: 16px;
} }
.course-name{ .course-name {
color:#666666; color: #666666;
font-size:14px; font-size: 14px;
margin-right:8px; margin-right: 8px;
} }
.tip{ .tip {
font-size:14px; font-size: 14px;
color:#FF4F4F; color: #ff4f4f;
margin-right:2px; margin-right: 2px;
} }
.course-state{ .course-state {
color:#999; color: #999;
font-size:14px; font-size: 14px;
} }
} }
} }
...@@ -494,7 +494,7 @@ class SelectOperatorModal extends React.Component { ...@@ -494,7 +494,7 @@ class SelectOperatorModal extends React.Component {
} else { } else {
_list = _.reject(selectLive, (item) => item.liveCourseId === record.liveCourseId); _list = _.reject(selectLive, (item) => item.liveCourseId === record.liveCourseId);
} }
if (_list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length > 20) { if (_list.length + currentTaskCourseData.length + selectVideo.internal.length + selectVideo.external.length + selectPicture.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程'); message.warning('无法继续选择,一个任务最多关联20个课程');
return; return;
} }
...@@ -505,14 +505,14 @@ class SelectOperatorModal extends React.Component { ...@@ -505,14 +505,14 @@ class SelectOperatorModal extends React.Component {
const { selectVideo, currentTaskCourseData, selectLive, selectPicture, videoCourseDivision } = this.state; const { selectVideo, currentTaskCourseData, selectLive, selectPicture, videoCourseDivision } = this.state;
let { [videoCourseDivision]: selectList } = selectVideo; let { [videoCourseDivision]: selectList } = selectVideo;
let otherVideoCourseDivision = videoCourseDivision === 'internal' ? 'external' : 'internal';
let _list = []; let _list = [];
if (selected || !_.find(selectList, (item) => item.id === record.id)) { if (selected || !_.find(selectList, (item) => item.id === record.id)) {
_list = _.uniq(selectList.concat([record]), false, (item) => item.id); _list = _.uniq(selectList.concat([record]), false, (item) => item.id);
} else { } else {
_list = _.reject(selectList, (item) => item.id === record.id); _list = _.reject(selectList, (item) => item.id === record.id);
} }
if (_list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length > 20) { if (_list.length + selectVideo[otherVideoCourseDivision]?.length + currentTaskCourseData.length + selectLive.length + selectPicture.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程'); message.warning('无法继续选择,一个任务最多关联20个课程');
return; return;
} }
...@@ -532,7 +532,7 @@ class SelectOperatorModal extends React.Component { ...@@ -532,7 +532,7 @@ class SelectOperatorModal extends React.Component {
} else { } else {
_list = _.reject(selectPicture, (item) => item.id === record.id); _list = _.reject(selectPicture, (item) => item.id === record.id);
} }
if (_list.length + currentTaskCourseData.length + selectLive.length + selectVideo.length > 20) { if (_list.length + currentTaskCourseData.length + selectLive.length + selectVideo.internal.length + selectVideo.external.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程'); message.warning('无法继续选择,一个任务最多关联20个课程');
return; return;
} }
...@@ -757,9 +757,10 @@ class SelectOperatorModal extends React.Component { ...@@ -757,9 +757,10 @@ class SelectOperatorModal extends React.Component {
} else { } else {
_list = _.reject(selectLive, (item) => _.find(changeRows, (data) => data.liveCourseId === item.liveCourseId)); _list = _.reject(selectLive, (item) => _.find(changeRows, (data) => data.liveCourseId === item.liveCourseId));
} }
if (_list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length > 20) { if (_list.length + currentTaskCourseData.length + selectVideo.internal.length + selectVideo.external.length + selectPicture.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程'); message.warning('无法继续选择,一个任务最多关联20个课程');
const extraLength = _list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length - 20; const extraLength =
_list.length + currentTaskCourseData.length + selectVideo.internal.length + selectVideo.external.length + +selectPicture.length - 20;
_list.splice(_list.length - extraLength, extraLength); _list.splice(_list.length - extraLength, extraLength);
} }
this.setState({ selectLive: _list }); this.setState({ selectLive: _list });
...@@ -847,14 +848,24 @@ class SelectOperatorModal extends React.Component { ...@@ -847,14 +848,24 @@ class SelectOperatorModal extends React.Component {
}, },
onSelectAll: (selected, _selectedRows, changeRows) => { onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = []; let _list = [];
let otherVideoCourseDivision = videoCourseDivision === 'internal' ? 'external' : 'internal';
if (selected) { if (selected) {
_list = _.uniq(selectVideo[videoCourseDivision].concat(changeRows), false, (item) => item.id); _list = _.uniq(selectVideo[videoCourseDivision].concat(changeRows), false, (item) => item.id);
} else { } else {
_list = _.reject(selectVideo[videoCourseDivision], (item) => _.find(changeRows, (data) => data.id === item.id)); _list = _.reject(selectVideo[videoCourseDivision], (item) => _.find(changeRows, (data) => data.id === item.id));
} }
if (_list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length > 20) { if (
_list.length + selectVideo[otherVideoCourseDivision]?.length + currentTaskCourseData.length + selectLive.length + selectPicture.length >
20
) {
message.warning('无法继续选择,一个任务最多关联20个课程'); message.warning('无法继续选择,一个任务最多关联20个课程');
const extraLength = _list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length - 20; const extraLength =
_list.length +
selectVideo[otherVideoCourseDivision]?.length +
currentTaskCourseData.length +
selectLive.length +
selectPicture.length -
20;
_list.splice(_list.length - extraLength, extraLength); _list.splice(_list.length - extraLength, extraLength);
} }
this.setState({ this.setState({
...@@ -951,9 +962,10 @@ class SelectOperatorModal extends React.Component { ...@@ -951,9 +962,10 @@ class SelectOperatorModal extends React.Component {
} else { } else {
_list = _.reject(selectPicture, (item) => _.find(changeRows, (data) => data.id === item.id)); _list = _.reject(selectPicture, (item) => _.find(changeRows, (data) => data.id === item.id));
} }
if (_list.length + currentTaskCourseData.length + selectVideo.length + selectLive.length > 20) { if (_list.length + currentTaskCourseData.length + selectVideo.internal.length + selectVideo.external.length + selectLive.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程'); message.warning('无法继续选择,一个任务最多关联20个课程');
const extraLength = _list.length + currentTaskCourseData.length + selectVideo.length + selectLive.length - 20; const extraLength =
_list.length + currentTaskCourseData.length + selectVideo.internal.length + selectVideo.external.length + selectLive.length - 20;
_list.splice(_list.length - extraLength, extraLength); _list.splice(_list.length - extraLength, extraLength);
} }
this.setState({ selectPicture: _list }); this.setState({ selectPicture: _list });
......
...@@ -436,7 +436,7 @@ class SelectPrepareFileModal extends React.Component { ...@@ -436,7 +436,7 @@ class SelectPrepareFileModal extends React.Component {
selectType, selectType,
multiple, multiple,
accept = ".ppt,.pptx,.doc,.docx,.pdf,.jpg,.jpeg,.png,.mp3,.mp4,.xlsx,.xls", accept = ".ppt,.pptx,.doc,.docx,.pdf,.jpg,.jpeg,.png,.mp3,.mp4,.xlsx,.xls",
tooltip = '支持文件类型:ppt、word、excel、pdf、jpg、jpeg、png、mp3、mp4' tooltip = '支持文件类型:ppt、word、excel、pdf、jpg、jpeg、png、mp3、mp4,支持批量上传文件'
} = this.props; } = this.props;
const selectedFileLength = selectedFileList.length; const selectedFileLength = selectedFileList.length;
......
...@@ -809,7 +809,7 @@ class FolderList extends React.Component { ...@@ -809,7 +809,7 @@ class FolderList extends React.Component {
<Choose> <Choose>
<When condition={hasManagementAuthority}> <When condition={hasManagementAuthority}>
<div className="lottie-icon-title">你还没有上传文件,点击 <div className="lottie-icon-title">你还没有上传文件,点击
<Tooltip title="支持文件类型:ppt、word、excel、pdf、jpg、mp3、mp4"> <Tooltip title="支持文件类型:ppt、word、excel、pdf、jpg、mp3、mp4,上传后默认私密,可邀请其他成员协作,支持批量上传文件">
<span <span
className="upload-btn" className="upload-btn"
onClick={this.handleChooseFile} onClick={this.handleChooseFile}
......
...@@ -314,7 +314,7 @@ class OperateArea extends React.Component { ...@@ -314,7 +314,7 @@ class OperateArea extends React.Component {
accept=".ppt,.pptx,.doc,.docx,.pdf,.jpg,.jpeg,.png,.mp3,.mp4,.xlsx,.xls" accept=".ppt,.pptx,.doc,.docx,.pdf,.jpg,.jpeg,.png,.mp3,.mp4,.xlsx,.xls"
onChange={(e) => this.handleUpload(e)} onChange={(e) => this.handleUpload(e)}
/> />
<Tooltip title="支持文件类型:ppt、word、excel、pdf、jpg、mp3、mp4,上传后默认私密,可邀请其他成员协作"> <Tooltip title="支持文件类型:ppt、word、excel、pdf、jpg、mp3、mp4,上传后默认私密,可邀请其他成员协作,支持批量上传文件">
<Button <Button
type={!showResultPage?"primary":""} type={!showResultPage?"primary":""}
disabled={showResultPage} disabled={showResultPage}
......
...@@ -14,7 +14,8 @@ import Main from './Main' ...@@ -14,7 +14,8 @@ import Main from './Main'
import zhCN from 'antd/es/locale/zh_CN' import zhCN from 'antd/es/locale/zh_CN'
import User from '@/common/js/user'; import User from '@/common/js/user';
import BaseService from "@/domains/basic-domain/baseService"; 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 { setStoreGroupPermission, setStorePermission, setStoreGroupList, setStoreList } from '@/store/actions/index';
import Service from "@/common/js/service"; import Service from "@/common/js/service";
import Bus from '@/core/tbus'; import Bus from '@/core/tbus';
...@@ -27,6 +28,7 @@ declare var window: any; ...@@ -27,6 +28,7 @@ declare var window: any;
const App: React.FC = (props: any) => { const App: React.FC = (props: any) => {
const [storeUserId, setStoreUserId] = useState('') const [storeUserId, setStoreUserId] = useState('')
const ctx: any = useContext(XMContext); const ctx: any = useContext(XMContext);
const [versionInfo, setVersionInfo] = useState<VersionInfo|null>(null)
const userId = User.getUserId(); const userId = User.getUserId();
const [menuType, setMenuType] = useState(true); const [menuType, setMenuType] = useState(true);
const enterpriseId = User.getEnterpriseId(); const enterpriseId = User.getEnterpriseId();
...@@ -35,6 +37,7 @@ const App: React.FC = (props: any) => { ...@@ -35,6 +37,7 @@ const App: React.FC = (props: any) => {
useEffect(() => { useEffect(() => {
getStoreAndUserInfo(); getStoreAndUserInfo();
getVersion();
if (window.location.hash === "#/") { if (window.location.hash === "#/") {
window.RCHistory.replace({ window.RCHistory.replace({
pathname: '/home', pathname: '/home',
...@@ -58,6 +61,26 @@ const App: React.FC = (props: any) => { ...@@ -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() { async function getStoreAndUserInfo() {
await (enterpriseId ? getStoreInfo() : getStoreGroupAndStoreList()); await (enterpriseId ? getStoreInfo() : getStoreGroupAndStoreList());
...@@ -147,10 +170,13 @@ const App: React.FC = (props: any) => { ...@@ -147,10 +170,13 @@ const App: React.FC = (props: any) => {
</Layout> </Layout>
</Layout> */} </Layout> */}
<Header id="app" handleMenuType={handleMenuType} menuType={menuType} /> <Header id="app" handleMenuType={handleMenuType} menuType={menuType} />
<VersionContext.Provider value={versionInfo}>
<ConfigProvider locale={zhCN} autoInsertSpaceInButton={false}> <ConfigProvider locale={zhCN} autoInsertSpaceInButton={false}>
<Main menuType={menuType} /> <Main menuType={menuType} />
</ConfigProvider> </ConfigProvider>
<Menu menuType={menuType} handleMenuType={handleMenuType} /> <Menu menuType={menuType} handleMenuType={handleMenuType} />
</VersionContext.Provider>
</div> </div>
) )
} }
......
import React from 'react'; import React, { useEffect, useState } from 'react';
import moment from "moment"
import Service from "@/common/js/service"; import Service from "@/common/js/service";
import BaseService from "@/domains/basic-domain/baseService"; import BaseService from "@/domains/basic-domain/baseService";
import User from "@/common/js/user"; import User from "@/common/js/user";
import { LIVE_SHARE } from "@/domains/course-domain/constants"; import { LIVE_SHARE } from "@/domains/course-domain/constants";
import moment from 'moment';
import { Modal, message } from 'antd'; import { Modal, message } from 'antd';
import './CollegeManagePage.less'; import './CollegeManagePage.less';
import storage from '@/common/js/storage';
const roleMap = { const roleMap = {
CloudManager: "管理员", CloudManager: "管理员",
...@@ -14,6 +15,97 @@ const roleMap = { ...@@ -14,6 +15,97 @@ const roleMap = {
CloudOperator: '运营师', 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 { export default class CollegeManagePage extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
...@@ -24,13 +116,16 @@ export default class CollegeManagePage extends React.Component { ...@@ -24,13 +116,16 @@ export default class CollegeManagePage extends React.Component {
enterpriseId: User.getEnterpriseId(), enterpriseId: User.getEnterpriseId(),
isAdmin: false, isAdmin: false,
createStoreList:[], createStoreList:[],
joinStoreList:[] joinStoreList:[],
surplusDayTime:365, //剩余天数
endTime: 0, //有效截至时间
}; };
} }
componentDidMount() { componentDidMount() {
this.getStoreList(); this.getStoreList();
this.getEnterpriseUser(); this.getEnterpriseUser();
this.getVersion()
} }
getEnterpriseUser() { getEnterpriseUser() {
...@@ -45,6 +140,18 @@ export default class CollegeManagePage extends React.Component { ...@@ -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() { getStoreList() {
const { enterpriseId } = this.state; const { enterpriseId } = this.state;
if (!enterpriseId) return null; if (!enterpriseId) return null;
...@@ -132,6 +239,7 @@ export default class CollegeManagePage extends React.Component { ...@@ -132,6 +239,7 @@ export default class CollegeManagePage extends React.Component {
} = this.state; } = this.state;
return ( return (
<div className="college-manage-page"> <div className="college-manage-page">
<ExpirationPopover surplusDayTime={this.state.surplusDayTime} endTime={this.state.endTime}/>
<div className="college-header"> <div className="college-header">
<div className="box"> <div className="box">
<img className="box-image" src="https://image.xiaomaiketang.com/xm/fe4NCjr7XF.png" /> <img className="box-image" src="https://image.xiaomaiketang.com/xm/fe4NCjr7XF.png" />
......
...@@ -183,4 +183,78 @@ ...@@ -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
...@@ -5,6 +5,7 @@ import Service from "@/common/js/service"; ...@@ -5,6 +5,7 @@ import Service from "@/common/js/service";
import BaseService from "@/domains/basic-domain/baseService"; import BaseService from "@/domains/basic-domain/baseService";
import User from "@/common/js/user"; import User from "@/common/js/user";
import Breadcrumbs from "@/components/Breadcrumbs"; import Breadcrumbs from "@/components/Breadcrumbs";
import ImgClipModal from '@/components/ImgClipModal'
import './CreateCollege.less'; import './CreateCollege.less';
let cutFlag = false; let cutFlag = false;
...@@ -18,6 +19,7 @@ export default class CreateCollege extends React.Component { ...@@ -18,6 +19,7 @@ export default class CreateCollege extends React.Component {
logo: 'https://image.xiaomaiketang.com/xm/fe4NCjr7XF.png', logo: 'https://image.xiaomaiketang.com/xm/fe4NCjr7XF.png',
name: '', name: '',
enterpriseId: User.getEnterpriseId(), enterpriseId: User.getEnterpriseId(),
imageFile: null, // 需要被截取的图片
}; };
this.loginInputRef = React.createRef() this.loginInputRef = React.createRef()
} }
...@@ -46,95 +48,14 @@ export default class CreateCollege extends React.Component { ...@@ -46,95 +48,14 @@ export default class CreateCollege extends React.Component {
handleSelectCover = (e) => { handleSelectCover = (e) => {
const imageFile = e.target.files[0]; const imageFile = e.target.files[0];
Upload.uploadBlobToOSS(imageFile, 'cover' + (new Date()).valueOf()).then((url) => { Upload.uploadBlobToOSS(imageFile, 'cover' + (new Date()).valueOf()).then((url) => {
this.uploadImage({ folderName: imageFile.name, ossUrl: url });
})
}
//上传图片
uploadImage = (imageFile) => {
const { folderName } = imageFile;
const fileName = window.random_string(16) + folderName.slice(folderName.lastIndexOf("."));
const self = this;
this.setState(
{
visible: true,
},
() => {
setTimeout(() => {
const okBtnDom = document.querySelector("#headPicModal");
const options = {
size: [500, 128],
ok: okBtnDom,
maxZoom: 3,
style: {
jpgFillColor: "transparent",
},
done: function (dataUrl) {
clearTimeout(self.timer);
self.timer = setTimeout(() => {
if ((self.state.rotate != this.rotate()) || (self.state.scale != this.scale())) {
const _dataUrl = this.clip()
const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl);
self.setState({
cutImageBlob,
dataUrl: _dataUrl,
rotate: this.rotate(),
scale: this.scale()
})
}
}, 500)
const cutImageBlob = self.convertBase64UrlToBlob(dataUrl);
self.setState({
cutImageBlob,
dataUrl
})
setTimeout(() => {
cutFlag = false;
}, 2000);
},
fail: (failInfo) => {
message.error("图片上传失败了,请重新上传");
},
loadComplete: function (img) {
setTimeout(() => {
const _dataUrl = this.clip()
self.setState({
dataUrl: _dataUrl,
hasImgReady: true
})
}, 100)
},
};
const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}`
if (!this.state.photoclip) {
const _photoclip = new PhotoClip("#headPicModal", options);
_photoclip.load(imgUrl);
this.setState({ this.setState({
photoclip: _photoclip, visible: true,
imageFile:{ folderName: imageFile.name, ossUrl: url }
}); });
} else { // this.uploadImage({ folderName: imageFile.name, ossUrl: url });
this.state.photoclip.clear(); })
this.state.photoclip.load(imgUrl);
}
}, 200);
} }
);
};
// base64转换成blob
convertBase64UrlToBlob = (urlData) => {
const bytes = window.atob(urlData.split(",")[1]);
const ab = new ArrayBuffer(bytes.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: "image/png" });
};
//获取resourceId //获取resourceId
getSignature = (blob, fileName) => { getSignature = (blob, fileName) => {
...@@ -184,6 +105,7 @@ export default class CreateCollege extends React.Component { ...@@ -184,6 +105,7 @@ export default class CreateCollege extends React.Component {
hasImgReady, hasImgReady,
cutImageBlob, cutImageBlob,
showError, showError,
imageFile
} = this.state; } = this.state;
return ( return (
<div className="college-manage-page"> <div className="college-manage-page">
...@@ -247,65 +169,9 @@ export default class CreateCollege extends React.Component { ...@@ -247,65 +169,9 @@ export default class CreateCollege extends React.Component {
style={{ display: "none" }} style={{ display: "none" }}
onChange={this.handleSelectCover} onChange={this.handleSelectCover}
/> />
<Modal { visible &&
title="设置图片" <ImgClipModal visible={visible} imgUrl={imageFile.ossUrl} aspectRatio='125/32' cropBoxHeight='128' onConfirm={this.getSignature} onClose={()=>{this.setState({ visible: false });}}/>
width={1080}
visible={visible}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
onCancel={() => {
this.setState({ visible: false });
}}
zIndex={10001}
footer={[
<Button
key="back"
onClick={() => {
this.setState({ visible: false });
}}
>
重新上传
</Button>,
<Button
key="submit"
type="primary"
disabled={!hasImgReady}
onClick={() => {
if (!cutFlag) {
cutFlag = true;
this.refs.hiddenBtn.click();
} }
this.getSignature(cutImageBlob);
}}
>
确定
</Button>,
]}
>
<div className="clip-box">
<div
id="headPicModal"
ref="headPicModal"
style={{
width: "500px",
height: "430px",
marginBottom: 0,
}}
></div>
<div id="clipBtn" style={{ display: "none" }} ref="hiddenBtn"></div>
<div className="preview-img">
<div className="title">效果预览</div>
<div id="preview-url-box" style={{width:500,height:128}}>
<img src={this.state.dataUrl} style={{ width: '100%' }} alt="" />
</div>
<div className="tip-box">
<div className="tip">温馨提示</div>
<div className="tip">①预览效果图时可能存在延迟,单击左侧图片刷新即可</div>
<div className="tip">②设置图片时双击可旋转图片,滚动可放大或缩小图片</div>
</div>
</div>
</div>
</Modal>
</div> </div>
) )
} }
......
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2019-09-10 18:26:03 * @Date: 2019-09-10 18:26:03
* @LastEditors: Please set LastEditors * @LastEditors: yuananting
* @LastEditTime: 2021-06-24 19:28:14 * @LastEditTime: 2021-07-06 14:37:49
* @Description: * @Description:
*/ */
import React, { useRef, useContext, useEffect, useState } from 'react'; import React, { useRef, useContext, useEffect, useState } from 'react';
...@@ -271,14 +271,14 @@ function Header(props) { ...@@ -271,14 +271,14 @@ function Header(props) {
onChange={(e) => { onChange={(e) => {
setStoreId(e.target.value); setStoreId(e.target.value);
User.setStoreId(e.target.value); User.setStoreId(e.target.value);
list.map((item)=>{ list.map((item) => {
if(item.id === e.target.value){ if (item.id === e.target.value) {
User.setStoreUserId(item.storeUserId); User.setStoreUserId(item.storeUserId);
} }
}) });
User.setUserId(User.getUserId()); User.setUserId(User.getUserId());
User.setToken(User.getToken()); User.setToken(User.getToken());
User.setEnterpriseId(User.getEnterpriseId()) User.setEnterpriseId(User.getEnterpriseId());
window.RCHistory.push('/home'); window.RCHistory.push('/home');
window.location.reload(); window.location.reload();
}} }}
...@@ -320,10 +320,10 @@ function Header(props) { ...@@ -320,10 +320,10 @@ function Header(props) {
</div> </div>
)} )}
<div className='right-box'> <div className='right-box'>
<div className='link-to-store'>
<div className='right-bg-img'> <div className='right-bg-img'>
<img src='https://image.xiaomaiketang.com/xm/WCwjyyXYda.png'></img> <img src='https://image.xiaomaiketang.com/xm/WCwjyyXYda.png'></img>
</div> </div>
<div className='link-to-store'>
<div className='link'> <div className='link'>
<span className='link-btn'> <span className='link-btn'>
<span className='icon iconfont tool-tip-right'>&#xe85d;</span> <span className='icon iconfont tool-tip-right'>&#xe85d;</span>
......
...@@ -196,7 +196,6 @@ ...@@ -196,7 +196,6 @@
margin-left: 62px; margin-left: 62px;
.college-container { .college-container {
position: relative; position: relative;
width: 360px;
height: 50px; height: 50px;
display: flex; display: flex;
align-items: center; align-items: center;
...@@ -310,13 +309,9 @@ ...@@ -310,13 +309,9 @@
display: flex; display: flex;
align-items: center; align-items: center;
} }
.right-bg-img {
img {
width: 277px;
height: 60px;
}
}
.link-to-store { .link-to-store {
position: relative;
display: flex; display: flex;
height: 49px; height: 49px;
line-height: 49px; line-height: 49px;
...@@ -329,6 +324,14 @@ ...@@ -329,6 +324,14 @@
.iconfont { .iconfont {
color: #fff; color: #fff;
} }
.right-bg-img {
position: absolute;
right: 347px;
img {
width: 277px;
height: 60px;
}
}
.link { .link {
cursor: pointer; cursor: pointer;
position: relative; position: relative;
...@@ -346,7 +349,7 @@ ...@@ -346,7 +349,7 @@
width: 216px; width: 216px;
height: 260px; height: 260px;
top: 49px; top: 49px;
left: 0; left: -50px;
background-color: #fff; background-color: #fff;
flex-wrap: wrap; flex-wrap: wrap;
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1); box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
......
...@@ -7,6 +7,7 @@ import User from '@/common/js/user'; ...@@ -7,6 +7,7 @@ import User from '@/common/js/user';
import WechatLogin from './WechatLogin'; import WechatLogin from './WechatLogin';
import BaseService from '@/domains/basic-domain/baseService'; import BaseService from '@/domains/basic-domain/baseService';
import axios from 'axios'; import axios from 'axios';
import storage from '@/common/js/storage';
import _ from 'underscore'; import _ from 'underscore';
import user from '@/common/js/user'; import user from '@/common/js/user';
const { TabPane } = Tabs; const { TabPane } = Tabs;
...@@ -34,6 +35,7 @@ function Login(props) { ...@@ -34,6 +35,7 @@ function Login(props) {
User.removeUserId(); User.removeUserId();
User.removeToken(); User.removeToken();
User.removeEnterpriseId(); User.removeEnterpriseId();
storage.set("expiration_tip_login",true)
}, []); }, []);
function getWXWorkLoginNoCheck(enterpriseId, userId) { function getWXWorkLoginNoCheck(enterpriseId, userId) {
const params = { const params = {
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
@active-color: #2966ff; @active-color: #2966ff;
.left-container { .left-container {
position: absolute; position: absolute;
z-index: 2; z-index: 10;
top: @top-height; top: @top-height;
left: 0; left: 0;
bottom: 0; bottom: 0;
...@@ -51,12 +51,13 @@ ...@@ -51,12 +51,13 @@
display: -webkit-flex; display: -webkit-flex;
flex-direction: column; flex-direction: column;
-webkit-flex-direction: column; -webkit-flex-direction: column;
height: calc(~'100% - 60px');
.nav { .nav {
-webkit-flex: 1; -webkit-flex: 1;
cursor: default; cursor: default;
font-size: 16px; font-size: 16px;
height: calc(~'100% - 72px'); height: calc(~'100% - 84px');
overflow: auto; overflow: auto;
&::-webkit-scrollbar { &::-webkit-scrollbar {
display: none; display: none;
...@@ -134,6 +135,121 @@ ...@@ -134,6 +135,121 @@
color: #5e606a; 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 { &.left-container-vertical {
...@@ -155,7 +271,9 @@ ...@@ -155,7 +271,9 @@
// display:inline-block; // display:inline-block;
// } // }
// } // }
} }
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected { .ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
background: @active-color !important; 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 StoreService from '@/domains/store-domain/storeService';
import { Menu } from 'antd'; import classNames from 'classnames';
import React, { Key, useContext, useEffect, useMemo, useState } from 'react'; import User from "@/common/js/user"
import { withRouter } from 'react-router-dom';
import _ from 'underscore'; import _ from 'underscore';
import { menuList } from '../../routes//config/menuList'; import tBus from '@/core/tbus'
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; 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) { function Aside(props: any) {
const { menuType, handleMenuType } = props; const { menuType, handleMenuType } = props;
const ctx: any = useContext(XMContext); const ctx: any = useContext(XMContext);
...@@ -35,11 +139,20 @@ function Aside(props: any) { ...@@ -35,11 +139,20 @@ function Aside(props: any) {
}); });
} }
return item; return item;
}); })
}, [props.location.pathname]); }, [props.location.pathname])
useEffect(() => {
getTopLeftLogo(); useEffect(()=> {
}, []); getTopLeftLogo()
tBus.bind("updateCollegeInfo",updateCollegeInfo)
return ()=> {
tBus.unbind("updateCollegeInfo",updateCollegeInfo)
}
},[])
function updateCollegeInfo() {
getTopLeftLogo()
}
function getTopLeftLogo() { function getTopLeftLogo() {
if (User.getToken()) { if (User.getToken()) {
...@@ -178,7 +291,11 @@ function Aside(props: any) { ...@@ -178,7 +291,11 @@ function Aside(props: any) {
})} })}
</Menu> </Menu>
</div> </div>
{
menuType ? <VersionPanel /> : ""
}
</div> </div>
</div> </div>
); );
} }
......
/* /*
* @Author: wufan * @Author: wufan
* @Date: 2020-11-30 10:47:38 * @Date: 2020-11-30 10:47:38
* @LastEditors: wufan * @LastEditors: Please set LastEditors
* @LastEditTime: 2021-06-21 11:16:21 * @LastEditTime: 2021-07-08 19:35:17
* @Description: web学院banner页面 * @Description: web学院banner页面
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -25,6 +25,7 @@ import "./StoreDecorationPage.less"; ...@@ -25,6 +25,7 @@ import "./StoreDecorationPage.less";
import Upload from "@/core/upload"; import Upload from "@/core/upload";
import { XMTable } from '@/components'; import { XMTable } from '@/components';
import college from '@/common/lottie/college'; import college from '@/common/lottie/college';
import ImgClipModal from '@/components/ImgClipModal'
const { confirm } = Modal; const { confirm } = Modal;
const DragHandle = sortableHandle(() => ( const DragHandle = sortableHandle(() => (
...@@ -51,7 +52,8 @@ class StoreH5Decoration extends React.Component { ...@@ -51,7 +52,8 @@ class StoreH5Decoration extends React.Component {
photoclip: null, photoclip: null,
preview: "", preview: "",
cutImageBlob: null, cutImageBlob: null,
hasImgReady: false // 图片是否上传成功 hasImgReady: false, // 图片是否上传成功
imageFile: null // 需要被截取的图片
}; };
} }
...@@ -208,88 +210,16 @@ class StoreH5Decoration extends React.Component { ...@@ -208,88 +210,16 @@ class StoreH5Decoration extends React.Component {
// 选择云盘资源 // 选择云盘资源
handleSelectImg = (file) => { handleSelectImg = (file) => {
if(file){
this.setState({ this.setState({
showSelectFileModal: false,
});
this.uploadImage(file);
};
//上传图片
uploadImage = (imageFile) => {
const self = this;
this.setState(
{
visible: true, visible: true,
}, imageFile:file
() => {
setTimeout(() => {
const okBtnDom = document.querySelector("#headPicModal");
const options = {
size: [500, 172],
ok: okBtnDom,
maxZoom: 3,
style: {
jpgFillColor: "transparent",
},
done: function (dataUrl) {
clearTimeout(self.timer);
self.timer = setTimeout(() => {
if ((self.state.rotate != this.rotate()) || (self.state.scale != this.scale())) {
console.log(this.scale(), 'scale')
const _dataUrl = this.clip()
const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl);
self.setState({
cutImageBlob,
dataUrl: _dataUrl,
rotate: this.rotate(),
scale: this.scale()
})
}
}, 500)
const cutImageBlob = self.convertBase64UrlToBlob(dataUrl);
self.setState({
cutImageBlob,
dataUrl
})
setTimeout(() => {
cutFlag = false;
}, 2000);
},
fail: (failInfo) => {
message.error("图片上传失败了,请重新上传");
},
loadComplete: function (img) {
setTimeout(() => {
const _dataUrl = this.clip()
self.setState({
dataUrl: _dataUrl,
hasImgReady: true
})
}, 100)
},
};
const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}`
if (!this.state.photoclip) {
const _photoclip = new PhotoClip("#headPicModal", options);
_photoclip.load(imgUrl);
this.setState({
photoclip: _photoclip,
}); });
} else {
this.state.photoclip.clear();
this.state.photoclip.load(imgUrl);
} }
}, 200);
}
);
}; };
//获取resourceId //获取resourceId
getSignature = (blob) => { getSignature = (blob) => {
Upload.uploadBlobToOSS(blob, "avatar" + new Date().valueOf()).then( Upload.uploadBlobToOSS(blob, "avatar" + new Date().valueOf()).then(
...@@ -344,16 +274,6 @@ class StoreH5Decoration extends React.Component { ...@@ -344,16 +274,6 @@ class StoreH5Decoration extends React.Component {
}); });
}; };
// base64转换成blob
convertBase64UrlToBlob = (urlData) => {
const bytes = window.atob(urlData.split(",")[1]);
const ab = new ArrayBuffer(bytes.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: "image/png" });
};
render() { render() {
const { const {
...@@ -362,7 +282,8 @@ class StoreH5Decoration extends React.Component { ...@@ -362,7 +282,8 @@ class StoreH5Decoration extends React.Component {
diskList, diskList,
visible, visible,
cutImageBlob, cutImageBlob,
hasImgReady hasImgReady,
imageFile,
} = this.state; } = this.state;
const DraggableContainer = (props) => ( const DraggableContainer = (props) => (
<SortableContainer <SortableContainer
...@@ -423,65 +344,9 @@ class StoreH5Decoration extends React.Component { ...@@ -423,65 +344,9 @@ class StoreH5Decoration extends React.Component {
}} }}
onSelect={this.handleSelectImg} onSelect={this.handleSelectImg}
/> />
<Modal { visible &&
title="设置图片" <ImgClipModal visible={visible} imgUrl={imageFile.ossUrl} aspectRatio='500/172' cropBoxHeight='172' onConfirm={this.getSignature} onClose={()=>{this.setState({ visible: false });}}/>
width={1080}
visible={visible}
onCancel={() => {
this.setState({ visible: false });
}}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
footer={[
<Button
key="back"
onClick={() => {
this.setState({ visible: false });
this.state.choosedBannerId ? this.handleReplaceDecoration(this.state.choosedBannerItem):this.handleToAddStoreDecoration();
}}
>
重新上传
</Button>,
<Button
key="submit"
type="primary"
disabled={!hasImgReady}
onClick={() => {
if (!cutFlag) {
cutFlag = true;
this.refs.hiddenBtn.click();
} }
this.getSignature(cutImageBlob);
}}
>
确定
</Button>,
]}
>
<div className="clip-box">
<div
id="headPicModal"
ref="headPicModal"
style={{
width: "500px",
height: "430px",
marginBottom: 0,
}}
></div>
<div id="clipBtn" style={{ display: "none" }} ref="hiddenBtn"></div>
<div className="preview-img">
<div className="title">效果预览</div>
<div id="H5-preview-url-box">
<img src={this.state.dataUrl} style={{ width: '100%' }} alt="" />
</div>
<div className="tip-box">
<div className="tip">温馨提示</div>
<div className="tip">①预览效果图时可能存在延迟,单击左侧图片刷新即可</div>
<div className="tip">②设置图片时双击可旋转图片,滚动可放大或缩小图片</div>
</div>
</div>
</div>
</Modal>
</div> </div>
); );
} }
......
...@@ -4,10 +4,10 @@ import { Form, Input, Button, Checkbox ,Select,Modal,message} from 'antd'; ...@@ -4,10 +4,10 @@ import { Form, Input, Button, Checkbox ,Select,Modal,message} from 'antd';
import {industryList,childIndustryList} from '@/domains/store-domain/constants' import {industryList,childIndustryList} from '@/domains/store-domain/constants'
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal'; import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
import Upload from '@/core/upload'; import Upload from '@/core/upload';
// import PhotoClip from 'photoclip';
import StoreService from "@/domains/store-domain/storeService"; import StoreService from "@/domains/store-domain/storeService";
import User from "@/common/js/user"; import User from "@/common/js/user";
import Bus from '@/core/tbus'; import Bus from '@/core/tbus';
import ImgClipModal from '@/components/ImgClipModal'
import "./StoreInfo.less"; import "./StoreInfo.less";
let cutFlag = false; let cutFlag = false;
class StoreInfo extends React.Component { class StoreInfo extends React.Component {
...@@ -23,6 +23,7 @@ class StoreInfo extends React.Component { ...@@ -23,6 +23,7 @@ class StoreInfo extends React.Component {
logo:'', logo:'',
showSelectFileModal:false, showSelectFileModal:false,
cutImageBlob: null, cutImageBlob: null,
imageFile: null, // 需要被截取的图片
} }
} }
componentWillMount(){ componentWillMount(){
...@@ -73,84 +74,11 @@ class StoreInfo extends React.Component { ...@@ -73,84 +74,11 @@ class StoreInfo extends React.Component {
}) })
} }
handleSelectCover = (file)=> { handleSelectCover = (file)=> {
this.uploadImage(file);
}
//上传图片
uploadImage = (imageFile) => {
const { folderName } = imageFile;
const fileName = window.random_string(16) + folderName.slice(folderName.lastIndexOf("."));
const self = this;
this.setState(
{
visible: true,
},
() => {
setTimeout(() => {
const okBtnDom = document.querySelector("#headPicModal");
const options = {
size: [500, 128],
ok: okBtnDom,
maxZoom: 3,
style: {
jpgFillColor: "transparent",
},
done: function (dataUrl) {
clearTimeout(self.timer);
self.timer = setTimeout(() => {
if ((self.state.rotate != this.rotate()) || (self.state.scale != this.scale())) {
console.log(this.scale(), 'scale')
const _dataUrl = this.clip()
const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl);
self.setState({
cutImageBlob,
dataUrl: _dataUrl,
rotate: this.rotate(),
scale: this.scale()
})
}
}, 500)
const cutImageBlob = self.convertBase64UrlToBlob(dataUrl);
self.setState({
cutImageBlob,
dataUrl
})
setTimeout(() => {
cutFlag = false;
}, 2000);
},
fail: (failInfo) => {
message.error("图片上传失败了,请重新上传");
},
loadComplete: function (img) {
setTimeout(() => {
const _dataUrl = this.clip()
self.setState({
dataUrl: _dataUrl,
hasImgReady: true
})
}, 100)
},
};
const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}`
if (!this.state.photoclip) {
const _photoclip = new PhotoClip("#headPicModal", options);
_photoclip.load(imgUrl);
this.setState({ this.setState({
photoclip: _photoclip, visible: true,
imageFile:file
}); });
} else {
this.state.photoclip.clear();
this.state.photoclip.load(imgUrl);
}
}, 200);
} }
);
};
//获取resourceId //获取resourceId
getSignature = (blob, fileName) => { getSignature = (blob, fileName) => {
...@@ -172,16 +100,6 @@ class StoreInfo extends React.Component { ...@@ -172,16 +100,6 @@ class StoreInfo extends React.Component {
logo:coverClicpPath logo:coverClicpPath
}) })
} }
// base64转换成blob
convertBase64UrlToBlob = (urlData) => {
const bytes = window.atob(urlData.split(",")[1]);
const ab = new ArrayBuffer(bytes.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: "image/png" });
};
updateInfo=()=>{ updateInfo=()=>{
const {storeName,storeFullName,logo,subjectType,corpIndustry,corpSubIndustry} = this.state; const {storeName,storeFullName,logo,subjectType,corpIndustry,corpSubIndustry} = this.state;
if(!storeName){ if(!storeName){
...@@ -216,7 +134,7 @@ class StoreInfo extends React.Component { ...@@ -216,7 +134,7 @@ class StoreInfo extends React.Component {
}); });
} }
render() { render() {
const {storeName,storeFullName,subjectType,corpIndustry,corpSubIndustry,showSelectFileModal,visible,hasImgReady,logo,cutImageBlob } = this.state; const {storeName,storeFullName,subjectType,corpIndustry,corpSubIndustry,showSelectFileModal,visible,hasImgReady,logo,cutImageBlob,imageFile} = this.state;
return ( return (
<div className="page store-info-page"> <div className="page store-info-page">
<div className="content-header">学院基本信息</div> <div className="content-header">学院基本信息</div>
...@@ -330,65 +248,9 @@ class StoreInfo extends React.Component { ...@@ -330,65 +248,9 @@ class StoreInfo extends React.Component {
onSelect={this.handleSelectCover} onSelect={this.handleSelectCover}
/> />
} }
<Modal { visible &&
title="设置图片" <ImgClipModal visible={visible} imgUrl={imageFile.ossUrl} aspectRatio='125/32' cropBoxHeight='128' onConfirm={this.getSignature} onClose={()=>{this.setState({ visible: false });}}/>
width={1080}
visible={visible}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
onCancel={() => {
this.setState({ visible: false });
}}
zIndex={10001}
footer={[
<Button
key="back"
onClick={() => {
this.setState({ visible: false });
}}
>
重新上传
</Button>,
<Button
key="submit"
type="primary"
disabled={!hasImgReady}
onClick={() => {
if (!cutFlag) {
cutFlag = true;
this.refs.hiddenBtn.click();
} }
this.getSignature(cutImageBlob);
}}
>
确定
</Button>,
]}
>
<div className="clip-box">
<div
id="headPicModal"
ref="headPicModal"
style={{
width: "500px",
height: "430px",
marginBottom: 0,
}}
></div>
<div id="clipBtn" style={{ display: "none" }} ref="hiddenBtn"></div>
<div className="preview-img">
<div className="title">效果预览</div>
<div id="preview-url-box" style={{width:500,height:128}}>
<img src={this.state.dataUrl} style={{ width: '100%' }} alt="" />
</div>
<div className="tip-box">
<div className="tip">温馨提示</div>
<div className="tip">①预览效果图时可能存在延迟,单击左侧图片刷新即可</div>
<div className="tip">②设置图片时双击可旋转图片,滚动可放大或缩小图片</div>
</div>
</div>
</div>
</Modal>
<div><Button type="primary" onClick={this.updateInfo} htmlType="submit" className="submit-btn">更新信息</Button></div> <div><Button type="primary" onClick={this.updateInfo} htmlType="submit" className="submit-btn">更新信息</Button></div>
</div> </div>
......
/* /*
* @Author: wufan * @Author: wufan
* @Date: 2020-11-30 10:47:38 * @Date: 2020-11-30 10:47:38
* @LastEditors: wufan * @LastEditors: Please set LastEditors
* @LastEditTime: 2021-06-21 11:16:31 * @LastEditTime: 2021-07-08 19:35:27
* @Description: web学院banner页面 * @Description: web学院banner页面
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -25,6 +25,7 @@ import "./StoreDecorationPage.less"; ...@@ -25,6 +25,7 @@ import "./StoreDecorationPage.less";
import Upload from "@/core/upload"; import Upload from "@/core/upload";
import { XMTable } from '@/components'; import { XMTable } from '@/components';
import college from '@/common/lottie/college'; import college from '@/common/lottie/college';
import ImgClipModal from '@/components/ImgClipModal'
const { confirm } = Modal; const { confirm } = Modal;
const DragHandle = sortableHandle(() => ( const DragHandle = sortableHandle(() => (
...@@ -51,7 +52,8 @@ class StoreWebDecoration extends React.Component { ...@@ -51,7 +52,8 @@ class StoreWebDecoration extends React.Component {
photoclip: null, photoclip: null,
preview: "", preview: "",
cutImageBlob: null, cutImageBlob: null,
hasImgReady: false // 图片是否上传成功 hasImgReady: false,// 图片是否上传成功
imageFile: null // 需要被截取的图片
}; };
} }
...@@ -207,87 +209,14 @@ class StoreWebDecoration extends React.Component { ...@@ -207,87 +209,14 @@ class StoreWebDecoration extends React.Component {
// 选择云盘资源 // 选择云盘资源
handleSelectImg = (file) => { handleSelectImg = (file) => {
if(file){
this.setState({ this.setState({
showSelectFileModal: false,
});
this.uploadImage(file);
};
//上传图片
uploadImage = (imageFile) => {
const self = this;
this.setState(
{
visible: true, visible: true,
}, imageFile:file
() => {
setTimeout(() => {
const okBtnDom = document.querySelector("#headPicModal");
const options = {
size: [500, 73],
ok: okBtnDom,
maxZoom: 3,
style: {
jpgFillColor: "transparent",
},
done: function (dataUrl) {
clearTimeout(self.timer);
self.timer = setTimeout(() => {
if ((self.state.rotate != this.rotate()) || (self.state.scale != this.scale())) {
console.log(this.scale(), 'scale')
const _dataUrl = this.clip()
const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl);
self.setState({
cutImageBlob,
dataUrl: _dataUrl,
rotate: this.rotate(),
scale: this.scale()
})
}
}, 500)
const cutImageBlob = self.convertBase64UrlToBlob(dataUrl);
self.setState({
cutImageBlob,
dataUrl
})
setTimeout(() => {
cutFlag = false;
}, 2000);
},
fail: (failInfo) => {
message.error("图片上传失败了,请重新上传");
},
loadComplete: function (img) {
setTimeout(() => {
const _dataUrl = this.clip()
self.setState({
dataUrl: _dataUrl,
hasImgReady: true
})
}, 100)
},
};
const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}`
if (!this.state.photoclip) {
const _photoclip = new PhotoClip("#headPicModal", options);
_photoclip.load(imgUrl);
this.setState({
photoclip: _photoclip,
}); });
} else {
this.state.photoclip.clear();
this.state.photoclip.load(imgUrl);
} }
}, 200);
}
);
}; };
//获取resourceId //获取resourceId
getSignature = (blob) => { getSignature = (blob) => {
Upload.uploadBlobToOSS(blob, "avatar" + new Date().valueOf()).then( Upload.uploadBlobToOSS(blob, "avatar" + new Date().valueOf()).then(
...@@ -342,16 +271,7 @@ class StoreWebDecoration extends React.Component { ...@@ -342,16 +271,7 @@ class StoreWebDecoration extends React.Component {
}); });
}; };
// base64转换成blob
convertBase64UrlToBlob = (urlData) => {
const bytes = window.atob(urlData.split(",")[1]);
const ab = new ArrayBuffer(bytes.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: "image/png" });
};
render() { render() {
const { const {
...@@ -360,7 +280,8 @@ class StoreWebDecoration extends React.Component { ...@@ -360,7 +280,8 @@ class StoreWebDecoration extends React.Component {
diskList, diskList,
visible, visible,
cutImageBlob, cutImageBlob,
hasImgReady hasImgReady,
imageFile
} = this.state; } = this.state;
const DraggableContainer = (props) => ( const DraggableContainer = (props) => (
<SortableContainer <SortableContainer
...@@ -421,65 +342,9 @@ class StoreWebDecoration extends React.Component { ...@@ -421,65 +342,9 @@ class StoreWebDecoration extends React.Component {
}} }}
onSelect={this.handleSelectImg} onSelect={this.handleSelectImg}
/> />
<Modal { visible &&
title="设置图片" <ImgClipModal visible={visible} imgUrl={imageFile.ossUrl} aspectRatio='500/73' cropBoxHeight='73' onConfirm={this.getSignature} onClose={()=>{this.setState({ visible: false });}}/>
width={1080}
visible={visible}
onCancel={() => {
this.setState({ visible: false });
}}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
footer={[
<Button
key="back"
onClick={() => {
this.setState({ visible: false });
this.state.choosedBannerId ? this.handleReplaceDecoration(this.state.choosedBannerItem):this.handleToAddStoreDecoration();
}}
>
重新上传
</Button>,
<Button
key="submit"
type="primary"
disabled={!hasImgReady}
onClick={() => {
if (!cutFlag) {
cutFlag = true;
this.refs.hiddenBtn.click();
} }
this.getSignature(cutImageBlob);
}}
>
确定
</Button>,
]}
>
<div className="clip-box">
<div
id="headPicModal"
ref="headPicModal"
style={{
width: "500px",
height: "430px",
marginBottom: 0,
}}
></div>
<div id="clipBtn" style={{ display: "none" }} ref="hiddenBtn"></div>
<div className="preview-img">
<div className="title">效果预览</div>
<div id="H5-preview-url-box">
<img src={this.state.dataUrl} style={{ width: '100%' }} alt="" />
</div>
<div className="tip-box">
<div className="tip">温馨提示</div>
<div className="tip">①预览效果图时可能存在延迟,单击左侧图片刷新即可</div>
<div className="tip">②设置图片时双击可旋转图片,滚动可放大或缩小图片</div>
</div>
</div>
</div>
</Modal>
</div> </div>
); );
} }
......
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