Merge branch 'dev_aliyun' of https://bdgit.educoder.net/Hjqreturn/educoder into dev_aliyun

newyslclassrooms
caicai8 5 years ago
commit 4b890b0650

@ -155,8 +155,14 @@ class ChallengesController < ApplicationController
def index def index
uid_logger("identifier: #{params}") uid_logger("identifier: #{params}")
base_columns = "challenges.id, challenges.subject, challenges.st, challenges.score, challenges.position,
@challenges = @shixun.challenges.fields_for_list challenges.shixun_id, games.identifier, games.status"
join_sql = "LEFT JOIN games ON games.challenge_id = challenges.id AND games.user_id = #{current_user.id}"
# 下面2个参数是为了解决列表获取通关人数与正在游玩人数的问题
@pass_games_map = @shixun.challenges.joins(:games).where(games: {status:2}).group(:challenge_id).reorder(nil).count
@play_games_map = @shixun.challenges.joins(:games).where(games: {status:[0,1]}).group(:challenge_id).reorder(nil).count
@challenges = @shixun.challenges.joins(join_sql).select(base_columns)
@editable = @shixun.status == 0 # before_action有判断权限如果没发布则肯定是管理人员 @editable = @shixun.status == 0 # before_action有判断权限如果没发布则肯定是管理人员
@user = current_user @user = current_user

@ -0,0 +1,34 @@
class CourseVideosController < ApplicationController
before_action :require_login
before_action :validate_params
before_action :find_course, only: [:create]
before_action :find_video, only: [:update]
before_action :teacher_allowed
def create
title = params[:name].strip
link = params[:link].strip
@course.course_videos.create!(title: title, link: link, is_link: 1, user_id: current_user.id)
render_ok
end
def update
title = params[:name].strip
link = params[:link].strip
@video.update!(title: title, link: link)
render_ok
end
private
def validate_params
tip_exception("视频名称不能为空") if params[:name].blank?
tip_exception("链接地址不能为空") if params[:link].blank?
end
def find_video
@video = CourseVideo.find params[:id]
@course = @video.course
end
end

@ -102,22 +102,32 @@ class CoursesController < ApplicationController
end end
def course_videos def course_videos
videos = @course.videos videos = @course.course_videos
@video_module = @course.course_modules.find_by(module_type: "video")
if params[:category_id].present? && params[:category_id].to_i != 0 if params[:category_id].present? && params[:category_id].to_i != 0
@category = @video_module&.course_second_categories.find_by(id: params[:category_id])
tip_exception("子目录id有误") if !@category.present?
videos = videos.where(course_second_category_id: params[:category_id].to_i) videos = videos.where(course_second_category_id: params[:category_id].to_i)
end end
videos = custom_sort(videos, params[:sort_by], params[:sort_direction]) videos = custom_sort(videos, params[:sort_by], params[:sort_direction])
@count = videos.count @count = videos.count
@videos = paginate videos.includes(user: :user_extension) @videos = paginate videos.includes(video: [user: :user_extension], user: :user_extension)
end end
def delete_course_video def delete_course_video
video = Video.find_by(id: params[:video_id]) if params[:is_link]
tip_exception(404, "找不到资源") if video.blank? video = @course.course_videos.find_by!(id: params[:video_id])
tip_exception(403, "...") unless (video.user_id == current_user.id || current_user.admin_or_business?) tip_exception(403, "...") unless (video.user_id == current_user.id || current_user.admin_or_business?)
video.destroy! video.destroy!
AliyunVod::Service.delete_video([video.uuid]) rescue nil else
video = Video.find_by(id: params[:video_id])
tip_exception(404, "找不到资源") if video.blank?
tip_exception(403, "...") unless (video.user_id == current_user.id || current_user.admin_or_business?)
video.destroy!
AliyunVod::Service.delete_video([video.uuid]) rescue nil
end
render_ok render_ok
end end
@ -127,7 +137,7 @@ class CoursesController < ApplicationController
category = @course.course_second_categories.find_by(id: params[:new_category_id]) category = @course.course_second_categories.find_by(id: params[:new_category_id])
if params[:new_category_id].to_i == 0 || category.present? if params[:new_category_id].to_i == 0 || category.present?
videos = @course.course_videos.where(id: params[:video_ids]) videos = @course.course_videos.where(video_id: params[:video_ids]).or(@course.course_videos.where(id: params[:video_ids]))
videos.update_all(course_second_category_id: params[:new_category_id]) videos.update_all(course_second_category_id: params[:new_category_id])
normal_status(0, "操作成功") normal_status(0, "操作成功")

