Merge branches 'dev_aliyun', 'dev_jupyter' and 'develop' of https://bdgit.educoder.net/Hjqreturn/educoder into develop

dev_new_shixunsrepository
杨树明 5 years ago
commit ad77dae87d

@ -962,8 +962,11 @@ class GamesController < ApplicationController
shixun = game.myshixun.shixun
shixun_tomcat = edu_setting('cloud_bridge')
service_host = edu_setting('vnc_url')
tpiGitURL = "#{edu_setting('git_address_domain')}/#{game.myshixun.repo_path}"
uri = "#{shixun_tomcat}/bridge/vnc/getvnc"
params = {tpiID: game.myshixun.id, :containers => "#{Base64.urlsafe_encode64(shixun_container_limit(shixun))}"}
params = {tpiID: game.myshixun.id, :containers => "#{Base64.urlsafe_encode64(shixun_container_limit(shixun))}",
tpiGitURL: tpiGitURL}
res = uri_post uri, params
if res && res['code'].to_i != 0
raise("实训云平台繁忙繁忙等级99")

@ -76,7 +76,7 @@ class JupytersController < ApplicationController
def active_with_tpi
myshixun = Myshixun.find_by(identifier: params[:identifier])
jupyter_active_tpm(myshixun)
jupyter_active_tpi(myshixun)
render json: {status: 0}
end

@ -216,10 +216,11 @@ class MyshixunsController < ApplicationController
old_time = Time.now.to_i
begin
shixun_tomcat = edu_setting('tomcat_webssh')
tpiGitURL = "#{edu_setting('git_address_domain')}/#{@myshixun.repo_path}"
uri = "#{shixun_tomcat}/bridge/webssh/getConnectInfo"
# 由于中间层采用混合云的方式因为local参数表示在有文件生成的实训是在本地生成还是在其他云端生成评测文件
local = @myshixun.shixun.challenges.where.not(show_type: -1).count == 0
params = {tpiID:@myshixun.id, podType:@myshixun.shixun.try(:webssh), local: local,
params = {tpiID:@myshixun.id, podType:@myshixun.shixun.try(:webssh), local: local, tpiGitURL: tpiGitURL,
containers:(Base64.urlsafe_encode64(shixun_container_limit @myshixun.shixun))}
res = uri_post uri, params
if res && res['code'].to_i != 0

@ -33,8 +33,8 @@ class ExportShixunReportService
base_css = %w(app/templates/shared/main.css app/templates/shixun_work/shixun_work.css app/templates/shared/codemirror.css)
base_css.each { |css| kit.stylesheets << Rails.root.join(css) }
file = Tempfile.new(filename)
kit.to_pdf(file.path)
file_name = "#{Rails.root}/files/archiveZip/#{filename}"
kit.to_pdf(file_name)
file
end

@ -0,0 +1,10 @@
class ModifyViewedCountForSubjects < ActiveRecord::Migration[5.2]
def change
subjects = Subject.where(status: 2).includes(:shixuns)
subjects.find_each do |subject|
subject.update_attribute(:visits, subject.visits + subject.shixuns.pluck(:myshixuns_count).sum)
end
end
end

@ -0,0 +1,7 @@
namespace :print_date do
desc "print date"
task :print => :environment do
Rails.logger.info("#{Time.now}")
end
end

@ -1,7 +1,7 @@
# 执行示例 bundle exec rake zip_pack:shixun_pack args=123,2323
namespace :zip_pack do
desc "手工打包作品"
OUTPUT_FOLDER = "/tmp"
OUTPUT_FOLDER = "#{Rails.root}/files/archiveZip"
task :shixun_pack => :environment do
@ -19,6 +19,7 @@ namespace :zip_pack do
student_works = homework.score_student_works
if student_works.size > 0
pdfs = []
Zip::File.open(zipfile_name, Zip::File::CREATE) do |zip|
student_works.find_each.map do |student_work|
export = ExportShixunReportService.new(homework, student_work)
@ -26,6 +27,7 @@ namespace :zip_pack do
pdfs << pdf
begin
zip.add(export.filename, pdf.path)
puts "out: #{export.filename}_#{pdf.path}"
rescue => ex
Rails.logger.error(ex.message)

@ -0,0 +1,38 @@
/*
* @Description: 填空
* @Author: tangjiang
* @Github:
* @Date: 2020-01-06 09:02:29
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-06 16:04:46
*/
import Quill from 'quill';
let Inline = Quill.import('blots/inline');
class FillBlot extends Inline {
static create (value) {
const node = super.cerate();
node.classList.add('icon icon-bianji2');
node.setAttribute('data-fill', 'fill');
node.addEventListener('DOMNodeRemoved', function () {
alert(123);
}, false);
return node;
}
static value (node) {
return {
dataSet: node.getAttribute('data-fill'),
onDOMNodeRemoved: () => {
alert('123456');
}
}
}
}
FillBlot.blotName = "fill";
FillBlot.tagName = "span";
export default FillBlot;

@ -0,0 +1,94 @@
<!--
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2020-01-06 16:20:03
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-06 17:13:19
-->
## QuillForEditor 使用 [https://quilljs.com/]
### 导入
- 目录 src/common/quillForEditor (默认加载当前文件夹下的 index.js 文件)
### 参数
| 字段 | 描述 |
| ----- | ----- |
| placeholder | 提示信息 |
| readOnly | 只读(只读取模式时,没有 工具栏且内容不可编辑通常用于展示quill内容) |
| autoFocus | 自动获得焦点 |
| options | 配置参数, 指定工具栏内容 |
| value | 文本编辑器内容 |
| imgAttrs | 指定上传图片的尺寸 |
| style | 指定quill容器样式 |
| wrapStyle | 指定包裹quill容器的样式|
| onContentChange | 当编辑器内容变化时调用此回调函数(注: 此时返回的内容为对象,提交到后台时需要格式成 JSON 字符串: JSON.stringify(xx)) |
| showUploadImage | 点击放大上传成功后的图片, 返回上传成功后的图片 url, (评论时点击图片这么大)|
### 添加工具栏
- 默认所有的
```
const options = [
'bold', // 加粗
'italic', // 斜体
'underline', // 下划线
{size: ['12px', '14px', '16px', '18px', '20px']}, // 字体大小
{align: []}, // 对齐方式
{list: 'ordered'}, // 有序列表
{list: 'bullet'}, // 无序列表
{script: 'sub'}, // 下标 x2
{script: 'super'}, // 上标 平方 (x2)
{ 'color': [] }, // 字体颜色
{ 'background': [] }, // 背景色
{header: [1,2,3,4,5,false]}, // H1,H2 ...
'blockquote', // 文件左边加一个边框样式
'code-block', // 块内容
'link', // 链接
'image', // 图片
'video', // 视频
'formula', // 数学公式
'clean' // 清除
]
```
### 使用
````
import QuillForEditor from 'xxx';
// 指定需要显示的工具栏信息, 不指定加载全部
const options = [
];
/**
* @description 获取编辑器返回的内容
* @params [Object] value 编辑器内容
*/
const handleCtxChange = (value) => {
// 编辑器内容非空判断
const _text = quill.getText();
const reg = /^[\s\S]*.*[^\s][\s\S]*$/;
if (!reg.test(_text)) {
// 处理编辑器内容为空
} else {
// 提交到后台的内容需要处理一下;
value = JSON.stringify(value)
}
}
<QuillForEditor
options={options}
onContentChange={handleCtxChange}
>
</QuillForEditor>
````

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-12-18 08:49:30
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-31 15:11:37
* @LastEditTime : 2020-01-06 16:45:50
*/
import './index.scss';
import 'quill/dist/quill.core.css'; // 核心样式
@ -18,8 +18,11 @@ import deepEqual from './deepEqual.js'
import { fetchUploadImage } from '../../services/ojService.js';
import { getImageUrl } from 'educoder'
import ImageBlot from './ImageBlot';
// import Toolbar from 'quill/modules/toolbar';
import FillBlot from './FillBlot';
const Size = Quill.import('attributors/style/size');
const Font = Quill.import('formats/font');
// const Color = Quill.import('attributes/style/color');
Size.whitelist = ['12px', '14px', '16px', '18px', '20px', false];
Font.whitelist = ['SimSun', 'SimHei','Microsoft-YaHei','KaiTi','FangSong','Arial','Times-New-Roman','sans-serif'];
@ -29,6 +32,8 @@ window.katex = katex;
Quill.register(ImageBlot);
Quill.register(Size);
Quill.register(Font, true);
// Quill.register({'modules/toolbar': Toolbar});
Quill.register(FillBlot);
// Quill.register(Color);
function QuillForEditor ({
@ -42,6 +47,7 @@ function QuillForEditor ({
wrapStyle = {},
showUploadImage,
onContentChange,
// addFill, // 点击填空成功的回调
// getQuillContent
}) {
// toolbar 默认值
@ -62,6 +68,8 @@ function QuillForEditor ({
// quill 实例
const [quill, setQuill] = useState(null);
const [selection, setSelection] = useState(null);
const [fillCount, setFillCount] = useState(0);
const [quillCtx, setQuillCtx] = useState({});
// 文本内容变化时
const handleOnChange = content => {
@ -70,18 +78,23 @@ function QuillForEditor ({
};
const renderOptions = options || defaultConfig;
// quill 配置信息
const quillOption = {
modules: {
toolbar: renderOptions
// toolbar: {
// container: renderOptions
// }
},
readOnly,
placeholder,
theme: readOnly ? 'bubble' : 'snow'
theme: readOnly ? 'bubble' : 'snow',
};
useEffect(() => {
const quillNode = document.createElement('div');
editorRef.current.appendChild(quillNode);
const _quill = new Quill(editorRef.current, quillOption);
@ -121,13 +134,20 @@ function QuillForEditor ({
}
}
});
_quill.getModule('toolbar').addHandler('fill', (e) => {
setFillCount(fillCount + 1);
const range = _quill.getSelection(true);
_quill.insertText(range.index, '▁');
// 点击填空图标时,插入一个下划线
// 1. 获取编辑器内容
});
}, []);
// 设置值
useEffect(() => {
if (!quill) return
// debugger;
const previous = quill.getContents()
if (value && value.hasOwnProperty('ops')) {
@ -173,7 +193,36 @@ function QuillForEditor ({
let handler;
quill.on(
'text-change',
(handler = () => {
(handler = (delta, oldDelta, source) => {
// let del = false;
// let delLen = 1;
// delta.ops.forEach(o => {
// // 存在删除并且只删除一个
// if (o.delete) {
// del = true;
// }
// });
// 删除编辑器内容
// if (del) {
// delLen = delta.ops[0].retain || 1; // 删除数组的长度
// // 获取删除的内容并判断其它是否有填空内容
// console.log('原编辑器内容', oldDelta);
// console.log('编辑器内容', quillCtx);
// }
// 获取删除的内容
// oldDelta
// if (del) {
// const ops = oldDelta.ops;
// const len = ops.length;
// // if (ops[len - 1] && ops[len - 1].insert) {
// // const str = ops[len - 1].insert;
// // const _len = str.length;
// // const _last = str.substr(_len - 1);
// // console.log('删除的一项', _last);
// // }
// }
const _ctx = quill.getContents();
setQuillCtx(_ctx);
handleOnChange(quill.getContents()); // getContents: 检索编辑器内容
})
);

@ -110,4 +110,21 @@
content: "微软雅黑";
font-family: "Microsoft YaHei";
}
//
.ql-snow .ql-fill{
display: inline-block;
position: relative;
color: #05101A;
// font-size: 18px;
vertical-align: top;
&::before{
position: absolute;
left: 50%;
top: -1px;
content: '\e709';
font-family: 'iconfont';
margin-left: -7px;
}
}
}

@ -214,7 +214,7 @@ const NewOrEditTask = (props) => {
{ renderQuit() }
</div>
<div className="split-pane-area">
<SplitPane split="vertical" minSize={350} maxSize={-350} defaultSize="40%">
<SplitPane className='outer-split-pane' split="vertical" minSize={350} maxSize={-350} defaultSize="40%">
<div className={'split-pane-left'}>
<LeftPane />
</div>

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-20 10:35:40
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-03 19:02:17
* @LastEditTime : 2020-01-06 16:17:22
*/
import './index.scss';
// import 'katex/dist/katex.css';
@ -277,7 +277,7 @@ class EditTab extends React.Component {
const quillConfig = [
{ header: 1}, {header: 2},
// {size: ['12px', '14px', '16px', '18px', '20px', false]},
'bold', 'italic', 'underline', 'strike', // 切换按钮
'bold', 'italic', 'underline', 'strike', // 切换按钮
'blockquote', 'code-block', // 代码块
{align: []}, { 'list': 'ordered' }, { 'list': 'bullet' }, // 列表
{ 'script': 'sub'}, { 'script': 'super' },

@ -217,3 +217,42 @@
.Resizer.disabled:hover {
border-color: transparent;
}
.outer-split-pane{
& > .Resizer{
position: relative;
&::before,
&::after{
position: absolute;
right: -12px;
top: 50%;
transition: opacity, background .3s;
}
&::before{
content: '';
border-radius: 50%;
background: rgba(235,235,235,.3);
width: 24px;
height: 24px;
// font-size: 12px;
}
&::after{
content: '\e712';
font-family: 'iconfont';
transform: scale(.7);
color: #666;
margin-top: -2px;
right: -14px;
opacity: .3;
}
&:hover{
&::before{
background: rgba(235,235,235, 1);
}
&::after{
opacity: 1;
}
}
}
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-23 10:53:19
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-27 16:22:47
* @LastEditTime : 2020-01-06 15:27:34
*/
import './index.scss';
import React, { useEffect, useState } from 'react';
@ -144,7 +144,7 @@ function StudentStudy (props) {
</div>
</div>
<div className="split-pane-area">
<SplitPane split="vertical" minSize={350} maxSize={-350} defaultSize="40%">
<SplitPane className="outer-split-pane" split="vertical" minSize={350} maxSize={-350} defaultSize="40%">
<div className={'split-pane-left'}>
<LeftPane />
</div>

Loading…
Cancel
Save