@ -7,7 +7,7 @@ class FilesController < ApplicationController
before_action :file_validate_sort_type, only: :index before_action :file_validate_sort_type, only: :index
before_action :validate_send_message_to_course_params, only: :bulk_send before_action :validate_send_message_to_course_params, only: :bulk_send
before_action :set_pagination, only: %i[index public_with_course_and_project mine_with_course_and_project] before_action :set_pagination, only: %i[index public_with_course_and_project mine_with_course_and_project]
before_action :validate_upload_params, only: %i[upload import] before_action :validate_upload_params, only: %i[import]
before_action :find_file, only: %i[show setting update] before_action :find_file, only: %i[show setting update]
before_action :publish_params, only: %i[upload import update] before_action :publish_params, only: %i[upload import update]
@ -163,6 +163,7 @@ class FilesController < ApplicationController
# 上传资源 # 上传资源
def upload def upload
find_course_second_category_id
attachment_ids = params[:attachment_ids] attachment_ids = params[:attachment_ids]
course_second_category_id = params[:course_second_category_id] || 0 # 0: 为主目录, 其他为次目录id course_second_category_id = params[:course_second_category_id] || 0 # 0: 为主目录, 其他为次目录id
# is_unified_setting = params.has_key?(:is_unified_setting) ? params[:is_unified_setting] : true # is_unified_setting = params.has_key?(:is_unified_setting) ? params[:is_unified_setting] : true
@ -170,25 +171,48 @@ class FilesController < ApplicationController
# course_group_publish_times = params[:course_group_publish_times] || [] # course_group_publish_times = params[:course_group_publish_times] || []
begin begin
attachment_ids.each do |attchment_id| if attachment_ids.present?
attachment = Attachment.find_by_id attchment_id attachment_ids.each do |attchment_id|
unless attachment.nil? attachment = Attachment.find_by_id attchment_id
attachment.container = @course unless attachment.nil?
attachment.course_second_category_id = course_second_category_id attachment.container = @course
attachment.description = params[:description] attachment.course_second_category_id = course_second_category_id
attachment.is_public = params[:is_public] && @course.is_public == 1 ? 1 : 0 attachment.description = params[:description]
attachment.is_publish = @atta_is_publish attachment.is_public = params[:is_public] && @course.is_public == 1 ? 1 : 0
attachment.delay_publish = @atta_delay_publish attachment.is_publish = @atta_is_publish
attachment.publish_time = @atta_publish_time attachment.delay_publish = @atta_delay_publish
attachment.unified_setting = @unified_setting attachment.publish_time = @atta_publish_time
if @unified_setting == 0 attachment.unified_setting = @unified_setting
attachment_group_setting attachment, params[:group_settings] if @unified_setting == 0
attachment_group_setting attachment, params[:group_settings]
end
# attachment.set_publish_time(publish_time) if is_unified_setting
# attachment.set_course_group_publish_time(@course, course_group_publish_times) if @course.course_groups.size > 0 && !is_unified_setting && publish_time.blank?
attachment.save!
end end
# attachment.set_publish_time(publish_time) if is_unified_setting
# attachment.set_course_group_publish_time(@course, course_group_publish_times) if @course.course_groups.size > 0 && !is_unified_setting && publish_time.blank?
attachment.save!
end end
else
tip_exception("资源名称不能为空") if params[:name].blank?
tip_exception("资源名称不能超过60个字符") if params[:name].strip.length > 60
tip_exception("链接地址不能为空") if params[:link].blank?
attachment = Attachment.new
attachment.container = @course
attachment.course_second_category_id = course_second_category_id
attachment.author_id = current_user.id
attachment.filename = params[:name].strip
attachment.link = params[:link].strip
attachment.description = params[:description]
attachment.is_public = params[:is_public] && @course.is_public == 1 ? 1 : 0
attachment.is_publish = @atta_is_publish
attachment.delay_publish = @atta_delay_publish
attachment.publish_time = @atta_publish_time
attachment.unified_setting = @unified_setting
if @unified_setting == 0
attachment_group_setting attachment, params[:group_settings]
end
attachment.save!
end end
rescue Exception => e rescue Exception => e
uid_logger_error(e.message) uid_logger_error(e.message)
tip_exception(e.message) tip_exception(e.message)

@ -1,6 +1,6 @@
class Users::VideosController < Users::BaseController class Users::VideosController < Users::BaseController
before_action :private_user_resources!, :check_account before_action :private_user_resources!, :check_account
before_action :require_teacher! before_action :require_teacher!, except: [:destroy]
before_action :require_auth_teacher!, except: [:index, :review] before_action :require_auth_teacher!, except: [:index, :review]
helper_method :current_video helper_method :current_video
@ -53,6 +53,19 @@ class Users::VideosController < Users::BaseController
render_error(ex.message) render_error(ex.message)
end end
def destroy
video = observed_user.videos.find_by(id: params[:video_id])
render_forbidden unless video.user_id != observed_user.id || !current_user.admin_or_business?
return render_not_found if video.blank?
return render_error('该状态下不能删除视频') unless video.pending?
video.destroy!
AliyunVod::Service.delete_video([video.uuid]) rescue nil
render_ok
end
private private
def current_video def current_video

@ -58,12 +58,18 @@ class Weapps::AttendancesController < ApplicationController
@history_attendances = @course.course_attendances.where(id: history_attendance_ids.uniq). @history_attendances = @course.course_attendances.where(id: history_attendance_ids.uniq).
where("attendance_date < '#{current_date}' or (attendance_date = '#{current_date}' and end_time < '#{current_end_time}')").order("id desc") where("attendance_date < '#{current_date}' or (attendance_date = '#{current_date}' and end_time < '#{current_end_time}')").order("id desc")
@current_attendance = @course.course_attendances.where(id: all_attendance_ids.uniq). @current_attendances = @course.course_attendances.where(id: all_attendance_ids.uniq).
where("attendance_date = '#{current_date}' and start_time <= '#{current_end_time}' and end_time > '#{current_end_time}'") where("attendance_date = '#{current_date}' and start_time <= '#{current_end_time}' and end_time > '#{current_end_time}'")
@history_count = @history_attendances.size @history_count = @history_attendances.size
# 当前签到如果存在快捷签到,则直接签到
quick_attendances = @current_attendances.where(mode: "QUICK")
if quick_attendances.present?
student_direct_attendance quick_attendances, member
end
student_attendance_ids = @history_attendances.pluck(:id) student_attendance_ids = @history_attendances.pluck(:id)
student_attendance_ids += @current_attendance.present? ? @current_attendance.pluck(:id) : [] student_attendance_ids += @current_attendances.present? ? @current_attendances.pluck(:id) : []
if student_attendance_ids.uniq.blank? if student_attendance_ids.uniq.blank?
@normal_count = 0 @normal_count = 0
@ -141,4 +147,16 @@ class Weapps::AttendancesController < ApplicationController
def edit_auth def edit_auth
tip_exception(403, "") unless @user_course_identity < Course::PROFESSOR || @attendance.user_id == current_user.id tip_exception(403, "") unless @user_course_identity < Course::PROFESSOR || @attendance.user_id == current_user.id
end end
def student_direct_attendance quick_attendances, member
quick_attendances.each do |attendance|
current_attendance = attendance.course_member_attendances.find_by(user_id: member.user_id)
if current_attendance.present?
current_attendance.update!(attendance_status: "NORMAL", attendance_mode: "QUICK")
else
attendance.course_member_attendances.create!(course_member_id: member.id, user_id: member.user_id, course_id: attendance.course_id,
course_group_id: member.course_group_id, attendance_status: "NORMAL", attendance_mode: "QUICK")
end
end
end
end end

@ -98,7 +98,7 @@ module CoursesHelper
when "attachment" when "attachment"
"/courses/#{course.id}/file/#{category.id}" "/courses/#{course.id}/file/#{category.id}"
when "video" when "video"
"/courses/#{course.id}/course_videos/#{category.id}" "/courses/#{course.id}/course_video/#{category.id}"
end end
end end

@ -69,12 +69,13 @@ class Challenge < ApplicationRecord
end end
# 开启挑战 # 开启挑战
def open_game user_id, shixun def open_game shixun
game = self.games.where(user_id: user_id).first # 这里的identifier,status是关联了games取了games的identifier,status
if game.present? identifier = self.identifier
shixun.task_pass || game.status != 3 ? "/tasks/#{game.identifier}" : "" if identifier.present?
shixun.task_pass || self.status != 3 ? "/tasks/#{identifier}" : ""
else else
"/api/shixuns/#{shixun.identifier}/shixun_exec" self.position == 1 ? "/api/shixuns/#{shixun.identifier}/shixun_exec" : ""
end end
end end
@ -92,16 +93,16 @@ class Challenge < ApplicationRecord
# end # end
## 用户关卡状态 0: 不能开启实训; 1:直接开启; 2表示已完成 ## 用户关卡状态 0: 不能开启实训; 1:直接开启; 2表示已完成
def user_tpi_status user_id def user_tpi_status shixun
# todo: 以前没加索引导致相同关卡,同一用户有多个games # todo: 以前没加索引导致相同关卡,同一用户有多个games
# 允许跳关则直接开启 # 允许跳关则直接开启
game = games.where(user_id: user_id).take identifier = self.identifier
if game.blank? if identifier.blank?
position == 1 ? 1 : 0 self.position == 1 ? 1 : 0
else else
if game.status == 3 if status == 3
shixun.task_pass ? 1 : 0 shixun.task_pass ? 1 : 0
elsif game.status == 2 elsif status == 2
2 2
else else
1 1

@ -1,6 +1,6 @@
class CourseAttendance < ApplicationRecord class CourseAttendance < ApplicationRecord
# status: 0: 未开启1已开启2已截止 # status: 0: 未开启1已开启2已截止
# mode: 0 两种签到1 二维码签到2 数字签到 # mode: 0 两种签到1 二维码签到2 数字签到3 快捷签到
enum mode: { ALL: 0, QRCODE: 1, NUMBER: 2, QUICK: 3 } enum mode: { ALL: 0, QRCODE: 1, NUMBER: 2, QUICK: 3 }
belongs_to :course belongs_to :course

@ -1,6 +1,6 @@
class CourseMemberAttendance < ApplicationRecord class CourseMemberAttendance < ApplicationRecord
# attendance_mode 0 初始数据1 二维码签到2 数字签到3 老师签到 # attendance_mode 0 初始数据1 二维码签到2 数字签到3 老师签到
enum attendance_mode: { DEFAULT: 0, QRCODE: 1, NUMBER: 2, TEACHER: 3} enum attendance_mode: { DEFAULT: 0, QRCODE: 1, NUMBER: 2, QUICK: 3, TEACHER: 4}
# attendance_status 1 正常签到2 请假0 旷课 # attendance_status 1 正常签到2 请假0 旷课
enum attendance_status: { NORMAL: 1, LEAVE: 2, ABSENCE: 0 } enum attendance_status: { NORMAL: 1, LEAVE: 2, ABSENCE: 0 }
belongs_to :course_member belongs_to :course_member

@ -1,4 +1,7 @@
class CourseVideo < ApplicationRecord class CourseVideo < ApplicationRecord
belongs_to :course belongs_to :course
belongs_to :video belongs_to :video, optional: true
belongs_to :user, optional: true
validates :title, length: { maximum: 60, too_long: "不能超过60个字符" }
end end

@ -16,10 +16,12 @@ if @challenges.present?
json.st challenge.st json.st challenge.st
json.name challenge.subject json.name challenge.subject
json.score challenge.score json.score challenge.score
json.passed_count challenge.user_passed_count json.passed_count @pass_games_map.fetch(challenge.id, 0)
json.playing_count challenge.playing_count #json.passed_count challenge.user_passed_count
json.playing_count @play_games_map.fetch(challenge.id, 0)
#json.playing_count challenge.playing_count
json.name_url shixun_challenge_path(challenge, shixun_identifier: @shixun.identifier) json.name_url shixun_challenge_path(challenge, shixun_identifier: @shixun.identifier)
#json.open_game challenge.open_game(@user.id, @shixun) json.open_game challenge.open_game(@shixun)
if @editable if @editable
json.edit_url edit_shixun_challenge_path(challenge, shixun_identifier: @shixun.identifier) json.edit_url edit_shixun_challenge_path(challenge, shixun_identifier: @shixun.identifier)
json.delete_url shixun_challenge_path(challenge, shixun_identifier: @shixun.identifier) json.delete_url shixun_challenge_path(challenge, shixun_identifier: @shixun.identifier)
@ -27,6 +29,6 @@ if @challenges.present?
json.down_url index_down_shixun_challenge_path(challenge, :shixun_identifier => @shixun.identifier) if @shixun.challenges_count != challenge.position json.down_url index_down_shixun_challenge_path(challenge, :shixun_identifier => @shixun.identifier) if @shixun.challenges_count != challenge.position
end end
#json.passed challenge.has_passed?(@user.id) #json.passed challenge.has_passed?(@user.id)
json.status challenge.user_tpi_status @user.id json.status challenge.user_tpi_status(@shixun)
end end
end end

@ -1,3 +1,22 @@
json.count @count json.count @count
json.videos @videos, partial: 'users/videos/video', as: :video
json.course_id @course.id json.videos @videos do |video|
if video.is_link
json.(video, :id, :title, :link, :user_id)
user = video.user
json.user_name user&.real_name
json.user_img url_to_avatar(user)
json.user_login user&.login
else
json.partial! 'users/videos/video', locals: { video: video.video }
end
end
json.course_id @course.id
if @category.present?
json.category_id @category.id
json.category_name @category.name
end
json.course_module_id @video_module&.id
json.has_category @video_module.course_second_categories.size > 0

@ -1,4 +1,4 @@
json.current_attendance @current_attendance do |attendance| json.current_attendance @current_attendances do |attendance|
json.partial! 'student_attendance', locals: {attendance: attendance} json.partial! 'student_attendance', locals: {attendance: attendance}
end end

@ -183,6 +183,9 @@ zh-CN:
attendance_date: '签到日期' attendance_date: '签到日期'
start_time: '开始时间' start_time: '开始时间'
end_time: '结束时间' end_time: '结束时间'
course_video:
title: '视频名称'
link: '链接地址'

@ -538,6 +538,8 @@ Rails.application.routes.draw do
get 'search_slim' get 'search_slim'
end end
resources :course_videos, only:[:create, :update], shallow: true
resources :course_stages, shallow: true do resources :course_stages, shallow: true do
member do member do
post :up_position post :up_position

@ -0,0 +1,5 @@
class MigrateMemberAttendanceMode < ActiveRecord::Migration[5.2]
def change
CourseMemberAttendance.where(attendance_mode: 3).update_all(attendance_mode: 4)
end
end

@ -0,0 +1,8 @@
class AddLinkToCourseVideos < ActiveRecord::Migration[5.2]
def change
add_column :course_videos, :is_link, :boolean, default: 0
add_column :course_videos, :title, :string
add_column :course_videos, :link, :string
add_column :course_videos, :user_id, :integer, index: true
end
end

@ -0,0 +1,5 @@
class AddLinkToAttachments < ActiveRecord::Migration[5.2]
def change
add_column :attachments, :link, :string
end
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

@ -10,11 +10,9 @@ import './index.scss';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Form, Button, Input } from 'antd'; import { Form, Button, Input } from 'antd';
import QuillForEditor from '../../quillForEditor'; import QuillForEditor from '../../quillForEditor';
// import { QuillDeltaToHtmlConverter } from 'quill-delta-to-html'
// import {formatDelta} from './util';
const FormItem = Form.Item; const FormItem = Form.Item;
function CommentForm (props) { function CommentForm(props) {
const { const {
onCancel, onCancel,
@ -28,9 +26,6 @@ function CommentForm (props) {
const [focus, setFocus] = useState(false); const [focus, setFocus] = useState(false);
const options = [ const options = [
// ['bold', 'italic', 'underline'],
// [{header: [1,2,3,false]}],
'code-block',
'link', 'link',
'image', 'image',
'formula' 'formula'
@ -52,12 +47,10 @@ function CommentForm (props) {
// 编辑器内容变化时 // 编辑器内容变化时
const handleContentChange = (content) => { const handleContentChange = (content) => {
console.log('编辑器内容', content);
setCtx(content); setCtx(content);
try { try {
// const _html = new QuillDeltaToHtmlConverter(content.ops, {}).convert(); // const _html = new QuillDeltaToHtmlConverter(content.ops, {}).convert();
// props.form.setFieldsValue({'comment': _html.replace(/<\/?[^>]*>/g, '')}); props.form.setFieldsValue({ 'comment': content });
props.form.setFieldsValue({'comment': content});
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
@ -69,7 +62,7 @@ function CommentForm (props) {
if (!err) { if (!err) {
setShowQuill(false); setShowQuill(false);
const content = ctx; const content = ctx;
props.form.setFieldsValue({'comment': ''}); props.form.setFieldsValue({ 'comment': '' });
setCtx(''); setCtx('');
// const _html = formatDelta(content.ops); // const _html = formatDelta(content.ops);
// console.log('保存的内容=====》》》》', content); // console.log('保存的内容=====》》》》', content);
@ -95,7 +88,7 @@ function CommentForm (props) {
{ {
getFieldDecorator('comment', { getFieldDecorator('comment', {
rules: [ rules: [
{ required: true, message: '评论内容不能为空'} { required: true, message: '评论内容不能为空' }
], ],
})( })(
<Input <Input
@ -112,7 +105,7 @@ function CommentForm (props) {
} }
<QuillForEditor <QuillForEditor
imgAttrs={{width: '60px', height: '30px'}} imgAttrs={{ width: '60px', height: '30px' }}
wrapStyle={{ wrapStyle={{
height: showQuill ? 'auto' : '0px', height: showQuill ? 'auto' : '0px',
opacity: showQuill ? 1 : 0, opacity: showQuill ? 1 : 0,
@ -130,7 +123,7 @@ function CommentForm (props) {
</FormItem> </FormItem>
<FormItem style={{ textAlign: 'right', display: showQuill ? 'block' : 'none' }}> <FormItem style={{ textAlign: 'right', display: showQuill ? 'block' : 'none' }}>
<Button onClick={handleCancle}>取消</Button> <Button onClick={handleCancle}>取消</Button>
<Button onClick={handleSubmit} type="primary" style={{ marginLeft: '10px'}}>发送</Button> <Button onClick={handleSubmit} type="primary" style={{ marginLeft: '10px' }}>发送</Button>
</FormItem> </FormItem>
</Form> </Form>
); );

@ -237,7 +237,6 @@ function CommentItem({
/> />
</div> </div>
{/* 显示上传的图片信息 */}
<div className="show_upload_image" style={{ display: url ? 'block' : 'none' }}> <div className="show_upload_image" style={{ display: url ? 'block' : 'none' }}>
<Icon type="close" className="image_close" onClick={handleClose} /> <Icon type="close" className="image_close" onClick={handleClose} />
<div className="image_info"> <div className="image_info">

@ -8,19 +8,15 @@
*/ */
import Quill from 'quill'; import Quill from 'quill';
let Inline = Quill.import('blots/inline'); let Inline = Quill.import('blots/inline');
// const BlockEmbed = Quill.import('blots/embed');
class FillBlot extends Inline { class FillBlot extends Inline {
static create (value) { static create(value) {
const node = super.cerate(value); const node = super.cerate(value);
// node.classList.add('icon icon-bianji2');
// node.setAttribute('data-fill', 'fill');
console.log('编辑器值===》》》》》', value);
node.setAttribute('data_index', value.data_index); node.setAttribute('data_index', value.data_index);
node.nodeValue = value.text; node.nodeValue = value.text;
return node; return node;
} }
static value (node) { static value(node) {
return { return {
// dataSet: node.getAttribute('data-fill'), // dataSet: node.getAttribute('data-fill'),
data_index: node.getAttribute('data_index') data_index: node.getAttribute('data_index')

@ -17,7 +17,6 @@ export default class ImageBlot extends BlockEmbed {
const node = super.create(); const node = super.create();
node.setAttribute('alt', value.alt); node.setAttribute('alt', value.alt);
node.setAttribute('src', value.url); node.setAttribute('src', value.url);
// console.log('~~~~~~~~~~~', node, value);
node.addEventListener('click', function () { node.addEventListener('click', function () {
value.onclick(value.url); value.onclick(value.url);
}, false); }, false);
@ -33,25 +32,14 @@ export default class ImageBlot extends BlockEmbed {
} }
// 宽度和高度都不存在时, // 宽度和高度都不存在时,
if (!value.width && !value.height) { if (!value.width && !value.height) {
// node.setAttribute('display', 'block');
node.setAttribute('width', '100%'); node.setAttribute('width', '100%');
} }
// node.setAttribute('style', { cursor: 'pointer' });
// if (node.onclick) {
// console.log('image 有图片点击事件======》》》》》》');
// // node.setAttribute('onclick', node.onCLick);
// }
// 给图片添加点击事件
// node.onclick = () => {
// value.onClick && value.onClick(value.url);
// }
return node; return node;
} }
// 获取节点值 // 获取节点值
static value (node) { static value(node) {
return { return {
alt: node.getAttribute('alt'), alt: node.getAttribute('alt'),
@ -61,7 +49,6 @@ export default class ImageBlot extends BlockEmbed {
height: node.height, height: node.height,
display: node.getAttribute('display'), display: node.getAttribute('display'),
id: node.id, id: node.id,
// style: node.style
}; };
} }
} }

@ -20,16 +20,17 @@ import { fetchUploadImage } from '../../services/ojService.js';
import { getImageUrl } from 'educoder' import { getImageUrl } from 'educoder'
import ImageBlot from './ImageBlot'; import ImageBlot from './ImageBlot';
import FillBlot from './FillBlot'; import FillBlot from './FillBlot';
import LinkBlot from './link-blot'
var Size = Quill.import('attributors/style/size'); var Size = Quill.import('attributors/style/size');
// const Color = Quill.import('attributes/style/color');
Size.whitelist = ['14px', '16px', '18px', '20px', false]; Size.whitelist = ['14px', '16px', '18px', '20px', false];
var fonts = ['Microsoft-YaHei','SimSun', 'SimHei','KaiTi','FangSong']; var fonts = ['Microsoft-YaHei', 'SimSun', 'SimHei', 'KaiTi', 'FangSong'];
var Font = Quill.import('formats/font'); var Font = Quill.import('formats/font');
Font.whitelist = fonts; //将字体加入到白名单 Font.whitelist = fonts; //将字体加入到白名单
window.Quill = Quill; window.Quill = Quill;
window.katex = katex; window.katex = katex;
Quill.register(ImageBlot); Quill.register(ImageBlot);
Quill.register(Size); Quill.register(Size);
Quill.register(LinkBlot);
Quill.register(Font, true); Quill.register(Font, true);
// Quill.register({'modules/toolbar': Toolbar}); // Quill.register({'modules/toolbar': Toolbar});
Quill.register({ Quill.register({
@ -38,7 +39,7 @@ Quill.register({
// Quill.register(Color); // Quill.register(Color);
function QuillForEditor ({ function QuillForEditor({
placeholder, placeholder,
readOnly, readOnly,
autoFocus = false, autoFocus = false,
@ -51,17 +52,16 @@ function QuillForEditor ({
onContentChange, onContentChange,
addFill, // 点击填空成功的回调 addFill, // 点击填空成功的回调
deleteFill // 删除填空,返回删除的下标 deleteFill // 删除填空,返回删除的下标
// getQuillContent
}) { }) {
// toolbar 默认值 // toolbar 默认值
const defaultConfig = [ const defaultConfig = [
'bold', 'italic', 'underline', 'bold', 'italic', 'underline',
{size: ['14px', '16px', '18px', '20px']}, { size: ['14px', '16px', '18px', '20px'] },
{align: []}, {list: 'ordered'}, {list: 'bullet'}, // 列表 { align: [] }, { list: 'ordered' }, { list: 'bullet' }, // 列表
{script: 'sub'}, {script: 'super'}, { script: 'sub' }, { script: 'super' },
{ 'color': [] }, { 'background': [] }, { 'color': [] }, { 'background': [] },
{ 'font': []}, { 'font': [] },
{header: [1,2,3,4,5,false]}, { header: [1, 2, 3, 4, 5, false] },
'blockquote', 'code-block', 'blockquote', 'code-block',
'link', 'image', 'video', 'link', 'image', 'video',
'formula', 'formula',
@ -77,7 +77,6 @@ function QuillForEditor ({
// 文本内容变化时 // 文本内容变化时
const handleOnChange = content => { const handleOnChange = content => {
// getQuillContent && getQuillContent(quill);
onContentChange && onContentChange(content, quill); onContentChange && onContentChange(content, quill);
}; };
@ -86,9 +85,7 @@ function QuillForEditor ({
const bindings = { const bindings = {
tab: { tab: {
key: 9, key: 9,
handler: function () { handler: function () { }
console.log('调用了tab=====>>>>');
}
}, },
backspace: { backspace: {
key: 'Backspace', key: 'Backspace',
@ -104,11 +101,10 @@ function QuillForEditor ({
* index: 删除元素的位置 * index: 删除元素的位置
* length: 删除元素的个数 * length: 删除元素的个数
*/ */
const {index, length} = range; const { index, length } = range;
const _start = length === 0 ? index - 1 : index; const _start = length === 0 ? index - 1 : index;
const _length = length || 1; const _length = length || 1;
let delCtx = this.quill.getText(_start, _length); // 删除的元素 let delCtx = this.quill.getText(_start, _length); // 删除的元素
// aa
const reg = /▁/g; const reg = /▁/g;
const delArrs = delCtx.match(reg); const delArrs = delCtx.match(reg);
if (delArrs) { if (delArrs) {
@ -216,7 +212,7 @@ function QuillForEditor ({
const ops = value.ops || []; const ops = value.ops || [];
ops.forEach((item, i) => { ops.forEach((item, i) => {
if (item.insert['image']) { if (item.insert['image']) {
item.insert['image'] = Object.assign({}, item.insert['image'], {style: { cursor: 'pointer' }, onclick: (url) => showUploadImage(url)}); item.insert['image'] = Object.assign({}, item.insert['image'], { style: { cursor: 'pointer' }, onclick: (url) => showUploadImage(url) });
} }
}); });
} }
@ -225,7 +221,7 @@ function QuillForEditor ({
if (!deepEqual(previous, current)) { if (!deepEqual(previous, current)) {
setSelection(quill.getSelection()) setSelection(quill.getSelection())
if (typeof value === 'string' && value) { if (typeof value === 'string' && value) {
// debugger // debugger
quill.clipboard.dangerouslyPasteHTML(value, 'api'); quill.clipboard.dangerouslyPasteHTML(value, 'api');
if (autoFocus) { if (autoFocus) {
quill.focus(); quill.focus();
@ -273,9 +269,9 @@ function QuillForEditor ({
// 返回结果 // 返回结果
return ( return (
<div className='quill_editor_for_react_area' style={wrapStyle}> <div className='quill_editor_for_react_area' style={wrapStyle}>
<div ref={editorRef} style={style}></div> <div ref={editorRef} style={style}></div>
</div> </div>
); );
} }

@ -0,0 +1,21 @@
import Quill from "quill";
const Inline = Quill.import('blots/inline');
export default class LinkBlot extends Inline {
static create(value) {
let node = super.create()
let rs = value
if (rs.indexOf('http://') < 0) {
rs = 'http://' + rs
}
node.setAttribute('href', rs)
node.setAttribute('target', '_blank')
return node;
}
static formats(node) {
return node.getAttribute('href');
}
}
LinkBlot.blotName = 'link'
LinkBlot.tagName = 'a'

@ -91,7 +91,7 @@ export default ({ StatusEnquiry, allUpdatashixunlist, Updatasearchlist }) => {
<div className="clearfix mb20 shaiContent"> <div className="clearfix mb20 shaiContent">
<span className="shaiTitle fl">方向</span> <span className="shaiTitle fl">方向</span>
<div className="fl pr shaiAllItem"> <div className="fl pr shaiAllItem">
<a className={searchValue === "a" ? "shaiItem shixun_repertoire active" : "shaiItem shixun_repertoire"} value="a" onClick={onSearchAll}>全部</a> <li className={searchValue === "a" ? "shaiItem shixun_repertoire active" : "shaiItem shixun_repertoire"} value="a" onClick={onSearchAll}>全部</li>
{ {
navs.map((item, key) => { navs.map((item, key) => {
return ( return (

@ -17,7 +17,6 @@ const initialState = {
commitTestRecordDetail: {}, // 调试代码执行结果 commitTestRecordDetail: {}, // 调试代码执行结果
commitRecordDetail: {}, // 提交成功后记录提交的详情 commitRecordDetail: {}, // 提交成功后记录提交的详情
commitRecord: [], // 提交记录 commitRecord: [], // 提交记录
userCode: '', // 保存当前用户输入的代码
isUpdateCode: false, // 是否更新了代码内容 isUpdateCode: false, // 是否更新了代码内容
userCodeTab: 'task', // 学员测评tab位置: task | record | comment userCodeTab: 'task', // 学员测评tab位置: task | record | comment
userTestInput: '', // 用户自定义输入值 userTestInput: '', // 用户自定义输入值
@ -59,7 +58,7 @@ const ojForUserReducer = (state = initialState, action) => {
hack: Object.assign({}, hack), hack: Object.assign({}, hack),
test_case: Object.assign({}, test_case), test_case: Object.assign({}, test_case),
comment_identifier: hack.identifier, comment_identifier: hack.identifier,
userCode: tempCode editor_code: tempCode
} }
case types.COMMIT_RECORD_DETAIL: case types.COMMIT_RECORD_DETAIL:
let result = action.payload.data; let result = action.payload.data;
@ -127,7 +126,8 @@ const ojForUserReducer = (state = initialState, action) => {
} }
return { return {
...state, ...state,
recordDetail: tempDetail recordDetail: tempDetail,
editor_code: tempDetail['code']
} }
case types.RESTORE_INITIAL_CODE: case types.RESTORE_INITIAL_CODE:
const curHack = state.hack; const curHack = state.hack;

Loading…
Cancel
Save