forked from Gitlink/forgeplus
Merge branch 'dev_trustie' of http://gitea.trustie.net/jasder/forgeplus into dev_trustie
This commit is contained in:
commit
88d2f1d07f
|
@ -2,7 +2,8 @@ class RepositoriesController < ApplicationController
|
|||
include ApplicationHelper
|
||||
include OperateProjectAbilityAble
|
||||
before_action :require_login, only: %i[edit update create_file update_file delete_file sync_mirror]
|
||||
before_action :find_project, except: [:tags, :commit, :sync_mirror]
|
||||
before_action :find_project_with_includes, only: :show
|
||||
before_action :find_project, except: [:tags, :commit, :sync_mirror, :show]
|
||||
before_action :authorizate!, except: [:sync_mirror, :tags, :commit]
|
||||
before_action :find_repository_by_id, only: %i[commit sync_mirror tags]
|
||||
before_action :authorizate_user_can_edit_repo!, only: %i[sync_mirror]
|
||||
|
@ -10,6 +11,7 @@ class RepositoriesController < ApplicationController
|
|||
before_action :get_latest_commit, :get_ref, only: %i[entries sub_entries]
|
||||
|
||||
def show
|
||||
@user = current_user
|
||||
@branches_count = Gitea::Repository::Branches::ListService.new(@project.owner, @project.identifier).call&.size
|
||||
@commits_count = Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier,
|
||||
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call[:total_count]
|
||||
|
@ -27,8 +29,8 @@ class RepositoriesController < ApplicationController
|
|||
|
||||
def entries
|
||||
@project.increment!(:visits)
|
||||
|
||||
@entries = Gitea::Repository::Entries::ListService.new(@project.owner, @project.identifier, ref: @ref).call
|
||||
@project_owner = @project.owner
|
||||
@entries = Gitea::Repository::Entries::ListService.new(@project_owner, @project.identifier, ref: @ref).call
|
||||
@entries = @entries.sort_by{ |hash| hash['type'] }
|
||||
end
|
||||
|
||||
|
@ -124,6 +126,10 @@ class RepositoriesController < ApplicationController
|
|||
render_not_found("未找到相关的仓库") unless @project
|
||||
end
|
||||
|
||||
def find_project_with_includes
|
||||
@project = Project.includes(:repository, :owner, :watchers, :praise_treads).find params[:id]
|
||||
end
|
||||
|
||||
def authorizate!
|
||||
if @project.repository.hidden? && !@project.member?(current_user)
|
||||
render_forbidden
|
||||
|
|
|
@ -23,13 +23,6 @@ module ProjectsHelper
|
|||
Gitea.gitea_config[:domain]
|
||||
end
|
||||
|
||||
def render_edit_project_permission(user, project)
|
||||
permission = "Reporter"
|
||||
member = project.members.includes(:roles).find_by(user: user)
|
||||
|
||||
member&.roles&.last&.name || permission
|
||||
end
|
||||
|
||||
def find_user_by_login_or_mail(identifier)
|
||||
(User.find_by_login identifier) || (User.find_by_mail identifier)
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module RepositoriesHelper
|
||||
def render_decode64_content(str)
|
||||
return nil if str.blank?
|
||||
Base64.decode64(str)
|
||||
Base64.decode64(str).force_encoding('UTF-8')
|
||||
end
|
||||
|
||||
def download_type(str)
|
||||
|
|
|
@ -143,4 +143,19 @@ class Project < ApplicationRecord
|
|||
self.class.name.constantize.project_types["#{self.project_type}"]
|
||||
end
|
||||
|
||||
def watched_by? user
|
||||
watchers.pluck(:user_id).include? user&.id
|
||||
end
|
||||
|
||||
def praised_by? user
|
||||
praise_treads.pluck(:user_id).include? user&.id
|
||||
end
|
||||
|
||||
def get_premission user
|
||||
permission = "Reporter"
|
||||
member = members.find_by(user: user)
|
||||
|
||||
member&.roles&.last&.name || permission
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -18,11 +18,11 @@ class Repositories::CreateService < ApplicationService
|
|||
|
||||
chain_params = {
|
||||
type: "create",
|
||||
ownername: user.try(:login),
|
||||
ownername: user.try(:login),
|
||||
reponame: @repository.try(:id)
|
||||
}
|
||||
ProjectCreateChainJob.perform_later(chain_params) #创建上链操作
|
||||
|
||||
# ProjectCreateChainJob.perform_later(chain_params) #创建上链操作
|
||||
|
||||
end
|
||||
end
|
||||
@repository
|
||||
|
|
|
@ -14,7 +14,14 @@ json.entries do
|
|||
json.sha entry['sha']
|
||||
json.type entry['type']
|
||||
json.size entry['size']
|
||||
json.content entry['content']
|
||||
content =
|
||||
if entry['name'] === 'README.md'
|
||||
content = Gitea::Repository::Entries::GetService.call(@project_owner, @project.identifier, entry['name'], ref: @ref)['content']
|
||||
render_decode64_content content
|
||||
else
|
||||
entry['content']
|
||||
end
|
||||
json.content content
|
||||
json.target entry['target']
|
||||
if entry['latest_commit']
|
||||
json.partial! 'last_commit', entry: entry
|
||||
|
|
|
@ -9,19 +9,19 @@ json.praises_count @project.praises_count.to_i
|
|||
json.forked_count @project.forked_count.to_i
|
||||
json.watchers_count @project.watchers_count.to_i
|
||||
json.versions_count @project.versions_count #里程碑数量
|
||||
json.version_releases_count @project.releases_size(current_user.try(:id), "all")
|
||||
json.version_releasesed_count @project.releases_size(current_user.try(:id), "released") #已发行的版本
|
||||
json.version_releases_count @project.releases_size(@user.try(:id), "all")
|
||||
json.version_releasesed_count @project.releases_size(@user.try(:id), "released") #已发行的版本
|
||||
json.contributor_users_count @project.contributor_users
|
||||
json.issue_tags_count @tags_count
|
||||
json.branches_count @branches_count
|
||||
json.commits_count @commits_count
|
||||
json.permission render_edit_project_permission(current_user, @project) if current_user
|
||||
json.permission @project.get_premission(@user)
|
||||
json.mirror_url @project&.repository.mirror_url
|
||||
json.mirror @project&.repository.mirror_url.present?
|
||||
json.type @project.numerical_for_project_type
|
||||
json.mirror_status @project.repository&.mirror&.numerical_for_status if @project.sync_mirror?
|
||||
json.watched current_user&.watched?(@project)
|
||||
json.praised current_user&.liked?(@project)
|
||||
json.watched @project.watched_by? @user
|
||||
json.praised @project.praised_by? @user
|
||||
json.status @project.status
|
||||
json.forked_from_project_id @project_fork_id
|
||||
json.fork_info do
|
||||
|
|
|
@ -1,952 +0,0 @@
|
|||
/*!
|
||||
* Quill Editor v1.3.6
|
||||
* https://quilljs.com/
|
||||
* Copyright (c) 2014, Jason Chen
|
||||
* Copyright (c) 2013, salesforce.com
|
||||
*/
|
||||
.ql-container {
|
||||
box-sizing: border-box;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
height: 100%;
|
||||
margin: 0px;
|
||||
position: relative;
|
||||
}
|
||||
.ql-container.ql-disabled .ql-tooltip {
|
||||
visibility: hidden;
|
||||
}
|
||||
.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before {
|
||||
pointer-events: none;
|
||||
}
|
||||
.ql-clipboard {
|
||||
left: -100000px;
|
||||
height: 1px;
|
||||
overflow-y: hidden;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
}
|
||||
.ql-clipboard p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.ql-editor {
|
||||
box-sizing: border-box;
|
||||
line-height: 1.42;
|
||||
height: 100%;
|
||||
outline: none;
|
||||
overflow-y: auto;
|
||||
padding: 12px 15px;
|
||||
tab-size: 4;
|
||||
-moz-tab-size: 4;
|
||||
text-align: left;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.ql-editor > * {
|
||||
cursor: text;
|
||||
}
|
||||
.ql-editor p,
|
||||
.ql-editor ol,
|
||||
.ql-editor ul,
|
||||
.ql-editor pre,
|
||||
.ql-editor blockquote,
|
||||
.ql-editor h1,
|
||||
.ql-editor h2,
|
||||
.ql-editor h3,
|
||||
.ql-editor h4,
|
||||
.ql-editor h5,
|
||||
.ql-editor h6 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol,
|
||||
.ql-editor ul {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
.ql-editor ol > li,
|
||||
.ql-editor ul > li {
|
||||
list-style-type: none;
|
||||
}
|
||||
.ql-editor ul > li::before {
|
||||
content: '\2022';
|
||||
}
|
||||
.ql-editor ul[data-checked=true],
|
||||
.ql-editor ul[data-checked=false] {
|
||||
pointer-events: none;
|
||||
}
|
||||
.ql-editor ul[data-checked=true] > li *,
|
||||
.ql-editor ul[data-checked=false] > li * {
|
||||
pointer-events: all;
|
||||
}
|
||||
.ql-editor ul[data-checked=true] > li::before,
|
||||
.ql-editor ul[data-checked=false] > li::before {
|
||||
color: #777;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
}
|
||||
.ql-editor ul[data-checked=true] > li::before {
|
||||
content: '\2611';
|
||||
}
|
||||
.ql-editor ul[data-checked=false] > li::before {
|
||||
content: '\2610';
|
||||
}
|
||||
.ql-editor li::before {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
width: 1.2em;
|
||||
}
|
||||
.ql-editor li:not(.ql-direction-rtl)::before {
|
||||
margin-left: -1.5em;
|
||||
margin-right: 0.3em;
|
||||
text-align: right;
|
||||
}
|
||||
.ql-editor li.ql-direction-rtl::before {
|
||||
margin-left: 0.3em;
|
||||
margin-right: -1.5em;
|
||||
}
|
||||
.ql-editor ol li:not(.ql-direction-rtl),
|
||||
.ql-editor ul li:not(.ql-direction-rtl) {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
.ql-editor ol li.ql-direction-rtl,
|
||||
.ql-editor ul li.ql-direction-rtl {
|
||||
padding-right: 1.5em;
|
||||
}
|
||||
.ql-editor ol li {
|
||||
counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
counter-increment: list-0;
|
||||
}
|
||||
.ql-editor ol li:before {
|
||||
content: counter(list-0, decimal) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-1 {
|
||||
counter-increment: list-1;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-1:before {
|
||||
content: counter(list-1, lower-alpha) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-1 {
|
||||
counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-2 {
|
||||
counter-increment: list-2;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-2:before {
|
||||
content: counter(list-2, lower-roman) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-2 {
|
||||
counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-3 {
|
||||
counter-increment: list-3;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-3:before {
|
||||
content: counter(list-3, decimal) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-3 {
|
||||
counter-reset: list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-4 {
|
||||
counter-increment: list-4;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-4:before {
|
||||
content: counter(list-4, lower-alpha) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-4 {
|
||||
counter-reset: list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-5 {
|
||||
counter-increment: list-5;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-5:before {
|
||||
content: counter(list-5, lower-roman) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-5 {
|
||||
counter-reset: list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-6 {
|
||||
counter-increment: list-6;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-6:before {
|
||||
content: counter(list-6, decimal) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-6 {
|
||||
counter-reset: list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-7 {
|
||||
counter-increment: list-7;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-7:before {
|
||||
content: counter(list-7, lower-alpha) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-7 {
|
||||
counter-reset: list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-8 {
|
||||
counter-increment: list-8;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-8:before {
|
||||
content: counter(list-8, lower-roman) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-8 {
|
||||
counter-reset: list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-9 {
|
||||
counter-increment: list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-9:before {
|
||||
content: counter(list-9, decimal) '. ';
|
||||
}
|
||||
.ql-editor .ql-indent-1:not(.ql-direction-rtl) {
|
||||
padding-left: 3em;
|
||||
}
|
||||
.ql-editor li.ql-indent-1:not(.ql-direction-rtl) {
|
||||
padding-left: 4.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 3em;
|
||||
}
|
||||
.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 4.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-2:not(.ql-direction-rtl) {
|
||||
padding-left: 6em;
|
||||
}
|
||||
.ql-editor li.ql-indent-2:not(.ql-direction-rtl) {
|
||||
padding-left: 7.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 6em;
|
||||
}
|
||||
.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 7.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-3:not(.ql-direction-rtl) {
|
||||
padding-left: 9em;
|
||||
}
|
||||
.ql-editor li.ql-indent-3:not(.ql-direction-rtl) {
|
||||
padding-left: 10.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 9em;
|
||||
}
|
||||
.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 10.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-4:not(.ql-direction-rtl) {
|
||||
padding-left: 12em;
|
||||
}
|
||||
.ql-editor li.ql-indent-4:not(.ql-direction-rtl) {
|
||||
padding-left: 13.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 12em;
|
||||
}
|
||||
.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 13.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-5:not(.ql-direction-rtl) {
|
||||
padding-left: 15em;
|
||||
}
|
||||
.ql-editor li.ql-indent-5:not(.ql-direction-rtl) {
|
||||
padding-left: 16.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 15em;
|
||||
}
|
||||
.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 16.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-6:not(.ql-direction-rtl) {
|
||||
padding-left: 18em;
|
||||
}
|
||||
.ql-editor li.ql-indent-6:not(.ql-direction-rtl) {
|
||||
padding-left: 19.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 18em;
|
||||
}
|
||||
.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 19.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-7:not(.ql-direction-rtl) {
|
||||
padding-left: 21em;
|
||||
}
|
||||
.ql-editor li.ql-indent-7:not(.ql-direction-rtl) {
|
||||
padding-left: 22.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 21em;
|
||||
}
|
||||
.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 22.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-8:not(.ql-direction-rtl) {
|
||||
padding-left: 24em;
|
||||
}
|
||||
.ql-editor li.ql-indent-8:not(.ql-direction-rtl) {
|
||||
padding-left: 25.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 24em;
|
||||
}
|
||||
.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 25.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-9:not(.ql-direction-rtl) {
|
||||
padding-left: 27em;
|
||||
}
|
||||
.ql-editor li.ql-indent-9:not(.ql-direction-rtl) {
|
||||
padding-left: 28.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 27em;
|
||||
}
|
||||
.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 28.5em;
|
||||
}
|
||||
.ql-editor .ql-video {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
.ql-editor .ql-video.ql-align-center {
|
||||
margin: 0 auto;
|
||||
}
|
||||
.ql-editor .ql-video.ql-align-right {
|
||||
margin: 0 0 0 auto;
|
||||
}
|
||||
.ql-editor .ql-bg-black {
|
||||
background-color: #000;
|
||||
}
|
||||
.ql-editor .ql-bg-red {
|
||||
background-color: #e60000;
|
||||
}
|
||||
.ql-editor .ql-bg-orange {
|
||||
background-color: #f90;
|
||||
}
|
||||
.ql-editor .ql-bg-yellow {
|
||||
background-color: #ff0;
|
||||
}
|
||||
.ql-editor .ql-bg-green {
|
||||
background-color: #008a00;
|
||||
}
|
||||
.ql-editor .ql-bg-blue {
|
||||
background-color: #06c;
|
||||
}
|
||||
.ql-editor .ql-bg-purple {
|
||||
background-color: #93f;
|
||||
}
|
||||
.ql-editor .ql-color-white {
|
||||
color: #fff;
|
||||
}
|
||||
.ql-editor .ql-color-red {
|
||||
color: #e60000;
|
||||
}
|
||||
.ql-editor .ql-color-orange {
|
||||
color: #f90;
|
||||
}
|
||||
.ql-editor .ql-color-yellow {
|
||||
color: #ff0;
|
||||
}
|
||||
.ql-editor .ql-color-green {
|
||||
color: #008a00;
|
||||
}
|
||||
.ql-editor .ql-color-blue {
|
||||
color: #06c;
|
||||
}
|
||||
.ql-editor .ql-color-purple {
|
||||
color: #93f;
|
||||
}
|
||||
.ql-editor .ql-font-serif {
|
||||
font-family: Georgia, Times New Roman, serif;
|
||||
}
|
||||
.ql-editor .ql-font-monospace {
|
||||
font-family: Monaco, Courier New, monospace;
|
||||
}
|
||||
.ql-editor .ql-size-small {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
.ql-editor .ql-size-large {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.ql-editor .ql-size-huge {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
.ql-editor .ql-direction-rtl {
|
||||
direction: rtl;
|
||||
text-align: inherit;
|
||||
}
|
||||
.ql-editor .ql-align-center {
|
||||
text-align: center;
|
||||
}
|
||||
.ql-editor .ql-align-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
.ql-editor .ql-align-right {
|
||||
text-align: right;
|
||||
}
|
||||
.ql-editor.ql-blank::before {
|
||||
color: rgba(0,0,0,0.6);
|
||||
content: attr(data-placeholder);
|
||||
font-style: italic;
|
||||
left: 15px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
}
|
||||
.ql-bubble.ql-toolbar:after,
|
||||
.ql-bubble .ql-toolbar:after {
|
||||
clear: both;
|
||||
content: '';
|
||||
display: table;
|
||||
}
|
||||
.ql-bubble.ql-toolbar button,
|
||||
.ql-bubble .ql-toolbar button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 24px;
|
||||
padding: 3px 5px;
|
||||
width: 28px;
|
||||
}
|
||||
.ql-bubble.ql-toolbar button svg,
|
||||
.ql-bubble .ql-toolbar button svg {
|
||||
float: left;
|
||||
height: 100%;
|
||||
}
|
||||
.ql-bubble.ql-toolbar button:active:hover,
|
||||
.ql-bubble .ql-toolbar button:active:hover {
|
||||
outline: none;
|
||||
}
|
||||
.ql-bubble.ql-toolbar input.ql-image[type=file],
|
||||
.ql-bubble .ql-toolbar input.ql-image[type=file] {
|
||||
display: none;
|
||||
}
|
||||
.ql-bubble.ql-toolbar button:hover,
|
||||
.ql-bubble .ql-toolbar button:hover,
|
||||
.ql-bubble.ql-toolbar button:focus,
|
||||
.ql-bubble .ql-toolbar button:focus,
|
||||
.ql-bubble.ql-toolbar button.ql-active,
|
||||
.ql-bubble .ql-toolbar button.ql-active,
|
||||
.ql-bubble.ql-toolbar .ql-picker-label:hover,
|
||||
.ql-bubble .ql-toolbar .ql-picker-label:hover,
|
||||
.ql-bubble.ql-toolbar .ql-picker-label.ql-active,
|
||||
.ql-bubble .ql-toolbar .ql-picker-label.ql-active,
|
||||
.ql-bubble.ql-toolbar .ql-picker-item:hover,
|
||||
.ql-bubble .ql-toolbar .ql-picker-item:hover,
|
||||
.ql-bubble.ql-toolbar .ql-picker-item.ql-selected,
|
||||
.ql-bubble .ql-toolbar .ql-picker-item.ql-selected {
|
||||
color: #fff;
|
||||
}
|
||||
.ql-bubble.ql-toolbar button:hover .ql-fill,
|
||||
.ql-bubble .ql-toolbar button:hover .ql-fill,
|
||||
.ql-bubble.ql-toolbar button:focus .ql-fill,
|
||||
.ql-bubble .ql-toolbar button:focus .ql-fill,
|
||||
.ql-bubble.ql-toolbar button.ql-active .ql-fill,
|
||||
.ql-bubble .ql-toolbar button.ql-active .ql-fill,
|
||||
.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-fill,
|
||||
.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-fill,
|
||||
.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-fill,
|
||||
.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-fill,
|
||||
.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-fill,
|
||||
.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-fill,
|
||||
.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-fill,
|
||||
.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-fill,
|
||||
.ql-bubble.ql-toolbar button:hover .ql-stroke.ql-fill,
|
||||
.ql-bubble .ql-toolbar button:hover .ql-stroke.ql-fill,
|
||||
.ql-bubble.ql-toolbar button:focus .ql-stroke.ql-fill,
|
||||
.ql-bubble .ql-toolbar button:focus .ql-stroke.ql-fill,
|
||||
.ql-bubble.ql-toolbar button.ql-active .ql-stroke.ql-fill,
|
||||
.ql-bubble .ql-toolbar button.ql-active .ql-stroke.ql-fill,
|
||||
.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,
|
||||
.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,
|
||||
.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,
|
||||
.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,
|
||||
.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,
|
||||
.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,
|
||||
.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill,
|
||||
.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill {
|
||||
fill: #fff;
|
||||
}
|
||||
.ql-bubble.ql-toolbar button:hover .ql-stroke,
|
||||
.ql-bubble .ql-toolbar button:hover .ql-stroke,
|
||||
.ql-bubble.ql-toolbar button:focus .ql-stroke,
|
||||
.ql-bubble .ql-toolbar button:focus .ql-stroke,
|
||||
.ql-bubble.ql-toolbar button.ql-active .ql-stroke,
|
||||
.ql-bubble .ql-toolbar button.ql-active .ql-stroke,
|
||||
.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-stroke,
|
||||
.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-stroke,
|
||||
.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-stroke,
|
||||
.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-stroke,
|
||||
.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-stroke,
|
||||
.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-stroke,
|
||||
.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-stroke,
|
||||
.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-stroke,
|
||||
.ql-bubble.ql-toolbar button:hover .ql-stroke-miter,
|
||||
.ql-bubble .ql-toolbar button:hover .ql-stroke-miter,
|
||||
.ql-bubble.ql-toolbar button:focus .ql-stroke-miter,
|
||||
.ql-bubble .ql-toolbar button:focus .ql-stroke-miter,
|
||||
.ql-bubble.ql-toolbar button.ql-active .ql-stroke-miter,
|
||||
.ql-bubble .ql-toolbar button.ql-active .ql-stroke-miter,
|
||||
.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-stroke-miter,
|
||||
.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-stroke-miter,
|
||||
.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,
|
||||
.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,
|
||||
.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-stroke-miter,
|
||||
.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-stroke-miter,
|
||||
.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter,
|
||||
.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter {
|
||||
stroke: #fff;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
.ql-bubble.ql-toolbar button:hover:not(.ql-active),
|
||||
.ql-bubble .ql-toolbar button:hover:not(.ql-active) {
|
||||
color: #ccc;
|
||||
}
|
||||
.ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-fill,
|
||||
.ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-fill,
|
||||
.ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill,
|
||||
.ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill {
|
||||
fill: #ccc;
|
||||
}
|
||||
.ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-stroke,
|
||||
.ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-stroke,
|
||||
.ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter,
|
||||
.ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter {
|
||||
stroke: #ccc;
|
||||
}
|
||||
}
|
||||
.ql-bubble {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.ql-bubble * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.ql-bubble .ql-hidden {
|
||||
display: none;
|
||||
}
|
||||
.ql-bubble .ql-out-bottom,
|
||||
.ql-bubble .ql-out-top {
|
||||
visibility: hidden;
|
||||
}
|
||||
.ql-bubble .ql-tooltip {
|
||||
position: absolute;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
.ql-bubble .ql-tooltip a {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ql-bubble .ql-tooltip.ql-flip {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
.ql-bubble .ql-formats {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.ql-bubble .ql-formats:after {
|
||||
clear: both;
|
||||
content: '';
|
||||
display: table;
|
||||
}
|
||||
.ql-bubble .ql-stroke {
|
||||
fill: none;
|
||||
stroke: #ccc;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 2;
|
||||
}
|
||||
.ql-bubble .ql-stroke-miter {
|
||||
fill: none;
|
||||
stroke: #ccc;
|
||||
stroke-miterlimit: 10;
|
||||
stroke-width: 2;
|
||||
}
|
||||
.ql-bubble .ql-fill,
|
||||
.ql-bubble .ql-stroke.ql-fill {
|
||||
fill: #ccc;
|
||||
}
|
||||
.ql-bubble .ql-empty {
|
||||
fill: none;
|
||||
}
|
||||
.ql-bubble .ql-even {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
.ql-bubble .ql-thin,
|
||||
.ql-bubble .ql-stroke.ql-thin {
|
||||
stroke-width: 1;
|
||||
}
|
||||
.ql-bubble .ql-transparent {
|
||||
opacity: 0.4;
|
||||
}
|
||||
.ql-bubble .ql-direction svg:last-child {
|
||||
display: none;
|
||||
}
|
||||
.ql-bubble .ql-direction.ql-active svg:last-child {
|
||||
display: inline;
|
||||
}
|
||||
.ql-bubble .ql-direction.ql-active svg:first-child {
|
||||
display: none;
|
||||
}
|
||||
.ql-bubble .ql-editor h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
.ql-bubble .ql-editor h2 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.ql-bubble .ql-editor h3 {
|
||||
font-size: 1.17em;
|
||||
}
|
||||
.ql-bubble .ql-editor h4 {
|
||||
font-size: 1em;
|
||||
}
|
||||
.ql-bubble .ql-editor h5 {
|
||||
font-size: 0.83em;
|
||||
}
|
||||
.ql-bubble .ql-editor h6 {
|
||||
font-size: 0.67em;
|
||||
}
|
||||
.ql-bubble .ql-editor a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.ql-bubble .ql-editor blockquote {
|
||||
border-left: 4px solid #ccc;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 5px;
|
||||
padding-left: 16px;
|
||||
}
|
||||
.ql-bubble .ql-editor code,
|
||||
.ql-bubble .ql-editor pre {
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.ql-bubble .ql-editor pre {
|
||||
white-space: pre-wrap;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 5px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.ql-bubble .ql-editor code {
|
||||
font-size: 85%;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.ql-bubble .ql-editor pre.ql-syntax {
|
||||
background-color: #23241f;
|
||||
color: #f8f8f2;
|
||||
overflow: visible;
|
||||
}
|
||||
.ql-bubble .ql-editor img {
|
||||
max-width: 100%;
|
||||
}
|
||||
.ql-bubble .ql-picker {
|
||||
color: #ccc;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
height: 24px;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.ql-bubble .ql-picker-label {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
padding-left: 8px;
|
||||
padding-right: 2px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.ql-bubble .ql-picker-label::before {
|
||||
display: inline-block;
|
||||
line-height: 22px;
|
||||
}
|
||||
.ql-bubble .ql-picker-options {
|
||||
background-color: #444;
|
||||
display: none;
|
||||
min-width: 100%;
|
||||
padding: 4px 8px;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ql-bubble .ql-picker-options .ql-picker-item {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
padding-bottom: 5px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-expanded .ql-picker-label {
|
||||
color: #777;
|
||||
z-index: 2;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-expanded .ql-picker-label .ql-fill {
|
||||
fill: #777;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-expanded .ql-picker-label .ql-stroke {
|
||||
stroke: #777;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-expanded .ql-picker-options {
|
||||
display: block;
|
||||
margin-top: -1px;
|
||||
top: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
.ql-bubble .ql-color-picker,
|
||||
.ql-bubble .ql-icon-picker {
|
||||
width: 28px;
|
||||
}
|
||||
.ql-bubble .ql-color-picker .ql-picker-label,
|
||||
.ql-bubble .ql-icon-picker .ql-picker-label {
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.ql-bubble .ql-color-picker .ql-picker-label svg,
|
||||
.ql-bubble .ql-icon-picker .ql-picker-label svg {
|
||||
right: 4px;
|
||||
}
|
||||
.ql-bubble .ql-icon-picker .ql-picker-options {
|
||||
padding: 4px 0px;
|
||||
}
|
||||
.ql-bubble .ql-icon-picker .ql-picker-item {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.ql-bubble .ql-color-picker .ql-picker-options {
|
||||
padding: 3px 5px;
|
||||
width: 152px;
|
||||
}
|
||||
.ql-bubble .ql-color-picker .ql-picker-item {
|
||||
border: 1px solid transparent;
|
||||
float: left;
|
||||
height: 16px;
|
||||
margin: 2px;
|
||||
padding: 0px;
|
||||
width: 16px;
|
||||
}
|
||||
.ql-bubble .ql-picker:not(.ql-color-picker):not(.ql-icon-picker) svg {
|
||||
position: absolute;
|
||||
margin-top: -9px;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
width: 18px;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-label[data-label]:not([data-label=''])::before,
|
||||
.ql-bubble .ql-picker.ql-font .ql-picker-label[data-label]:not([data-label=''])::before,
|
||||
.ql-bubble .ql-picker.ql-size .ql-picker-label[data-label]:not([data-label=''])::before,
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-item[data-label]:not([data-label=''])::before,
|
||||
.ql-bubble .ql-picker.ql-font .ql-picker-item[data-label]:not([data-label=''])::before,
|
||||
.ql-bubble .ql-picker.ql-size .ql-picker-item[data-label]:not([data-label=''])::before {
|
||||
content: attr(data-label);
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-header {
|
||||
width: 98px;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-label::before,
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-item::before {
|
||||
content: 'Normal';
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
|
||||
content: 'Heading 1';
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
|
||||
content: 'Heading 2';
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
|
||||
content: 'Heading 3';
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
|
||||
content: 'Heading 4';
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
|
||||
content: 'Heading 5';
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
|
||||
content: 'Heading 6';
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
|
||||
font-size: 2em;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
|
||||
font-size: 1.17em;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
|
||||
font-size: 1em;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
|
||||
font-size: 0.83em;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
|
||||
font-size: 0.67em;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-font {
|
||||
width: 108px;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-font .ql-picker-label::before,
|
||||
.ql-bubble .ql-picker.ql-font .ql-picker-item::before {
|
||||
content: 'Sans Serif';
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-font .ql-picker-label[data-value=serif]::before,
|
||||
.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
|
||||
content: 'Serif';
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before,
|
||||
.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
|
||||
content: 'Monospace';
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
|
||||
font-family: Georgia, Times New Roman, serif;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
|
||||
font-family: Monaco, Courier New, monospace;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-size {
|
||||
width: 98px;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-size .ql-picker-label::before,
|
||||
.ql-bubble .ql-picker.ql-size .ql-picker-item::before {
|
||||
content: 'Normal';
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-size .ql-picker-label[data-value=small]::before,
|
||||
.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
|
||||
content: 'Small';
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-size .ql-picker-label[data-value=large]::before,
|
||||
.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
|
||||
content: 'Large';
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-size .ql-picker-label[data-value=huge]::before,
|
||||
.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
|
||||
content: 'Huge';
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
|
||||
font-size: 10px;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
|
||||
font-size: 18px;
|
||||
}
|
||||
.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
|
||||
font-size: 32px;
|
||||
}
|
||||
.ql-bubble .ql-color-picker.ql-background .ql-picker-item {
|
||||
background-color: #fff;
|
||||
}
|
||||
.ql-bubble .ql-color-picker.ql-color .ql-picker-item {
|
||||
background-color: #000;
|
||||
}
|
||||
.ql-bubble .ql-toolbar .ql-formats {
|
||||
margin: 8px 12px 8px 0px;
|
||||
}
|
||||
.ql-bubble .ql-toolbar .ql-formats:first-child {
|
||||
margin-left: 12px;
|
||||
}
|
||||
.ql-bubble .ql-color-picker svg {
|
||||
margin: 1px;
|
||||
}
|
||||
.ql-bubble .ql-color-picker .ql-picker-item.ql-selected,
|
||||
.ql-bubble .ql-color-picker .ql-picker-item:hover {
|
||||
border-color: #fff;
|
||||
}
|
||||
.ql-bubble .ql-tooltip {
|
||||
background-color: #444;
|
||||
border-radius: 25px;
|
||||
color: #fff;
|
||||
}
|
||||
.ql-bubble .ql-tooltip-arrow {
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
content: " ";
|
||||
display: block;
|
||||
left: 50%;
|
||||
margin-left: -6px;
|
||||
position: absolute;
|
||||
}
|
||||
.ql-bubble .ql-tooltip:not(.ql-flip) .ql-tooltip-arrow {
|
||||
border-bottom: 6px solid #444;
|
||||
top: -6px;
|
||||
}
|
||||
.ql-bubble .ql-tooltip.ql-flip .ql-tooltip-arrow {
|
||||
border-top: 6px solid #444;
|
||||
bottom: -6px;
|
||||
}
|
||||
.ql-bubble .ql-tooltip.ql-editing .ql-tooltip-editor {
|
||||
display: block;
|
||||
}
|
||||
.ql-bubble .ql-tooltip.ql-editing .ql-formats {
|
||||
visibility: hidden;
|
||||
}
|
||||
.ql-bubble .ql-tooltip-editor {
|
||||
display: none;
|
||||
}
|
||||
.ql-bubble .ql-tooltip-editor input[type=text] {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
height: 100%;
|
||||
outline: none;
|
||||
padding: 10px 20px;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
.ql-bubble .ql-tooltip-editor a {
|
||||
top: 10px;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
}
|
||||
.ql-bubble .ql-tooltip-editor a:before {
|
||||
color: #ccc;
|
||||
content: "\D7";
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.ql-container.ql-bubble:not(.ql-disabled) a {
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ql-container.ql-bubble:not(.ql-disabled) a::before {
|
||||
background-color: #444;
|
||||
border-radius: 15px;
|
||||
top: -5px;
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
content: attr(href);
|
||||
font-weight: normal;
|
||||
overflow: hidden;
|
||||
padding: 5px 15px;
|
||||
text-decoration: none;
|
||||
z-index: 1;
|
||||
}
|
||||
.ql-container.ql-bubble:not(.ql-disabled) a::after {
|
||||
border-top: 6px solid #444;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
top: 0;
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
.ql-container.ql-bubble:not(.ql-disabled) a::before,
|
||||
.ql-container.ql-bubble:not(.ql-disabled) a::after {
|
||||
left: 0;
|
||||
margin-left: 50%;
|
||||
position: absolute;
|
||||
transform: translate(-50%, -100%);
|
||||
transition: visibility 0s ease 200ms;
|
||||
visibility: hidden;
|
||||
}
|
||||
.ql-container.ql-bubble:not(.ql-disabled) a:hover::before,
|
||||
.ql-container.ql-bubble:not(.ql-disabled) a:hover::after {
|
||||
visibility: visible;
|
||||
}
|
11489
public/quill/quill.js
11489
public/quill/quill.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -1,945 +0,0 @@
|
|||
/*!
|
||||
* Quill Editor v1.3.6
|
||||
* https://quilljs.com/
|
||||
* Copyright (c) 2014, Jason Chen
|
||||
* Copyright (c) 2013, salesforce.com
|
||||
*/
|
||||
.ql-container {
|
||||
box-sizing: border-box;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
height: 100%;
|
||||
margin: 0px;
|
||||
position: relative;
|
||||
}
|
||||
.ql-container.ql-disabled .ql-tooltip {
|
||||
visibility: hidden;
|
||||
}
|
||||
.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before {
|
||||
pointer-events: none;
|
||||
}
|
||||
.ql-clipboard {
|
||||
left: -100000px;
|
||||
height: 1px;
|
||||
overflow-y: hidden;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
}
|
||||
.ql-clipboard p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.ql-editor {
|
||||
box-sizing: border-box;
|
||||
line-height: 1.42;
|
||||
height: 100%;
|
||||
outline: none;
|
||||
overflow-y: auto;
|
||||
padding: 12px 15px;
|
||||
tab-size: 4;
|
||||
-moz-tab-size: 4;
|
||||
text-align: left;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.ql-editor > * {
|
||||
cursor: text;
|
||||
}
|
||||
.ql-editor p,
|
||||
.ql-editor ol,
|
||||
.ql-editor ul,
|
||||
.ql-editor pre,
|
||||
.ql-editor blockquote,
|
||||
.ql-editor h1,
|
||||
.ql-editor h2,
|
||||
.ql-editor h3,
|
||||
.ql-editor h4,
|
||||
.ql-editor h5,
|
||||
.ql-editor h6 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol,
|
||||
.ql-editor ul {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
.ql-editor ol > li,
|
||||
.ql-editor ul > li {
|
||||
list-style-type: none;
|
||||
}
|
||||
.ql-editor ul > li::before {
|
||||
content: '\2022';
|
||||
}
|
||||
.ql-editor ul[data-checked=true],
|
||||
.ql-editor ul[data-checked=false] {
|
||||
pointer-events: none;
|
||||
}
|
||||
.ql-editor ul[data-checked=true] > li *,
|
||||
.ql-editor ul[data-checked=false] > li * {
|
||||
pointer-events: all;
|
||||
}
|
||||
.ql-editor ul[data-checked=true] > li::before,
|
||||
.ql-editor ul[data-checked=false] > li::before {
|
||||
color: #777;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
}
|
||||
.ql-editor ul[data-checked=true] > li::before {
|
||||
content: '\2611';
|
||||
}
|
||||
.ql-editor ul[data-checked=false] > li::before {
|
||||
content: '\2610';
|
||||
}
|
||||
.ql-editor li::before {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
width: 1.2em;
|
||||
}
|
||||
.ql-editor li:not(.ql-direction-rtl)::before {
|
||||
margin-left: -1.5em;
|
||||
margin-right: 0.3em;
|
||||
text-align: right;
|
||||
}
|
||||
.ql-editor li.ql-direction-rtl::before {
|
||||
margin-left: 0.3em;
|
||||
margin-right: -1.5em;
|
||||
}
|
||||
.ql-editor ol li:not(.ql-direction-rtl),
|
||||
.ql-editor ul li:not(.ql-direction-rtl) {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
.ql-editor ol li.ql-direction-rtl,
|
||||
.ql-editor ul li.ql-direction-rtl {
|
||||
padding-right: 1.5em;
|
||||
}
|
||||
.ql-editor ol li {
|
||||
counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
counter-increment: list-0;
|
||||
}
|
||||
.ql-editor ol li:before {
|
||||
content: counter(list-0, decimal) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-1 {
|
||||
counter-increment: list-1;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-1:before {
|
||||
content: counter(list-1, lower-alpha) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-1 {
|
||||
counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-2 {
|
||||
counter-increment: list-2;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-2:before {
|
||||
content: counter(list-2, lower-roman) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-2 {
|
||||
counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-3 {
|
||||
counter-increment: list-3;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-3:before {
|
||||
content: counter(list-3, decimal) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-3 {
|
||||
counter-reset: list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-4 {
|
||||
counter-increment: list-4;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-4:before {
|
||||
content: counter(list-4, lower-alpha) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-4 {
|
||||
counter-reset: list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-5 {
|
||||
counter-increment: list-5;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-5:before {
|
||||
content: counter(list-5, lower-roman) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-5 {
|
||||
counter-reset: list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-6 {
|
||||
counter-increment: list-6;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-6:before {
|
||||
content: counter(list-6, decimal) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-6 {
|
||||
counter-reset: list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-7 {
|
||||
counter-increment: list-7;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-7:before {
|
||||
content: counter(list-7, lower-alpha) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-7 {
|
||||
counter-reset: list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-8 {
|
||||
counter-increment: list-8;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-8:before {
|
||||
content: counter(list-8, lower-roman) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-8 {
|
||||
counter-reset: list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-9 {
|
||||
counter-increment: list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-9:before {
|
||||
content: counter(list-9, decimal) '. ';
|
||||
}
|
||||
.ql-editor .ql-indent-1:not(.ql-direction-rtl) {
|
||||
padding-left: 3em;
|
||||
}
|
||||
.ql-editor li.ql-indent-1:not(.ql-direction-rtl) {
|
||||
padding-left: 4.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 3em;
|
||||
}
|
||||
.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 4.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-2:not(.ql-direction-rtl) {
|
||||
padding-left: 6em;
|
||||
}
|
||||
.ql-editor li.ql-indent-2:not(.ql-direction-rtl) {
|
||||
padding-left: 7.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 6em;
|
||||
}
|
||||
.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 7.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-3:not(.ql-direction-rtl) {
|
||||
padding-left: 9em;
|
||||
}
|
||||
.ql-editor li.ql-indent-3:not(.ql-direction-rtl) {
|
||||
padding-left: 10.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 9em;
|
||||
}
|
||||
.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 10.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-4:not(.ql-direction-rtl) {
|
||||
padding-left: 12em;
|
||||
}
|
||||
.ql-editor li.ql-indent-4:not(.ql-direction-rtl) {
|
||||
padding-left: 13.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 12em;
|
||||
}
|
||||
.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 13.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-5:not(.ql-direction-rtl) {
|
||||
padding-left: 15em;
|
||||
}
|
||||
.ql-editor li.ql-indent-5:not(.ql-direction-rtl) {
|
||||
padding-left: 16.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 15em;
|
||||
}
|
||||
.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 16.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-6:not(.ql-direction-rtl) {
|
||||
padding-left: 18em;
|
||||
}
|
||||
.ql-editor li.ql-indent-6:not(.ql-direction-rtl) {
|
||||
padding-left: 19.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 18em;
|
||||
}
|
||||
.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 19.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-7:not(.ql-direction-rtl) {
|
||||
padding-left: 21em;
|
||||
}
|
||||
.ql-editor li.ql-indent-7:not(.ql-direction-rtl) {
|
||||
padding-left: 22.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 21em;
|
||||
}
|
||||
.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 22.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-8:not(.ql-direction-rtl) {
|
||||
padding-left: 24em;
|
||||
}
|
||||
.ql-editor li.ql-indent-8:not(.ql-direction-rtl) {
|
||||
padding-left: 25.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 24em;
|
||||
}
|
||||
.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 25.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-9:not(.ql-direction-rtl) {
|
||||
padding-left: 27em;
|
||||
}
|
||||
.ql-editor li.ql-indent-9:not(.ql-direction-rtl) {
|
||||
padding-left: 28.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 27em;
|
||||
}
|
||||
.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 28.5em;
|
||||
}
|
||||
.ql-editor .ql-video {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
.ql-editor .ql-video.ql-align-center {
|
||||
margin: 0 auto;
|
||||
}
|
||||
.ql-editor .ql-video.ql-align-right {
|
||||
margin: 0 0 0 auto;
|
||||
}
|
||||
.ql-editor .ql-bg-black {
|
||||
background-color: #000;
|
||||
}
|
||||
.ql-editor .ql-bg-red {
|
||||
background-color: #e60000;
|
||||
}
|
||||
.ql-editor .ql-bg-orange {
|
||||
background-color: #f90;
|
||||
}
|
||||
.ql-editor .ql-bg-yellow {
|
||||
background-color: #ff0;
|
||||
}
|
||||
.ql-editor .ql-bg-green {
|
||||
background-color: #008a00;
|
||||
}
|
||||
.ql-editor .ql-bg-blue {
|
||||
background-color: #06c;
|
||||
}
|
||||
.ql-editor .ql-bg-purple {
|
||||
background-color: #93f;
|
||||
}
|
||||
.ql-editor .ql-color-white {
|
||||
color: #fff;
|
||||
}
|
||||
.ql-editor .ql-color-red {
|
||||
color: #e60000;
|
||||
}
|
||||
.ql-editor .ql-color-orange {
|
||||
color: #f90;
|
||||
}
|
||||
.ql-editor .ql-color-yellow {
|
||||
color: #ff0;
|
||||
}
|
||||
.ql-editor .ql-color-green {
|
||||
color: #008a00;
|
||||
}
|
||||
.ql-editor .ql-color-blue {
|
||||
color: #06c;
|
||||
}
|
||||
.ql-editor .ql-color-purple {
|
||||
color: #93f;
|
||||
}
|
||||
.ql-editor .ql-font-serif {
|
||||
font-family: Georgia, Times New Roman, serif;
|
||||
}
|
||||
.ql-editor .ql-font-monospace {
|
||||
font-family: Monaco, Courier New, monospace;
|
||||
}
|
||||
.ql-editor .ql-size-small {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
.ql-editor .ql-size-large {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.ql-editor .ql-size-huge {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
.ql-editor .ql-direction-rtl {
|
||||
direction: rtl;
|
||||
text-align: inherit;
|
||||
}
|
||||
.ql-editor .ql-align-center {
|
||||
text-align: center;
|
||||
}
|
||||
.ql-editor .ql-align-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
.ql-editor .ql-align-right {
|
||||
text-align: right;
|
||||
}
|
||||
.ql-editor.ql-blank::before {
|
||||
color: rgba(0,0,0,0.6);
|
||||
content: attr(data-placeholder);
|
||||
font-style: italic;
|
||||
left: 15px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
}
|
||||
.ql-snow.ql-toolbar:after,
|
||||
.ql-snow .ql-toolbar:after {
|
||||
clear: both;
|
||||
content: '';
|
||||
display: table;
|
||||
}
|
||||
.ql-snow.ql-toolbar button,
|
||||
.ql-snow .ql-toolbar button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 24px;
|
||||
padding: 3px 5px;
|
||||
width: 28px;
|
||||
}
|
||||
.ql-snow.ql-toolbar button svg,
|
||||
.ql-snow .ql-toolbar button svg {
|
||||
float: left;
|
||||
height: 100%;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:active:hover,
|
||||
.ql-snow .ql-toolbar button:active:hover {
|
||||
outline: none;
|
||||
}
|
||||
.ql-snow.ql-toolbar input.ql-image[type=file],
|
||||
.ql-snow .ql-toolbar input.ql-image[type=file] {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:hover,
|
||||
.ql-snow .ql-toolbar button:hover,
|
||||
.ql-snow.ql-toolbar button:focus,
|
||||
.ql-snow .ql-toolbar button:focus,
|
||||
.ql-snow.ql-toolbar button.ql-active,
|
||||
.ql-snow .ql-toolbar button.ql-active,
|
||||
.ql-snow.ql-toolbar .ql-picker-label:hover,
|
||||
.ql-snow .ql-toolbar .ql-picker-label:hover,
|
||||
.ql-snow.ql-toolbar .ql-picker-label.ql-active,
|
||||
.ql-snow .ql-toolbar .ql-picker-label.ql-active,
|
||||
.ql-snow.ql-toolbar .ql-picker-item:hover,
|
||||
.ql-snow .ql-toolbar .ql-picker-item:hover,
|
||||
.ql-snow.ql-toolbar .ql-picker-item.ql-selected,
|
||||
.ql-snow .ql-toolbar .ql-picker-item.ql-selected {
|
||||
color: #06c;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:hover .ql-fill,
|
||||
.ql-snow .ql-toolbar button:hover .ql-fill,
|
||||
.ql-snow.ql-toolbar button:focus .ql-fill,
|
||||
.ql-snow .ql-toolbar button:focus .ql-fill,
|
||||
.ql-snow.ql-toolbar button.ql-active .ql-fill,
|
||||
.ql-snow .ql-toolbar button.ql-active .ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-label:hover .ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-label:hover .ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-item:hover .ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-item:hover .ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-fill,
|
||||
.ql-snow.ql-toolbar button:hover .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar button:hover .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar button:focus .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar button:focus .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar button.ql-active .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar button.ql-active .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill {
|
||||
fill: #06c;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:hover .ql-stroke,
|
||||
.ql-snow .ql-toolbar button:hover .ql-stroke,
|
||||
.ql-snow.ql-toolbar button:focus .ql-stroke,
|
||||
.ql-snow .ql-toolbar button:focus .ql-stroke,
|
||||
.ql-snow.ql-toolbar button.ql-active .ql-stroke,
|
||||
.ql-snow .ql-toolbar button.ql-active .ql-stroke,
|
||||
.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke,
|
||||
.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke,
|
||||
.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke,
|
||||
.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke,
|
||||
.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke,
|
||||
.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke,
|
||||
.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke,
|
||||
.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke,
|
||||
.ql-snow.ql-toolbar button:hover .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar button:hover .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar button:focus .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar button:focus .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar button.ql-active .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar button.ql-active .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter {
|
||||
stroke: #06c;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
.ql-snow.ql-toolbar button:hover:not(.ql-active),
|
||||
.ql-snow .ql-toolbar button:hover:not(.ql-active) {
|
||||
color: #444;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-fill,
|
||||
.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-fill,
|
||||
.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill {
|
||||
fill: #444;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke,
|
||||
.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke,
|
||||
.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter {
|
||||
stroke: #444;
|
||||
}
|
||||
}
|
||||
.ql-snow {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.ql-snow * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.ql-snow .ql-hidden {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow .ql-out-bottom,
|
||||
.ql-snow .ql-out-top {
|
||||
visibility: hidden;
|
||||
}
|
||||
.ql-snow .ql-tooltip {
|
||||
position: absolute;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
.ql-snow .ql-tooltip a {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ql-snow .ql-tooltip.ql-flip {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
.ql-snow .ql-formats {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.ql-snow .ql-formats:after {
|
||||
clear: both;
|
||||
content: '';
|
||||
display: table;
|
||||
}
|
||||
.ql-snow .ql-stroke {
|
||||
fill: none;
|
||||
stroke: #444;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 2;
|
||||
}
|
||||
.ql-snow .ql-stroke-miter {
|
||||
fill: none;
|
||||
stroke: #444;
|
||||
stroke-miterlimit: 10;
|
||||
stroke-width: 2;
|
||||
}
|
||||
.ql-snow .ql-fill,
|
||||
.ql-snow .ql-stroke.ql-fill {
|
||||
fill: #444;
|
||||
}
|
||||
.ql-snow .ql-empty {
|
||||
fill: none;
|
||||
}
|
||||
.ql-snow .ql-even {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
.ql-snow .ql-thin,
|
||||
.ql-snow .ql-stroke.ql-thin {
|
||||
stroke-width: 1;
|
||||
}
|
||||
.ql-snow .ql-transparent {
|
||||
opacity: 0.4;
|
||||
}
|
||||
.ql-snow .ql-direction svg:last-child {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow .ql-direction.ql-active svg:last-child {
|
||||
display: inline;
|
||||
}
|
||||
.ql-snow .ql-direction.ql-active svg:first-child {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow .ql-editor h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
.ql-snow .ql-editor h2 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.ql-snow .ql-editor h3 {
|
||||
font-size: 1.17em;
|
||||
}
|
||||
.ql-snow .ql-editor h4 {
|
||||
font-size: 1em;
|
||||
}
|
||||
.ql-snow .ql-editor h5 {
|
||||
font-size: 0.83em;
|
||||
}
|
||||
.ql-snow .ql-editor h6 {
|
||||
font-size: 0.67em;
|
||||
}
|
||||
.ql-snow .ql-editor a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.ql-snow .ql-editor blockquote {
|
||||
border-left: 4px solid #ccc;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 5px;
|
||||
padding-left: 16px;
|
||||
}
|
||||
.ql-snow .ql-editor code,
|
||||
.ql-snow .ql-editor pre {
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.ql-snow .ql-editor pre {
|
||||
white-space: pre-wrap;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 5px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.ql-snow .ql-editor code {
|
||||
font-size: 85%;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.ql-snow .ql-editor pre.ql-syntax {
|
||||
background-color: #23241f;
|
||||
color: #f8f8f2;
|
||||
overflow: visible;
|
||||
}
|
||||
.ql-snow .ql-editor img {
|
||||
max-width: 100%;
|
||||
}
|
||||
.ql-snow .ql-picker {
|
||||
color: #444;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
height: 24px;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.ql-snow .ql-picker-label {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
padding-left: 8px;
|
||||
padding-right: 2px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.ql-snow .ql-picker-label::before {
|
||||
display: inline-block;
|
||||
line-height: 22px;
|
||||
}
|
||||
.ql-snow .ql-picker-options {
|
||||
background-color: #fff;
|
||||
display: none;
|
||||
min-width: 100%;
|
||||
padding: 4px 8px;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ql-snow .ql-picker-options .ql-picker-item {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
padding-bottom: 5px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-expanded .ql-picker-label {
|
||||
color: #ccc;
|
||||
z-index: 2;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-fill {
|
||||
fill: #ccc;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-stroke {
|
||||
stroke: #ccc;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-expanded .ql-picker-options {
|
||||
display: block;
|
||||
margin-top: -1px;
|
||||
top: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
.ql-snow .ql-color-picker,
|
||||
.ql-snow .ql-icon-picker {
|
||||
width: 28px;
|
||||
}
|
||||
.ql-snow .ql-color-picker .ql-picker-label,
|
||||
.ql-snow .ql-icon-picker .ql-picker-label {
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.ql-snow .ql-color-picker .ql-picker-label svg,
|
||||
.ql-snow .ql-icon-picker .ql-picker-label svg {
|
||||
right: 4px;
|
||||
}
|
||||
.ql-snow .ql-icon-picker .ql-picker-options {
|
||||
padding: 4px 0px;
|
||||
}
|
||||
.ql-snow .ql-icon-picker .ql-picker-item {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.ql-snow .ql-color-picker .ql-picker-options {
|
||||
padding: 3px 5px;
|
||||
width: 152px;
|
||||
}
|
||||
.ql-snow .ql-color-picker .ql-picker-item {
|
||||
border: 1px solid transparent;
|
||||
float: left;
|
||||
height: 16px;
|
||||
margin: 2px;
|
||||
padding: 0px;
|
||||
width: 16px;
|
||||
}
|
||||
.ql-snow .ql-picker:not(.ql-color-picker):not(.ql-icon-picker) svg {
|
||||
position: absolute;
|
||||
margin-top: -9px;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
width: 18px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-label]:not([data-label=''])::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-label]:not([data-label=''])::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-label]:not([data-label=''])::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-label]:not([data-label=''])::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-label]:not([data-label=''])::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-label]:not([data-label=''])::before {
|
||||
content: attr(data-label);
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header {
|
||||
width: 98px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
|
||||
content: 'Normal';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
|
||||
content: 'Heading 1';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
|
||||
content: 'Heading 2';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
|
||||
content: 'Heading 3';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
|
||||
content: 'Heading 4';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
|
||||
content: 'Heading 5';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
|
||||
content: 'Heading 6';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
|
||||
font-size: 2em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
|
||||
font-size: 1.17em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
|
||||
font-size: 1em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
|
||||
font-size: 0.83em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
|
||||
font-size: 0.67em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font {
|
||||
width: 108px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
|
||||
content: 'Sans Serif';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
|
||||
content: 'Serif';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
|
||||
content: 'Monospace';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
|
||||
font-family: Georgia, Times New Roman, serif;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
|
||||
font-family: Monaco, Courier New, monospace;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size {
|
||||
width: 98px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
|
||||
content: 'Normal';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=small]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
|
||||
content: 'Small';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=large]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
|
||||
content: 'Large';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=huge]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
|
||||
content: 'Huge';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
|
||||
font-size: 10px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
|
||||
font-size: 18px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
|
||||
font-size: 32px;
|
||||
}
|
||||
.ql-snow .ql-color-picker.ql-background .ql-picker-item {
|
||||
background-color: #fff;
|
||||
}
|
||||
.ql-snow .ql-color-picker.ql-color .ql-picker-item {
|
||||
background-color: #000;
|
||||
}
|
||||
.ql-toolbar.ql-snow {
|
||||
border: 1px solid #ccc;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
|
||||
padding: 8px;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-formats {
|
||||
margin-right: 15px;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-picker-label {
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-picker-options {
|
||||
border: 1px solid transparent;
|
||||
box-shadow: rgba(0,0,0,0.2) 0 2px 8px;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-label {
|
||||
border-color: #ccc;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options {
|
||||
border-color: #ccc;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item.ql-selected,
|
||||
.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item:hover {
|
||||
border-color: #000;
|
||||
}
|
||||
.ql-toolbar.ql-snow + .ql-container.ql-snow {
|
||||
border-top: 0px;
|
||||
}
|
||||
.ql-snow .ql-tooltip {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 0px 0px 5px #ddd;
|
||||
color: #444;
|
||||
padding: 5px 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ql-snow .ql-tooltip::before {
|
||||
content: "Visit URL:";
|
||||
line-height: 26px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.ql-snow .ql-tooltip input[type=text] {
|
||||
display: none;
|
||||
border: 1px solid #ccc;
|
||||
font-size: 13px;
|
||||
height: 26px;
|
||||
margin: 0px;
|
||||
padding: 3px 5px;
|
||||
width: 170px;
|
||||
}
|
||||
.ql-snow .ql-tooltip a.ql-preview {
|
||||
display: inline-block;
|
||||
max-width: 200px;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: top;
|
||||
}
|
||||
.ql-snow .ql-tooltip a.ql-action::after {
|
||||
border-right: 1px solid #ccc;
|
||||
content: 'Edit';
|
||||
margin-left: 16px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
.ql-snow .ql-tooltip a.ql-remove::before {
|
||||
content: 'Remove';
|
||||
margin-left: 8px;
|
||||
}
|
||||
.ql-snow .ql-tooltip a {
|
||||
line-height: 26px;
|
||||
}
|
||||
.ql-snow .ql-tooltip.ql-editing a.ql-preview,
|
||||
.ql-snow .ql-tooltip.ql-editing a.ql-remove {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow .ql-tooltip.ql-editing input[type=text] {
|
||||
display: inline-block;
|
||||
}
|
||||
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
|
||||
border-right: 0px;
|
||||
content: 'Save';
|
||||
padding-right: 0px;
|
||||
}
|
||||
.ql-snow .ql-tooltip[data-mode=link]::before {
|
||||
content: "Enter link:";
|
||||
}
|
||||
.ql-snow .ql-tooltip[data-mode=formula]::before {
|
||||
content: "Enter formula:";
|
||||
}
|
||||
.ql-snow .ql-tooltip[data-mode=video]::before {
|
||||
content: "Enter video:";
|
||||
}
|
||||
.ql-snow a {
|
||||
color: #06c;
|
||||
}
|
||||
.ql-container.ql-snow {
|
||||
border: 1px solid #ccc;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,34 +0,0 @@
|
|||
|
||||
新版tpi改动的文件:
|
||||
Index.js
|
||||
contex/TPIContextProvider.js
|
||||
page/main/LeftViewContainer.js
|
||||
taskList/TaskList.js
|
||||
TPMIndexHOC.js
|
||||
App.js
|
||||
CodeRepositoryViewContainer.js
|
||||
|
||||
Index.js
|
||||
choose={context.chooses}
|
||||
|
||||
|
||||
TPIContextProvider.js
|
||||
|
||||
LeftViewContainer.js
|
||||
|
||||
TaskList.js
|
||||
|
||||
TPMIndexHOC.js
|
||||
|
||||
MainContentContainer
|
||||
新:rep_content返回值多了一层 {content: '...'}
|
||||
|
||||
|
||||
|
||||
|
||||
TODO
|
||||
待同步
|
||||
1、timer图标样式更换
|
||||
index.html
|
||||
WebSSHTimer.css
|
||||
WebSSHTimer.js
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 447edde157092e65f05b36bc41c472659c482c6a
|
|
@ -1,93 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const paths = require('./paths');
|
||||
|
||||
// Make sure that including paths.js after env.js will read .env variables.
|
||||
delete require.cache[require.resolve('./paths')];
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV;
|
||||
if (!NODE_ENV) {
|
||||
throw new Error(
|
||||
'The NODE_ENV environment variable is required but was not specified.'
|
||||
);
|
||||
}
|
||||
|
||||
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
|
||||
var dotenvFiles = [
|
||||
`${paths.dotenv}.${NODE_ENV}.local`,
|
||||
`${paths.dotenv}.${NODE_ENV}`,
|
||||
// Don't include `.env.local` for `test` environment
|
||||
// since normally you expect tests to produce the same
|
||||
// results for everyone
|
||||
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
|
||||
paths.dotenv,
|
||||
].filter(Boolean);
|
||||
|
||||
// Load environment variables from .env* files. Suppress warnings using silent
|
||||
// if this file is missing. dotenv will never modify any environment variables
|
||||
// that have already been set. Variable expansion is supported in .env files.
|
||||
// https://github.com/motdotla/dotenv
|
||||
// https://github.com/motdotla/dotenv-expand
|
||||
dotenvFiles.forEach(dotenvFile => {
|
||||
if (fs.existsSync(dotenvFile)) {
|
||||
require('dotenv-expand')(
|
||||
require('dotenv').config({
|
||||
path: dotenvFile,
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// We support resolving modules according to `NODE_PATH`.
|
||||
// This lets you use absolute paths in imports inside large monorepos:
|
||||
// https://github.com/facebookincubator/create-react-app/issues/253.
|
||||
// It works similar to `NODE_PATH` in Node itself:
|
||||
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
|
||||
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
|
||||
// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
|
||||
// https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
|
||||
// We also resolve them to make sure all tools using them work consistently.
|
||||
const appDirectory = fs.realpathSync(process.cwd());
|
||||
process.env.NODE_PATH = (process.env.NODE_PATH || '')
|
||||
.split(path.delimiter)
|
||||
.filter(folder => folder && !path.isAbsolute(folder))
|
||||
.map(folder => path.resolve(appDirectory, folder))
|
||||
.join(path.delimiter);
|
||||
|
||||
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
|
||||
// injected into the application via DefinePlugin in Webpack configuration.
|
||||
const REACT_APP = /^REACT_APP_/i;
|
||||
|
||||
function getClientEnvironment(publicUrl) {
|
||||
const raw = Object.keys(process.env)
|
||||
.filter(key => REACT_APP.test(key))
|
||||
.reduce(
|
||||
(env, key) => {
|
||||
env[key] = process.env[key];
|
||||
return env;
|
||||
},
|
||||
{
|
||||
// Useful for determining whether we’re running in production mode.
|
||||
// Most importantly, it switches React into the correct mode.
|
||||
NODE_ENV: process.env.NODE_ENV || 'development',
|
||||
// Useful for resolving the correct path to static assets in `public`.
|
||||
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
|
||||
// This should only be used as an escape hatch. Normally you would put
|
||||
// images into the `src` and `import` them in code to get their paths.
|
||||
PUBLIC_URL: '/forgeplus-react/build/.',
|
||||
}
|
||||
);
|
||||
// Stringify all values so we can feed into Webpack DefinePlugin
|
||||
const stringified = {
|
||||
'process.env': Object.keys(raw).reduce((env, key) => {
|
||||
env[key] = JSON.stringify(raw[key]);
|
||||
return env;
|
||||
}, {}),
|
||||
};
|
||||
|
||||
return { raw, stringified };
|
||||
}
|
||||
|
||||
module.exports = getClientEnvironment;
|
|
@ -1,14 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
// This is a custom Jest transformer turning style imports into empty objects.
|
||||
// http://facebook.github.io/jest/docs/en/webpack.html
|
||||
|
||||
module.exports = {
|
||||
process() {
|
||||
return 'module.exports = {};';
|
||||
},
|
||||
getCacheKey() {
|
||||
// The output is always the same.
|
||||
return 'cssTransform';
|
||||
},
|
||||
};
|
|
@ -1,12 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
|
||||
// This is a custom Jest transformer turning file imports into filenames.
|
||||
// http://facebook.github.io/jest/docs/en/webpack.html
|
||||
|
||||
module.exports = {
|
||||
process(src, filename) {
|
||||
return `module.exports = ${JSON.stringify(path.basename(filename))};`;
|
||||
},
|
||||
};
|
|
@ -1,55 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const url = require('url');
|
||||
|
||||
// Make sure any symlinks in the project folder are resolved:
|
||||
// https://github.com/facebookincubator/create-react-app/issues/637
|
||||
const appDirectory = fs.realpathSync(process.cwd());
|
||||
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
|
||||
|
||||
const envPublicUrl = process.env.PUBLIC_URL;
|
||||
|
||||
function ensureSlash(path, needsSlash) {
|
||||
const hasSlash = path.endsWith('/');
|
||||
if (hasSlash && !needsSlash) {
|
||||
return path.substr(path, path.length - 1);
|
||||
} else if (!hasSlash && needsSlash) {
|
||||
return `${path}/`;
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
const getPublicUrl = appPackageJson =>
|
||||
envPublicUrl || require(appPackageJson).homepage;
|
||||
|
||||
// We use `PUBLIC_URL` environment variable or "homepage" field to infer
|
||||
// "public path" at which the app is served.
|
||||
// Webpack needs to know it to put the right <script> hrefs into HTML even in
|
||||
// single-page apps that may serve index.html for nested URLs like /todos/42.
|
||||
// We can't use a relative path in HTML because we don't want to load something
|
||||
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
|
||||
function getServedPath(appPackageJson) {
|
||||
const publicUrl = getPublicUrl(appPackageJson);
|
||||
const servedUrl =
|
||||
envPublicUrl || (publicUrl ? url.parse(publicUrl).pathname : '/');
|
||||
return ensureSlash(servedUrl, true);
|
||||
}
|
||||
|
||||
// config after eject: we're in ./config/
|
||||
module.exports = {
|
||||
dotenv: resolveApp('.env'),
|
||||
appBuild: resolveApp('build'),
|
||||
appPublic: resolveApp('public'),
|
||||
appHtml: resolveApp('public/index.html'),
|
||||
appIndexJs: resolveApp('src/index.js'),
|
||||
appPackageJson: resolveApp('package.json'),
|
||||
appSrc: resolveApp('src'),
|
||||
yarnLockFile: resolveApp('yarn.lock'),
|
||||
testsSetup: resolveApp('src/setupTests.js'),
|
||||
appNodeModules: resolveApp('node_modules'),
|
||||
publicUrl: getPublicUrl(resolveApp('package.json')),
|
||||
servedPath: getServedPath(resolveApp('package.json')),
|
||||
};
|
|
@ -1,22 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
if (typeof Promise === 'undefined') {
|
||||
// Rejection tracking prevents a common issue where React gets into an
|
||||
// inconsistent state due to an error, but it gets swallowed by a Promise,
|
||||
// and the user has no idea what causes React's erratic future behavior.
|
||||
require('promise/lib/rejection-tracking').enable();
|
||||
window.Promise = require('promise/lib/es6-extensions.js');
|
||||
}
|
||||
|
||||
// fetch() polyfill for making API calls.
|
||||
require('whatwg-fetch');
|
||||
|
||||
// Object.assign() is commonly used with React.
|
||||
// It will use the native implementation if it's present and isn't buggy.
|
||||
Object.assign = require('object-assign');
|
||||
|
||||
// In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet.
|
||||
// We don't polyfill it in the browser--this is user's responsibility.
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
require('raf').polyfill(global);
|
||||
}
|
|
@ -1,287 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
|
||||
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
|
||||
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
|
||||
const eslintFormatter = require('react-dev-utils/eslintFormatter');
|
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||
// const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
|
||||
const getClientEnvironment = require('./env');
|
||||
const paths = require('./paths');
|
||||
|
||||
// Webpack uses `publicPath` to determine where the app is being served from.
|
||||
// In development, we always serve from the root. This makes config easier.
|
||||
const publicPath = '/';
|
||||
// `publicUrl` is just like `publicPath`, but we will provide it to our app
|
||||
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
||||
// Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
|
||||
const publicUrl = '';
|
||||
// Get environment variables to inject into our app.
|
||||
const env = getClientEnvironment(publicUrl);
|
||||
|
||||
// This is the development configuration.
|
||||
// It is focused on developer experience and fast rebuilds.
|
||||
// The production configuration is different and lives in a separate file.
|
||||
// 测试用的
|
||||
module.exports = {
|
||||
// You may want 'eval' instead if you prefer to see the compiled output in DevTools.
|
||||
// See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.s
|
||||
//devtool: "cheap-module-eval-source-map",
|
||||
// 开启调试
|
||||
devtool: "source-map", // 开启调试
|
||||
// These are the "entry points" to our application.
|
||||
// This means they will be the "root" imports that are included in JS bundle.
|
||||
// The first two entry points enable "hot" CSS and auto-refreshes for JS.
|
||||
entry: [
|
||||
// We ship a few polyfills by default:
|
||||
require.resolve('./polyfills'),
|
||||
// Include an alternative client for WebpackDevServer. A client's job is to
|
||||
// connect to WebpackDevServer by a socket and get notified about changes.
|
||||
// When you save a file, the client will either apply hot updates (in case
|
||||
// of CSS changes), or refresh the page (in case of JS changes). When you
|
||||
// make a syntax error, this client will display a syntax error overlay.
|
||||
// Note: instead of the default WebpackDevServer client, we use a custom one
|
||||
// to bring better experience for Create React App users. You can replace
|
||||
// the line below with these two lines if you prefer the stock client:
|
||||
// require.resolve('webpack-dev-server/client') + '?/',
|
||||
// require.resolve('webpack/hot/dev-server'),
|
||||
require.resolve('react-dev-utils/webpackHotDevClient'),
|
||||
// Finally, this is your app's code:
|
||||
paths.appIndexJs,
|
||||
// We include the app code last so that if there is a runtime error during
|
||||
// initialization, it doesn't blow up the WebpackDevServer client, and
|
||||
// changing JS code would still trigger a refresh.
|
||||
],
|
||||
output: {
|
||||
// Add /* filename */ comments to generated require()s in the output.
|
||||
pathinfo: true,
|
||||
// This does not produce a real file. It's just the virtual path that is
|
||||
// served by WebpackDevServer in development. This is the JS bundle
|
||||
// containing code from all our entry points, and the Webpack runtime.
|
||||
filename: 'static/js/bundle.js',
|
||||
// There are also additional JS chunk files if you use code splitting.
|
||||
chunkFilename: 'static/js/[name].chunk.js',
|
||||
// This is the URL that app is served from. We use "/" in development.
|
||||
publicPath: publicPath,
|
||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
||||
devtoolModuleFilenameTemplate: info =>
|
||||
path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),
|
||||
},
|
||||
resolve: {
|
||||
// This allows you to set a fallback for where Webpack should look for modules.
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebookincubator/create-react-app/issues/253
|
||||
modules: ['node_modules', paths.appNodeModules].concat(
|
||||
// It is guaranteed to exist because we tweak it in `env.js`
|
||||
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
|
||||
),
|
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
// https://github.com/facebookincubator/create-react-app/issues/290
|
||||
// `web` extension prefixes have been added for better support
|
||||
// for React Native Web.
|
||||
extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'],
|
||||
alias: {
|
||||
|
||||
"educoder": __dirname + "/../src/common/educoder.js",
|
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
'react-native': 'react-native-web',
|
||||
},
|
||||
plugins: [
|
||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
||||
// This often causes confusion because we only process files within src/ with babel.
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
||||
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
|
||||
// MonacoEditor
|
||||
// https://github.com/Microsoft/monaco-editor/blob/master/docs/integrate-esm.md
|
||||
// https://github.com/Microsoft/monaco-editor-webpack-plugin/issues/56
|
||||
// new MonacoWebpackPlugin(),
|
||||
],
|
||||
},
|
||||
module: {
|
||||
strictExportPresence: true,
|
||||
rules: [
|
||||
// TODO: Disable require.ensure as it's not a standard language feature.
|
||||
// We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176.
|
||||
// { parser: { requireEnsure: false } },
|
||||
|
||||
// First, run the linter.
|
||||
// It's important to do this before Babel processes the JS.
|
||||
// {
|
||||
// test: /\.(js|jsx|mjs)$/,
|
||||
// enforce: 'pre',
|
||||
// use: [
|
||||
// {
|
||||
// options: {
|
||||
// formatter: eslintFormatter,
|
||||
// eslintPath: require.resolve('eslint'),
|
||||
//
|
||||
// },
|
||||
// loader: require.resolve('eslint-loader'),
|
||||
// },
|
||||
// ],
|
||||
// include: paths.appSrc,
|
||||
// },
|
||||
{
|
||||
// "oneOf" will traverse all following loaders until one will
|
||||
// match the requirements. When no loader matches it will fall
|
||||
// back to the "file" loader at the end of the loader list.
|
||||
oneOf: [
|
||||
// "url" loader works like "file" loader except that it embeds assets
|
||||
// smaller than specified limit in bytes as data URLs to avoid requests.
|
||||
// A missing `test` is equivalent to a match.
|
||||
{
|
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
|
||||
loader: require.resolve('url-loader'),
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// Process JS with Babel.
|
||||
{
|
||||
test: /\.(js|jsx|mjs)$/,
|
||||
include: paths.appSrc,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
|
||||
// This is a feature of `babel-loader` for webpack (not Babel itself).
|
||||
// It enables caching results in ./node_modules/.cache/babel-loader/
|
||||
// directory for faster rebuilds.
|
||||
cacheDirectory: true,
|
||||
},
|
||||
},
|
||||
// "postcss" loader applies autoprefixer to our CSS.
|
||||
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
||||
// "style" loader turns CSS into JS modules that inject <style> tags.
|
||||
// In production, we use a plugin to extract that CSS to a file, but
|
||||
// in development "style" loader enables hot editing of CSS.
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
require.resolve('style-loader'),
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('postcss-loader'),
|
||||
options: {
|
||||
// Necessary for external CSS imports to work
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2677
|
||||
ident: 'postcss',
|
||||
plugins: () => [
|
||||
require('postcss-flexbugs-fixes'),
|
||||
autoprefixer({
|
||||
browsers: [
|
||||
'>1%',
|
||||
'last 4 versions',
|
||||
'Firefox ESR',
|
||||
'not ie < 9', // React doesn't support IE8 anyway
|
||||
],
|
||||
flexbox: 'no-2009',
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
require.resolve("style-loader"),
|
||||
{
|
||||
loader: require.resolve("css-loader"),
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve("sass-loader")
|
||||
}
|
||||
],
|
||||
},
|
||||
// "file" loader makes sure those assets get served by WebpackDevServer.
|
||||
// When you `import` an asset, you get its (virtual) filename.
|
||||
// In production, they would get copied to the `build` folder.
|
||||
// This loader doesn't use a "test" so it will catch all modules
|
||||
// that fall through the other loaders.
|
||||
{
|
||||
// Exclude `js` files to keep "css" loader working as it injects
|
||||
// its runtime that would otherwise processed through "file" loader.
|
||||
// Also exclude `html` and `json` extensions so they get processed
|
||||
// by webpacks internal loaders.
|
||||
exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
|
||||
loader: require.resolve('file-loader'),
|
||||
options: {
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// ** STOP ** Are you adding a new loader?
|
||||
// Make sure to add the new loader(s) before the "file" loader.
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In development, this will be an empty string.
|
||||
new InterpolateHtmlPlugin(env.raw),
|
||||
// Generates an `index.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
inject: true,
|
||||
template: paths.appHtml,
|
||||
}),
|
||||
// Add module names to factory functions so they appear in browser profiler.
|
||||
new webpack.NamedModulesPlugin(),
|
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`.
|
||||
new webpack.DefinePlugin(env.stringified),
|
||||
// This is necessary to emit hot updates (currently CSS only):
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
// Watcher doesn't work well if you mistype casing in a path so we use
|
||||
// a plugin that prints an error when you attempt to do this.
|
||||
// See https://github.com/facebookincubator/create-react-app/issues/240
|
||||
new CaseSensitivePathsPlugin(),
|
||||
// If you require a missing module and then `npm install` it, you still have
|
||||
// to restart the development server for Webpack to discover it. This plugin
|
||||
// makes the discovery automatic so you don't have to restart.
|
||||
// See https://github.com/facebookincubator/create-react-app/issues/186
|
||||
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
|
||||
// Moment.js is an extremely popular library that bundles large locale files
|
||||
// by default due to how Webpack interprets its code. This is a practical
|
||||
// solution that requires the user to opt into importing specific locales.
|
||||
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
||||
// You can remove this if you don't use Moment.js:
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||
// new MonacoWebpackPlugin(),
|
||||
],
|
||||
// Some libraries import Node modules but don't use them in the browser.
|
||||
// Tell Webpack to provide empty mocks for them so importing them works.
|
||||
node: {
|
||||
dgram: 'empty',
|
||||
fs: 'empty',
|
||||
net: 'empty',
|
||||
tls: 'empty',
|
||||
child_process: 'empty',
|
||||
},
|
||||
// Turn off performance hints during development because we don't do any
|
||||
// splitting or minification in interest of speed. These warnings become
|
||||
// cumbersome.
|
||||
performance: {
|
||||
hints: false,
|
||||
},
|
||||
};
|
|
@ -1,391 +0,0 @@
|
|||
'use strict';
|
||||
// extract-css-assets-webpack-plugin
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
const ManifestPlugin = require('webpack-manifest-plugin');
|
||||
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
|
||||
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
|
||||
const eslintFormatter = require('react-dev-utils/eslintFormatter');
|
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
|
||||
// const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
|
||||
// const TerserPlugin = require('terser-webpack-plugin');
|
||||
|
||||
const paths = require('./paths');
|
||||
const getClientEnvironment = require('./env');
|
||||
|
||||
// Webpack uses `publicPath` to determine where the app is being served from.
|
||||
// It requires a trailing slash, or the file assets will get an incorrect path.
|
||||
const publicPath = paths.servedPath;
|
||||
// Some apps do not use client-side routing with pushState.
|
||||
// For these, "homepage" can be set to "." to enable relative asset paths.
|
||||
const shouldUseRelativeAssetPaths = publicPath === './';
|
||||
// Source maps are resource heavy and can cause out of memory issue for large source files.
|
||||
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
|
||||
// `publicUrl` is just like `publicPath`, but we will provide it to our app
|
||||
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
||||
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
|
||||
const publicUrl = publicPath.slice(0, -1);
|
||||
// Get environment variables to inject into our app.
|
||||
const env = getClientEnvironment(publicUrl);
|
||||
|
||||
// Assert this just to be safe.
|
||||
// Development builds of React are slow and not intended for production.
|
||||
if (env.stringified['process.env'].NODE_ENV !== '"production"') {
|
||||
throw new Error('Production builds must have NODE_ENV=production.');
|
||||
}
|
||||
|
||||
// Note: defined here because it will be used more than once.
|
||||
const cssFilename = './static/css/[name].[contenthash:8].css';
|
||||
|
||||
// ExtractTextPlugin expects the build output to be flat.
|
||||
// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27)
|
||||
// However, our output is structured with css, js and media folders.
|
||||
// To have this structure working with relative paths, we have to use custom options.
|
||||
const extractTextPluginOptions = shouldUseRelativeAssetPaths
|
||||
? // Making sure that the publicPath goes back to to build folder.
|
||||
{ publicPath: Array(cssFilename.split('/').length).join('../') }
|
||||
: {};
|
||||
|
||||
// This is the production configuration.
|
||||
// It compiles slowly and is focused on producing a fast and minimal bundle.
|
||||
// The development configuration is different and lives in a separate file.
|
||||
// 上线用的
|
||||
// console.log('publicPath ', publicPath)
|
||||
module.exports = {
|
||||
// optimization: {
|
||||
// minimize: true,
|
||||
// minimizer: [new TerserPlugin()],
|
||||
// },
|
||||
// externals: {
|
||||
// 'react': 'window.React'
|
||||
// },
|
||||
// Don't attempt to continue if there are any errors.
|
||||
bail: true,
|
||||
// We generate sourcemaps in production. This is slow but gives good results.
|
||||
// You can exclude the *.map files from the build during deployment.
|
||||
// devtool: shouldUseSourceMap ? 'nosources-source-map' : false, //正式版
|
||||
devtool:false,//测试版
|
||||
// In production, we only want to load the polyfills and the app code.
|
||||
entry: [require.resolve('./polyfills'), paths.appIndexJs],
|
||||
output: {
|
||||
// The build folder.
|
||||
path: paths.appBuild,
|
||||
// Generated JS file names (with nested folders).
|
||||
// There will be one main bundle, and one file per asynchronous chunk.
|
||||
// We don't currently advertise code splitting but Webpack supports it.
|
||||
filename: './static/js/[name].[chunkhash:8].js',
|
||||
chunkFilename: './static/js/[name].[chunkhash:8].chunk.js',
|
||||
// We inferred the "public path" (such as / or /my-project) from homepage.
|
||||
// cdn
|
||||
// publicPath: 'https://shixun.educoder.net/react/build/', //publicPath, https://cdn.educoder.net
|
||||
// publicPath: 'https://cdn-testeduplus2.educoder.net/react/build/', //publicPath, https://cdn.educoder.net
|
||||
publicPath: '/forgeplus-react/build/', //publicPath, https://cdn.educoder.net
|
||||
|
||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
||||
devtoolModuleFilenameTemplate: info =>
|
||||
path
|
||||
.relative(paths.appSrc, info.absoluteResourcePath)
|
||||
.replace(/\\/g, '/'),
|
||||
},
|
||||
resolve: {
|
||||
// This allows you to set a fallback for where Webpack should look for modules.
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebookincubator/create-react-app/issues/253
|
||||
modules: ['node_modules', paths.appNodeModules].concat(
|
||||
// It is guaranteed to exist because we tweak it in `env.js`
|
||||
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
|
||||
),
|
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
// https://github.com/facebookincubator/create-react-app/issues/290
|
||||
// `web` extension prefixes have been added for better support
|
||||
// for React Native Web.
|
||||
extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'],
|
||||
alias: {
|
||||
"educoder": __dirname + "/../src/common/educoder.js",
|
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
'react-native': 'react-native-web',
|
||||
},
|
||||
plugins: [
|
||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
||||
// This often causes confusion because we only process files within src/ with babel.
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
||||
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
|
||||
],
|
||||
},
|
||||
module: {
|
||||
strictExportPresence: true,
|
||||
rules: [
|
||||
// TODO: Disable require.ensure as it's not a standard language feature.
|
||||
// We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176.
|
||||
// { parser: { requireEnsure: false } },
|
||||
|
||||
// First, run the linter.
|
||||
// It's important to do this before Babel processes the JS.
|
||||
{
|
||||
test: /\.(js|jsx|mjs)$/,
|
||||
enforce: 'pre',
|
||||
use: [
|
||||
{
|
||||
options: {
|
||||
formatter: eslintFormatter,
|
||||
eslintPath: require.resolve('eslint'),
|
||||
|
||||
},
|
||||
loader: require.resolve('eslint-loader'),
|
||||
},
|
||||
],
|
||||
include: paths.appSrc,
|
||||
},
|
||||
{
|
||||
// "oneOf" will traverse all following loaders until one will
|
||||
// match the requirements. When no loader matches it will fall
|
||||
// back to the "file" loader at the end of the loader list.
|
||||
oneOf: [
|
||||
// "url" loader works just like "file" loader but it also embeds
|
||||
// assets smaller than specified size as data URLs to avoid requests.
|
||||
{
|
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
|
||||
loader: require.resolve('url-loader'),
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// Process JS with Babel.
|
||||
{
|
||||
test: /\.(js|jsx|mjs)$/,
|
||||
include: paths.appSrc,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
|
||||
compact: true,
|
||||
},
|
||||
},
|
||||
// The notation here is somewhat confusing.
|
||||
// "postcss" loader applies autoprefixer to our CSS.
|
||||
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
||||
// "style" loader normally turns CSS into JS modules injecting <style>,
|
||||
// but unlike in development configuration, we do something different.
|
||||
// `ExtractTextPlugin` first applies the "postcss" and "css" loaders
|
||||
// (second argument), then grabs the result CSS and puts it into a
|
||||
// separate file in our build process. This way we actually ship
|
||||
// a single CSS file in production instead of JS code injecting <style>
|
||||
// tags. If you use code splitting, however, any async bundles will still
|
||||
// use the "style" loader inside the async code so CSS from them won't be
|
||||
// in the main CSS file.
|
||||
{
|
||||
test: /\.css$/,
|
||||
loader: ExtractTextPlugin.extract(
|
||||
Object.assign(
|
||||
{
|
||||
fallback: {
|
||||
loader: require.resolve('style-loader'),
|
||||
options: {
|
||||
hmr: false,
|
||||
},
|
||||
},
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
minimize: true,
|
||||
sourceMap: shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('postcss-loader'),
|
||||
options: {
|
||||
// Necessary for external CSS imports to work
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2677
|
||||
ident: 'postcss',
|
||||
plugins: () => [
|
||||
require('postcss-flexbugs-fixes'),
|
||||
autoprefixer({
|
||||
browsers: [
|
||||
'>1%',
|
||||
'last 4 versions',
|
||||
'Firefox ESR',
|
||||
'not ie < 9', // React doesn't support IE8 anyway
|
||||
],
|
||||
flexbox: 'no-2009',
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
extractTextPluginOptions
|
||||
)
|
||||
),
|
||||
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
require.resolve("style-loader"),
|
||||
{
|
||||
loader: require.resolve("css-loader"),
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
minimize: true,
|
||||
sourceMap: shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve("sass-loader")
|
||||
}
|
||||
],
|
||||
},
|
||||
// "file" loader makes sure assets end up in the `build` folder.
|
||||
// When you `import` an asset, you get its filename.
|
||||
// This loader doesn't use a "test" so it will catch all modules
|
||||
// that fall through the other loaders.
|
||||
{
|
||||
loader: require.resolve('file-loader'),
|
||||
// Exclude `js` files to keep "css" loader working as it injects
|
||||
// it's runtime that would otherwise processed through "file" loader.
|
||||
// Also exclude `html` and `json` extensions so they get processed
|
||||
// by webpacks internal loaders.
|
||||
exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
|
||||
options: {
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// ** STOP ** Are you adding a new loader?
|
||||
// Make sure to add the new loader(s) before the "file" loader.
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In production, it will be an empty string unless you specify "homepage"
|
||||
// in `package.json`, in which case it will be the pathname of that URL.
|
||||
new InterpolateHtmlPlugin(env.raw),
|
||||
// Generates an `index.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
inject: true,
|
||||
template: paths.appHtml,
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeRedundantAttributes: true,
|
||||
useShortDoctype: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
keepClosingSlash: true,
|
||||
minifyJS: true,
|
||||
minifyCSS: true,
|
||||
minifyURLs: true,
|
||||
},
|
||||
}),
|
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
|
||||
// It is absolutely essential that NODE_ENV was set to production here.
|
||||
// Otherwise React will be compiled in the very slow development mode.
|
||||
new webpack.DefinePlugin(env.stringified),
|
||||
// Minify the code.
|
||||
// new webpack.optimize.UglifyJsPlugin({
|
||||
// compress: {
|
||||
// warnings: false,
|
||||
// // Disabled because of an issue with Uglify breaking seemingly valid code:
|
||||
// // https://github.com/facebookincubator/create-react-app/issues/2376
|
||||
// // Pending further investigation:
|
||||
// // https://github.com/mishoo/UglifyJS2/issues/2011
|
||||
// comparisons: false,
|
||||
// },
|
||||
// mangle: {
|
||||
// safari10: true,
|
||||
// },
|
||||
// output: {
|
||||
// comments: false,
|
||||
// // Turned on because emoji and regex is not minified properly using default
|
||||
// // https://github.com/facebookincubator/create-react-app/issues/2488
|
||||
// ascii_only: true,
|
||||
// },
|
||||
// sourceMap: shouldUseSourceMap,
|
||||
// }),
|
||||
//正式版上线后打开去掉debuger和console
|
||||
// new ParallelUglifyPlugin({
|
||||
// cacheDir: '.cache/',
|
||||
// uglifyJS:{
|
||||
// output: {
|
||||
// comments: false
|
||||
// },
|
||||
// compress: {
|
||||
// drop_debugger: true,
|
||||
// drop_console: true
|
||||
// }
|
||||
// }
|
||||
// }),
|
||||
// Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
|
||||
new ExtractTextPlugin({
|
||||
filename: cssFilename,
|
||||
}),
|
||||
// Generate a manifest file which contains a mapping of all asset filenames
|
||||
// to their corresponding output file so that tools can pick it up without
|
||||
// having to parse `index.html`.
|
||||
new ManifestPlugin({
|
||||
fileName: 'asset-manifest.json',
|
||||
}),
|
||||
// Generate a service worker script that will precache, and keep up to date,
|
||||
// the HTML & assets that are part of the Webpack build.
|
||||
new SWPrecacheWebpackPlugin({
|
||||
// By default, a cache-busting query parameter is appended to requests
|
||||
// used to populate the caches, to ensure the responses are fresh.
|
||||
// If a URL is already hashed by Webpack, then there is no concern
|
||||
// about it being stale, and the cache-busting can be skipped.
|
||||
dontCacheBustUrlsMatching: /\.\w{8}\./,
|
||||
filename: 'service-worker.js',
|
||||
logger(message) {
|
||||
if (message.indexOf('Total precache size is') === 0) {
|
||||
// This message occurs for every build and is a bit too noisy.
|
||||
return;
|
||||
}
|
||||
if (message.indexOf('Skipping static resource') === 0) {
|
||||
// This message obscures real errors so we ignore it.
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2612
|
||||
return;
|
||||
}
|
||||
// console.log(message);
|
||||
},
|
||||
minify: true,
|
||||
// For unknown URLs, fallback to the index page
|
||||
navigateFallback: publicUrl + '/index.html',
|
||||
// Ignores URLs starting from /__ (useful for Firebase):
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2237#issuecomment-302693219
|
||||
navigateFallbackWhitelist: [/^(?!\/__).*/],
|
||||
// Don't precache sourcemaps (they're large) and build asset manifest:
|
||||
staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
|
||||
}),
|
||||
// Moment.js is an extremely popular library that bundles large locale files
|
||||
// by default due to how Webpack interprets its code. This is a practical
|
||||
// solution that requires the user to opt into importing specific locales.
|
||||
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
||||
// You can remove this if you don't use Moment.js:
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||
// new MonacoWebpackPlugin(),
|
||||
],
|
||||
// Some libraries import Node modules but don't use them in the browser.
|
||||
// Tell Webpack to provide empty mocks for them so importing them works.
|
||||
node: {
|
||||
dgram: 'empty',
|
||||
fs: 'empty',
|
||||
net: 'empty',
|
||||
tls: 'empty',
|
||||
child_process: 'empty',
|
||||
},
|
||||
};
|
|
@ -1,95 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
|
||||
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
|
||||
const ignoredFiles = require('react-dev-utils/ignoredFiles');
|
||||
const config = require('./webpack.config.dev');
|
||||
const paths = require('./paths');
|
||||
|
||||
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
|
||||
const host = process.env.HOST || '0.0.0.0';
|
||||
|
||||
module.exports = function(proxy, allowedHost) {
|
||||
return {
|
||||
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
|
||||
// websites from potentially accessing local content through DNS rebinding:
|
||||
// https://github.com/webpack/webpack-dev-server/issues/887
|
||||
// https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
|
||||
// However, it made several existing use cases such as development in cloud
|
||||
// environment or subdomains in development significantly more complicated:
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2271
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2233
|
||||
// While we're investigating better solutions, for now we will take a
|
||||
// compromise. Since our WDS configuration only serves files in the `public`
|
||||
// folder we won't consider accessing them a vulnerability. However, if you
|
||||
// use the `proxy` feature, it gets more dangerous because it can expose
|
||||
// remote code execution vulnerabilities in backends like Django and Rails.
|
||||
// So we will disable the host check normally, but enable it if you have
|
||||
// specified the `proxy` setting. Finally, we let you override it if you
|
||||
// really know what you're doing with a special environment variable.
|
||||
disableHostCheck:
|
||||
!proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
|
||||
// Enable gzip compression of generated files.
|
||||
compress: true,
|
||||
// Silence WebpackDevServer's own logs since they're generally not useful.
|
||||
// It will still show compile warnings and errors with this setting.
|
||||
clientLogLevel: 'none',
|
||||
// By default WebpackDevServer serves physical files from current directory
|
||||
// in addition to all the virtual build products that it serves from memory.
|
||||
// This is confusing because those files won’t automatically be available in
|
||||
// production build folder unless we copy them. However, copying the whole
|
||||
// project directory is dangerous because we may expose sensitive files.
|
||||
// Instead, we establish a convention that only files in `public` directory
|
||||
// get served. Our build script will copy `public` into the `build` folder.
|
||||
// In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
|
||||
// Note that we only recommend to use `public` folder as an escape hatch
|
||||
// for files like `favicon.ico`, `manifest.json`, and libraries that are
|
||||
// for some reason broken when imported through Webpack. If you just want to
|
||||
// use an image, put it in `src` and `import` it from JavaScript instead.
|
||||
contentBase: paths.appPublic,
|
||||
// By default files from `contentBase` will not trigger a page reload.
|
||||
watchContentBase: true,
|
||||
// Enable hot reloading server. It will provide /sockjs-node/ endpoint
|
||||
// for the WebpackDevServer client so it can learn when the files were
|
||||
// updated. The WebpackDevServer client is included as an entry point
|
||||
// in the Webpack development configuration. Note that only changes
|
||||
// to CSS are currently hot reloaded. JS changes will refresh the browser.
|
||||
hot: true,
|
||||
// It is important to tell WebpackDevServer to use the same "root" path
|
||||
// as we specified in the config. In development, we always serve from /.
|
||||
publicPath: config.output.publicPath,
|
||||
// WebpackDevServer is noisy by default so we emit custom message instead
|
||||
// by listening to the compiler events with `compiler.plugin` calls above.
|
||||
quiet: true,
|
||||
// Reportedly, this avoids CPU overload on some systems.
|
||||
// https://github.com/facebookincubator/create-react-app/issues/293
|
||||
// src/node_modules is not ignored to support absolute imports
|
||||
// https://github.com/facebookincubator/create-react-app/issues/1065
|
||||
watchOptions: {
|
||||
ignored: ignoredFiles(paths.appSrc),
|
||||
},
|
||||
// Enable HTTPS if the HTTPS environment variable is set to 'true'
|
||||
https: protocol === 'https',
|
||||
host: host,
|
||||
overlay: false,
|
||||
historyApiFallback: {
|
||||
// Paths with dots should still use the history fallback.
|
||||
// See https://github.com/facebookincubator/create-react-app/issues/387.
|
||||
disableDotRule: true,
|
||||
},
|
||||
public: allowedHost,
|
||||
proxy,
|
||||
before(app) {
|
||||
// This lets us open files from the runtime error overlay.
|
||||
app.use(errorOverlayMiddleware());
|
||||
// This service worker file is effectively a 'no-op' that will reset any
|
||||
// previous service worker registered for the same host:port combination.
|
||||
// We do this in development to avoid hitting the production cache if
|
||||
// it used the same host and port.
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432
|
||||
app.use(noopServiceWorkerMiddleware());
|
||||
},
|
||||
};
|
||||
};
|
|
@ -1,46 +0,0 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import './index.css';
|
||||
import './indexPlus.css';
|
||||
import App from './App';
|
||||
|
||||
// 加之前main.js 18.1MB
|
||||
// import { message } from 'antd';
|
||||
import message from 'antd/lib/message';
|
||||
import 'antd/lib/message/style/css';
|
||||
|
||||
import { AppContainer } from 'react-hot-loader';
|
||||
|
||||
import registerServiceWorker from './registerServiceWorker';
|
||||
|
||||
import { configureUrlQuery } from 'react-url-query';
|
||||
|
||||
import history from './history';
|
||||
|
||||
// link the history used in our app to url-query so it can update the URL with it.
|
||||
configureUrlQuery({ history });
|
||||
// ----------------------------------------------------------------------------------- 请求配置
|
||||
|
||||
window.__useKindEditor = false;
|
||||
|
||||
|
||||
const render = (Component) => {
|
||||
ReactDOM.render(
|
||||
<AppContainer {...this.props} {...this.state}>
|
||||
<Component {...this.props} {...this.state}/>
|
||||
</AppContainer>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// ReactDOM.render(
|
||||
// ,
|
||||
// document.getElementById('root'));
|
||||
// registerServiceWorker();
|
||||
|
||||
render(App);
|
||||
if (module.hot) {
|
||||
module.hot.accept('./App', () => { render(App) });
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,190 +0,0 @@
|
|||
{
|
||||
"name": "educoder",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@icedesign/base": "^0.2.5",
|
||||
"@monaco-editor/react": "^2.3.0",
|
||||
"@novnc/novnc": "^1.1.0",
|
||||
"antd": "^3.23.2",
|
||||
"array-flatten": "^2.1.2",
|
||||
"autoprefixer": "7.1.6",
|
||||
"axios": "^0.18.0",
|
||||
"babel-core": "6.26.0",
|
||||
"babel-eslint": "7.2.3",
|
||||
"babel-jest": "20.0.3",
|
||||
"babel-loader": "7.1.2",
|
||||
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
||||
"babel-preset-react-app": "^3.1.1",
|
||||
"babel-runtime": "6.26.0",
|
||||
"bizcharts": "^3.5.5",
|
||||
"bundle-loader": "^0.5.6",
|
||||
"case-sensitive-paths-webpack-plugin": "2.1.1",
|
||||
"chalk": "1.1.3",
|
||||
"classnames": "^2.2.5",
|
||||
"clipboard": "^2.0.4",
|
||||
"codemirror": "^5.46.0",
|
||||
"connected-react-router": "4.4.1",
|
||||
"css-loader": "0.28.7",
|
||||
"dotenv": "4.0.0",
|
||||
"dotenv-expand": "4.2.0",
|
||||
"echarts": "^4.2.0-rc.2",
|
||||
"editor.md": "^1.5.0",
|
||||
"eslint": "4.10.0",
|
||||
"eslint-config-react-app": "^2.1.0",
|
||||
"eslint-loader": "1.9.0",
|
||||
"eslint-plugin-flowtype": "2.39.1",
|
||||
"eslint-plugin-import": "2.8.0",
|
||||
"eslint-plugin-jsx-a11y": "5.1.1",
|
||||
"eslint-plugin-react": "7.4.0",
|
||||
"extract-text-webpack-plugin": "3.0.2",
|
||||
"file-loader": "1.1.5",
|
||||
"fs-extra": "3.0.1",
|
||||
"html-webpack-plugin": "2.29.0",
|
||||
"immutability-helper": "^2.6.6",
|
||||
"install": "^0.12.2",
|
||||
"jest": "20.0.4",
|
||||
"js-base64": "^2.5.1",
|
||||
"katex": "^0.11.1",
|
||||
"lodash": "^4.17.5",
|
||||
"loglevel": "^1.6.1",
|
||||
"material-ui": "^1.0.0-beta.40",
|
||||
"md5": "^2.2.1",
|
||||
"moment": "^2.23.0",
|
||||
"monaco-editor": "0.13",
|
||||
"monaco-editor-webpack-plugin": "^1.8.1",
|
||||
"npm": "^6.10.1",
|
||||
"numeral": "^2.0.6",
|
||||
"object-assign": "4.1.1",
|
||||
"postcss-flexbugs-fixes": "3.2.0",
|
||||
"postcss-loader": "2.0.8",
|
||||
"promise": "8.0.1",
|
||||
"prop-types": "^15.6.1",
|
||||
"qs": "^6.6.0",
|
||||
"quill": "^1.3.7",
|
||||
"quill-delta-to-html": "^0.11.0",
|
||||
"raf": "3.4.0",
|
||||
"rc-form": "^2.1.7",
|
||||
"rc-pagination": "^1.16.2",
|
||||
"rc-rate": "^2.4.0",
|
||||
"rc-select": "^8.0.12",
|
||||
"rc-tree": "^1.7.11",
|
||||
"rc-upload": "^2.5.1",
|
||||
"react": "^16.9.0",
|
||||
"react-beautiful-dnd": "^10.0.4",
|
||||
"react-codemirror": "^1.0.0",
|
||||
"react-codemirror2": "^6.0.0",
|
||||
"react-color": "^2.18.0",
|
||||
"react-content-loader": "^3.1.1",
|
||||
"react-cookies": "^0.1.1",
|
||||
"react-dev-utils": "^5.0.0",
|
||||
"react-dom": "^16.9.0",
|
||||
"react-hot-loader": "^4.0.0",
|
||||
"react-infinite-scroller": "^1.2.4",
|
||||
"react-loadable": "^5.3.1",
|
||||
"react-monaco-editor": "^0.25.1",
|
||||
"react-player": "^1.11.1",
|
||||
"react-redux": "5.0.7",
|
||||
"react-router": "^4.2.0",
|
||||
"react-router-dom": "^4.2.2",
|
||||
"react-split-pane": "^0.1.89",
|
||||
"react-url-query": "^1.4.0",
|
||||
"redux": "^4.0.0",
|
||||
"redux-thunk": "2.3.0",
|
||||
"rsuite": "^4.0.1",
|
||||
"sass-loader": "^7.3.1",
|
||||
"scroll-into-view": "^1.12.3",
|
||||
"source-map-support": "^0.5.16",
|
||||
"showdown": "^1.9.1",
|
||||
"showdown-katex": "^0.6.0",
|
||||
"store": "^2.0.12",
|
||||
"style-loader": "0.19.0",
|
||||
"styled-components": "^4.1.3",
|
||||
"sw-precache-webpack-plugin": "0.11.4",
|
||||
"url-loader": "0.6.2",
|
||||
"webpack": "3.8.1",
|
||||
"webpack-dev-server": "2.9.4",
|
||||
"webpack-manifest-plugin": "1.3.2",
|
||||
"webpack-parallel-uglify-plugin": "^1.1.0",
|
||||
"whatwg-fetch": "2.0.3",
|
||||
"wrap-md-editor": "^0.2.20"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node --max_old_space_size=20000 scripts/start.js",
|
||||
"build": "node --max_old_space_size=15360 scripts/build.js",
|
||||
"concat": "node scripts/concat.js",
|
||||
"gen_stats": "NODE_ENV=production webpack --profile --config=./config/webpack.config.prod.js --json > stats.json",
|
||||
"ana": "webpack-bundle-analyzer ./stats.json",
|
||||
"analyze": "npm run build -- --stats && webpack-bundle-analyzer build/bundle-stats.json",
|
||||
"analyz": "NODE_ENV=production npm_config_report=true npm run build"
|
||||
},
|
||||
"jest": {
|
||||
"collectCoverageFrom": [
|
||||
"src/**/*.{js,jsx,mjs}"
|
||||
],
|
||||
"setupFiles": [
|
||||
"<rootDir>/config/polyfills.js"
|
||||
],
|
||||
"testMatch": [
|
||||
"<rootDir>/src/**/__tests__/**/*.{js,jsx,mjs}",
|
||||
"<rootDir>/src/**/?(*.)(spec|test).{js,jsx,mjs}"
|
||||
],
|
||||
"testEnvironment": "node",
|
||||
"testURL": "http://localhost",
|
||||
"transform": {
|
||||
"^.+\\.(js|jsx|mjs)$": "<rootDir>/node_modules/babel-jest",
|
||||
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
|
||||
"^(?!.*\\.(js|jsx|mjs|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
|
||||
},
|
||||
"transformIgnorePatterns": [
|
||||
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs)$"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"^react-native$": "react-native-web"
|
||||
},
|
||||
"moduleFileExtensions": [
|
||||
"web.js",
|
||||
"mjs",
|
||||
"js",
|
||||
"json",
|
||||
"web.jsx",
|
||||
"jsx",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
"react",
|
||||
"react-app"
|
||||
],
|
||||
"plugins": [
|
||||
[
|
||||
"import",
|
||||
{
|
||||
"libraryName": "antd",
|
||||
"libraryDirectory": "lib",
|
||||
"style": "css"
|
||||
},
|
||||
"ant"
|
||||
],
|
||||
"syntax-dynamic-import"
|
||||
]
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"proxy": "http://localhost:3000",
|
||||
"port": "3007",
|
||||
"devDependencies": {
|
||||
"@babel/runtime": "7.0.0-beta.51",
|
||||
"babel-plugin-import": "^1.11.0",
|
||||
"compression-webpack-plugin": "^1.1.12",
|
||||
"concat": "^1.0.3",
|
||||
"happypack": "^5.0.1",
|
||||
"mockjs": "^1.1.0",
|
||||
"node-sass": "^4.12.0",
|
||||
"reqwest": "^2.0.5",
|
||||
"webpack-bundle-analyzer": "^3.0.3",
|
||||
"webpack-parallel-uglify-plugin": "^1.1.0"
|
||||
}
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
其他的文档位置:
|
||||
/educoder/public/react/public/js/readme.txt 关于js_min_all
|
||||
/educoder/educoder/public/react/scripts/readme-cdn.txt 关于CDN
|
||||
/educoder/public/react/src/modules/page/readme.txt 关于TPI
|
||||
/educoder/public/editormd/lib/readme-marked.txt 关于md编辑器 marked.js
|
||||
|
||||
1、 安装node v6.9.x;此安装包含了node和npm。
|
||||
|
||||
2、 安装cnpm(命令行): npm install -g cnpm --registry=https://registry.npm.taobao.org
|
||||
|
||||
3、 安装依赖的js库(public/react目录下<即项目package.json所在目录>,开启命令行): cnpm install
|
||||
|
||||
4、 如果你的ruby服务使用的是3000端口,则需要在package.json中修改"port"参数的值
|
||||
|
||||
5、 启动服务(命令行-目录同3): npm start
|
||||
|
||||
6、 build初始化 npm run build
|
||||
|
||||
注意:
|
||||
1、cnpm install 之前先需要修改下ruby mine的一个settings,防止ruby mine对node_modules目录里的内容建索引(详情见线上文档-react开发环境搭建)
|
||||
|
||||
|
||||
线上文档-react开发环境搭建 地址: https://www.trustie.net/boards/6862/topics/46425
|
||||
|
||||
2、package.json中配置:
|
||||
"proxy": "http://localhost:3000",
|
||||
"port": "3007"
|
||||
|
||||
目前暂时必须写为和上面的一样,ruby服务端口为3000,node服务端口为3007;当当前端口为3007时,程序会将axios发出的请求转到localhost:3000上,进行跨域请求。
|
||||
|
||||
3、静态js加载问题:
|
||||
editormd源码改动,注释掉了564行 加载codemirror/codemirror.min的js代码。因为codemirror 已经加载了,codemirror对象会带有插件,重复加载会覆盖全局codemirror对象,使得之前加载的插件失效
|
||||
|
||||
----------------------------------------------------------------------------------------------
|
||||
|
||||
React开发相关知识点
|
||||
需要了解的ES6的知识 https://www.trustie.net/boards/6862/topics/46427
|
||||
|
||||
----------------------------------------------------------------------------------------------
|
||||
|
||||
新加入的lib有: axios、material-ui、lodash、classnames、moment、immutability-helper
|
||||
rc-tree、rc-form 、rc-rate、rc-pagination、rc-select 、showdown
|
||||
考虑替代删除确认弹出框的组件http://react-component.github.io/tooltip/examples/onVisibleChange.html
|
||||
|
||||
|
||||
----------------------------------------------------------------------------------------------
|
||||
TPI State整理 START
|
||||
----------------------------------------------------------------------------------------------
|
||||
TPIContextProvider 详情接口的所有state
|
||||
|
||||
Index.js
|
||||
taskListLoading
|
||||
challenges
|
||||
challengesDrawerOpen
|
||||
|
||||
MainContentContainer.js
|
||||
repositoryCode: '',
|
||||
currentPath: '', // 当前所选的path,可能是一个只读的path(只读path的话,challenge.athIndex为-1)
|
||||
isEditablePath // 是否是可以编辑的path()
|
||||
|
||||
open: false, // 繁忙等级等提示用Dialog, TODO 考虑重构封装到根组件
|
||||
gameBuilding: false, // 评测中标志
|
||||
codeStatus: 2, // 0 已修改 1 保存中 2 已保存 3 保存失败
|
||||
|
||||
codeLoading: false, // code加载中
|
||||
resetCodeDialogOpen: false, // TODO考虑重构封装到根组件
|
||||
resetPassedCodeDialogOpen: false, // TODO考虑重构封装到根组件
|
||||
|
||||
|
||||
LeftViewContainer.js
|
||||
tabIndex: 0, 页签index
|
||||
dialogOpen: false,
|
||||
gameAnswer: '', 答案
|
||||
snackbarOpen: false,
|
||||
comments: [], 评论
|
||||
comment_count_without_reply: 0, 评论数量 TODO 和详情接口字段重复
|
||||
// 默认pageSize为10
|
||||
currentPage: 1, 评论分页
|
||||
loadingComments: true, 评论加载中
|
||||
gotNewReply: false, 新的回复
|
||||
|
||||
CodeRepositoryViewContainer.js
|
||||
drawerOpen: false,
|
||||
loadingFirstRepoFiles: false, drawer里的loading状态
|
||||
fileTreeData: "", 文件树
|
||||
codeRepositoryViewExpanded: false, 展开状态
|
||||
|
||||
|
||||
CodeEvaluateView.js
|
||||
testSetsInitedArray: testSetsExpandedArrayInitVal.slice(0), 测试集是否初始化标志
|
||||
evaluateViewExpanded: false,
|
||||
tabIndex: 1, 页签index
|
||||
----------------------------------------------------------------------------------------------
|
||||
TPI State整理 END
|
||||
----------------------------------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------------------------------
|
||||
重要:TPI实现时修改的js库的记录 START
|
||||
----------------------------------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------------------------------
|
||||
重要:TPI实现时修改的js库的记录 END
|
||||
----------------------------------------------------------------------------------------------
|
||||
create_kindeditor.js __isR 表示是react环境,react环境下采用事件通知react组件来处理
|
||||
if (window['__isR'] === true) {
|
||||
$(document).trigger("onReply", { commentContent:tContents, id:id, editor:params.editor } );
|
||||
} else {
|
||||
params.form.submit();
|
||||
}
|
||||
|
||||
editormd.min.js 直接注释掉了codemirror.min的加载,应该改成有codeMirror了则不加载
|
||||
// codemirror 已经加载了,codemirror会有插件,重复加载会使得之前加载的插件失效
|
||||
// editormd.loadScript(loadPath + "codemirror/codemirror.min", function() {
|
||||
对应提交项
|
||||
Revision: 73d95ce266d5d7e55a3a88d08d1247b3a08c7caf
|
||||
Date: 2018/4/2 16:12:21
|
||||
Message: 切下一题时更新左侧editormd里的内容,更新右侧codemirror内容。
|
||||
|
||||
js_min_all.js 最后面手动加入了若干js代码,还没做分离、再合并处理 date:180507
|
||||
is_cdn_link tpi_html_show方法
|
||||
|
||||
|
||||
|
||||
|
||||
----------------------------------------------------------------------------------------------
|
||||
TPM使用react实现的利弊 START
|
||||
----------------------------------------------------------------------------------------------
|
||||
1、全部使用react重写
|
||||
做法:第一屏使用新接口,之前的js脚本还是继续使用,有必要的话(需要局部刷新的),将部分jquery实现改为react实现
|
||||
利:
|
||||
tpi中评论组件、文件树组件方便复用
|
||||
js、css库管理方便
|
||||
|
||||
暂时不依赖于react的状态管理
|
||||
之前的ajax请求还是可以暂时复用
|
||||
|
||||
|
||||
弊:
|
||||
接口评估?
|
||||
rails模板要改成jsx语法
|
||||
头部功能区域、底部静态链接区域会存在重复代码 : react版和非react版
|
||||
codemirror等组件的使用会不会有问题?
|
||||
|
||||
学习成本
|
||||
|
||||
!!目前决定,新页面或者评论组件所在页面使用react实现
|
||||
----------------------------------------------------------------------------------------------
|
||||
TPM使用react实现的利弊 END
|
||||
----------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
其他方式:comments组件build到新入口后,将代码copy到rails页面
|
||||
----------------------------------------------------------------------------------------------
|
||||
不错的库 START
|
||||
----------------------------------------------------------------------------------------------
|
||||
https://livicons.com/icons-original -- 收费 动画icon
|
||||
https://github.com/maxwellito/vivus -- 让SVG标签动起来
|
||||
http://ianlunn.github.io/Hover/ -- hover 动画
|
||||
https://github.com/legomushroom/mojs
|
||||
https://github.com/juliangarnier/anime --js动画
|
||||
https://codepen.io/juliangarnier/pen/gmOwJX
|
||||
https://github.com/daneden/animate.css
|
||||
|
||||
|
||||
A responsive tour snippet, with a step-by-step guide(onboarding) to help users understand how to use your website.
|
||||
|
||||
https://github.com/sorich87/bootstrap-tour
|
||||
https://github.com/linkedin/hopscotch
|
||||
https://github.com/Robophil/Product-Tour
|
||||
|
||||
code editor
|
||||
https://microsoft.github.io/monaco-editor/
|
|
@ -1,251 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'production';
|
||||
process.env.NODE_ENV = 'production';
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
const fs = require('fs-extra');
|
||||
const webpack = require('webpack');
|
||||
const config = require('../config/webpack.config.prod');
|
||||
const paths = require('../config/paths');
|
||||
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
||||
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
|
||||
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
|
||||
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
|
||||
const printBuildError = require('react-dev-utils/printBuildError');
|
||||
|
||||
var CombinedStream = require('combined-stream');
|
||||
var fs2 = require('fs');
|
||||
|
||||
const measureFileSizesBeforeBuild =
|
||||
FileSizeReporter.measureFileSizesBeforeBuild;
|
||||
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
|
||||
const useYarn = fs.existsSync(paths.yarnLockFile);
|
||||
|
||||
// These sizes are pretty large. We'll warn for bundles exceeding them.
|
||||
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
|
||||
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
|
||||
|
||||
// Warn and crash if required files are missing
|
||||
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function removeExceptGitDir(dir) {
|
||||
// readdirSync
|
||||
const list = fs2.readdirSync(dir)
|
||||
// if (err) return done(err);
|
||||
var pending = list.length;
|
||||
// if (!pending) return done(null, results);
|
||||
list.forEach(function(file) {
|
||||
if (file.indexOf('.git') == -1) {
|
||||
file = path.resolve(dir, file);
|
||||
fs.removeSync(file)
|
||||
}
|
||||
});
|
||||
}
|
||||
// First, read the current file sizes in build directory.
|
||||
// This lets us display how much they changed later.
|
||||
measureFileSizesBeforeBuild(paths.appBuild)
|
||||
.then(previousFileSizes => {
|
||||
// Remove all content but keep the directory so that
|
||||
// if you're in it, you don't end up in Trash
|
||||
// fs.emptyDirSync(paths.appBuild);
|
||||
console.log('removeExceptGitDir')
|
||||
|
||||
removeExceptGitDir(paths.appBuild)
|
||||
|
||||
console.log('copyPublicFolder')
|
||||
// Merge with the public folder
|
||||
copyPublicFolder();
|
||||
// Start the webpack build
|
||||
return build(previousFileSizes);
|
||||
})
|
||||
.then(
|
||||
({ stats, previousFileSizes, warnings }) => {
|
||||
if (warnings.length) {
|
||||
console.log(chalk.yellow('Compiled with warnings.\n'));
|
||||
console.log(warnings.join('\n\n'));
|
||||
console.log(
|
||||
'\nSearch for the ' +
|
||||
chalk.underline(chalk.yellow('keywords')) +
|
||||
' to learn more about each warning.'
|
||||
);
|
||||
console.log(
|
||||
'To ignore, add ' +
|
||||
chalk.cyan('// eslint-disable-next-line') +
|
||||
' to the line before.\n'
|
||||
);
|
||||
} else {
|
||||
console.log(chalk.green('Compiled successfully.\n'));
|
||||
}
|
||||
|
||||
console.log('File sizes after gzip:\n');
|
||||
printFileSizesAfterBuild(
|
||||
stats,
|
||||
previousFileSizes,
|
||||
paths.appBuild,
|
||||
WARN_AFTER_BUNDLE_GZIP_SIZE,
|
||||
WARN_AFTER_CHUNK_GZIP_SIZE
|
||||
);
|
||||
console.log();
|
||||
|
||||
const appPackage = require(paths.appPackageJson);
|
||||
const publicUrl = paths.publicUrl;
|
||||
const publicPath = config.output.publicPath;
|
||||
const buildFolder = path.relative(process.cwd(), paths.appBuild);
|
||||
printHostingInstructions(
|
||||
appPackage,
|
||||
publicUrl,
|
||||
publicPath,
|
||||
buildFolder,
|
||||
useYarn
|
||||
);
|
||||
},
|
||||
err => {
|
||||
console.log(chalk.red('Failed to compile.\n'));
|
||||
printBuildError(err);
|
||||
process.exit(1);
|
||||
}
|
||||
);
|
||||
|
||||
// Create the production build and print the deployment instructions.
|
||||
function build(previousFileSizes) {
|
||||
console.log('Creating an optimized production build...');
|
||||
|
||||
let compiler = webpack(config);
|
||||
return new Promise((resolve, reject) => {
|
||||
compiler.run((err, stats) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
const messages = formatWebpackMessages(stats.toJson({}, true));
|
||||
if (messages.errors.length) {
|
||||
// Only keep the first error. Others are often indicative
|
||||
// of the same problem, but confuse the reader with noise.
|
||||
if (messages.errors.length > 1) {
|
||||
messages.errors.length = 1;
|
||||
}
|
||||
return reject(new Error(messages.errors.join('\n\n')));
|
||||
}
|
||||
if (
|
||||
process.env.CI &&
|
||||
(typeof process.env.CI !== 'string' ||
|
||||
process.env.CI.toLowerCase() !== 'false') &&
|
||||
messages.warnings.length
|
||||
) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'\nTreating warnings as errors because process.env.CI = true.\n' +
|
||||
'Most CI servers set it automatically.\n'
|
||||
)
|
||||
);
|
||||
return reject(new Error(messages.warnings.join('\n\n')));
|
||||
}
|
||||
|
||||
generateNewIndexJsp();
|
||||
|
||||
return resolve({
|
||||
stats,
|
||||
previousFileSizes,
|
||||
warnings: messages.warnings,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function copyPublicFolder() {
|
||||
fs.copySync(paths.appPublic, paths.appBuild, {
|
||||
dereference: true,
|
||||
filter: file => file !== paths.appHtml,
|
||||
});
|
||||
}
|
||||
|
||||
// 给build脚本增加的方法,对其生成的index.html做一些文本替换,以及cdn处理
|
||||
function generateNewIndexJsp() {
|
||||
// var combinedStream = CombinedStream.create();
|
||||
var filePath = paths.appBuild + '/index.html';
|
||||
// var htmlContent = fs2.createReadStream( filePath )
|
||||
|
||||
// stream没有replace方法
|
||||
// htmlContent = htmlContent.replace('/js/js_min_all.js', '/react/build/js/js_min_all.js')
|
||||
// htmlContent = htmlContent.replace('/css/css_min_all.css', '/react/build/css/css_min_all.css')
|
||||
|
||||
// combinedStream.append(htmlContent);
|
||||
// combinedStream.pipe(fs2.createWriteStream( filePath ));
|
||||
|
||||
var outputPath = paths.appBuild + '/../../../index.html'
|
||||
fs2.readFile(filePath, 'utf8', function (err,data) {
|
||||
if (err) {
|
||||
return console.log(err);
|
||||
}
|
||||
const newVersion = '1.1.1'
|
||||
let cdnHost = 'https://shixun.educoder.net'
|
||||
cdnHost = 'https://ali-cdn.educoder.net'
|
||||
cdnHost = ''
|
||||
|
||||
|
||||
var mainRegex = /<script type="text\/javascript" src="\/forgeplus-react\/build\/.\/static\/js\/main.([a-zA-Z0-9]{8,}).js"><\/script>/
|
||||
var matchResult = data.match(mainRegex)
|
||||
var code = `
|
||||
<script>
|
||||
(function() {
|
||||
var _host = '/forgeplus-react/build/'
|
||||
/**/
|
||||
if (window.location.host == 'pre-newweb.educoder.net') {
|
||||
_host = 'https://testali-cdn.educoder.net/react/build/'
|
||||
} else if (window.location.host == 'www.educoder.net') {
|
||||
_host = 'https://ali-cdn.educoder.net/react/build/'
|
||||
}
|
||||
document.write('<script type="text/javascript" src="' + _host + 'js/js_min_all.js"><\\/script>');
|
||||
document.write('<script type="text/javascript" src="' + _host + 'static/js/main.${matchResult[1]}.js"><\\/script>');
|
||||
})()
|
||||
</script>
|
||||
`
|
||||
var jsMinAllRegex = /<script type="text\/javascript" src="\/js\/js_min_all.js"><\/script>/
|
||||
// <script type="text/javascript" src="/js/js_min_all.js"></script>
|
||||
var result = data
|
||||
.replace(jsMinAllRegex, code)
|
||||
// .replace('/js/js_min_all.js', `${cdnHost}/react/build/js/js_min_all.js?v=${newVersion}`)
|
||||
// .replace('/js/js_min_all_2.js', `${cdnHost}/react/build/js/js_min_all_2.js?v=${newVersion}`)
|
||||
|
||||
// ${cdnHost} 加了cdn后,这个文件里的字体文件加载会有跨域的报错 ../fonts/fontawesome-webfont.eot
|
||||
// TODO tpi 评测结果关闭也使用了fontawesome
|
||||
.replace('/css/css_min_all.css', `${cdnHost}/react/build/css/css_min_all.css?v=${newVersion}`)
|
||||
.replace('/css/iconfont.css', `${cdnHost}/react/build/css/iconfont.css?v=${newVersion}`)
|
||||
.replace(/\/js\/create_kindeditor.js/g, `${cdnHost}/react/build/js/create_kindeditor.js?v=${newVersion}`)
|
||||
|
||||
.replace(mainRegex, '')
|
||||
// .replace('/react/build/./static/css/main', `${cdnHost}/react/build/./static/css/main`)
|
||||
// .replace('/react/build/./static/js/main', `${cdnHost}/react/build/./static/js/main`)
|
||||
|
||||
// .replace(/https:\/\/testeduplus2.educoder.net/g, '');
|
||||
// .replace(/http:\/\/testbdweb.educoder.net/g, '');
|
||||
|
||||
// .replace('/css/css_min_all.css', '/react/build/css/css_min_all.css');
|
||||
|
||||
fs2.writeFile(outputPath, result, 'utf8', function (err) {
|
||||
if (err) return console.log(err);
|
||||
// commitAndPush();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function commitAndPush() {
|
||||
var exec = require('child_process').exec;
|
||||
function puts(error, stdout, stderr) { console.log(stdout) }
|
||||
var options = {cwd:"./build"};
|
||||
exec("git status && git commit -am 'b' && git push", options, puts);
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
var fs = require('fs');
|
||||
var uglify = require("uglify-js");
|
||||
var path = require('path');
|
||||
var concat = require('concat')
|
||||
|
||||
var results = [];
|
||||
var walk = function(dir, done) {
|
||||
|
||||
fs.readdir(dir, function(err, list) {
|
||||
console.log(list)
|
||||
if (err) return done(err);
|
||||
var pending = list.length;
|
||||
if (!pending) return done(null, results);
|
||||
list.forEach(function(file) {
|
||||
file = path.resolve(dir, file);
|
||||
fs.stat(file, function(err, stat) {
|
||||
if (stat && stat.isDirectory()) {
|
||||
walk(file, function(err, res) {
|
||||
// results = results.concat(res);
|
||||
if (!--pending) done(null, results);
|
||||
});
|
||||
} else {
|
||||
results.push(file);
|
||||
if (!--pending) done(null, results);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 需要输出文件名数组时改为true
|
||||
var jsDir = './public/js/';
|
||||
var cssDir = './public/css'
|
||||
|
||||
// true &&
|
||||
false &&
|
||||
walk(cssDir, function() {
|
||||
console.log('results', results.length, results)
|
||||
})
|
||||
// return;
|
||||
|
||||
// ----------------------------------------------------------------------------- CSS
|
||||
var cssResults = [
|
||||
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\css\\edu-common.css',
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\css\\edu-public.css',
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\css\\taskstyle.css' ,
|
||||
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\css\\font-awesome.css',
|
||||
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\css\\editormd.min.css',
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\css\\merge.css',
|
||||
|
||||
]
|
||||
concat(cssResults, './public/css/css_min_all.css')
|
||||
|
||||
return;
|
||||
|
||||
// ----------------------------------------------------------------------------- JS
|
||||
var _results = [
|
||||
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\js\\jquery-1.8.3.min.js',
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\js\\editormd\\underscore.min.js',
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\js\\editormd\\marked.min.js',
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\js\\editormd\\prettify.min.js',
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\js\\editormd\\raphael.min.js',
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\js\\editormd\\sequence-diagram.min.js',
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\js\\editormd\\flowchart.min.js',
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\js\\editormd\\jquery.flowchart.min.js',
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\js\\editormd\\editormd.min.js',
|
||||
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\js\\codemirror\\codemirror.js',
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\js\\codemirror\\mode\\javascript.js',
|
||||
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\js\\diff_match_patch.js',
|
||||
|
||||
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\js\\merge.js',
|
||||
|
||||
'D:\\Code\\trustieplus\\public\\react\\public\\js\\edu_tpi.js',
|
||||
|
||||
]
|
||||
|
||||
concat(_results, './public/js/js_min_all.js')
|
||||
|
||||
// var uglified = uglify.minify(['./public/js/merge.js']);
|
||||
// console.log('uglified', uglified)
|
||||
// fs.writeFile('concat.min.js', uglified.code, function (err){
|
||||
// if(err) {
|
||||
// console.log(err);
|
||||
// } else {
|
||||
// console.log("Script generated and saved:", 'concat.min.js');
|
||||
// }
|
||||
// });
|
||||
|
||||
|
||||
// var uglified = uglify.minify(['file1.js', 'file2.js', 'file3.js']);
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
var fs2 = require('fs');
|
||||
|
||||
function generateNewIndexJsp() {
|
||||
|
||||
var filePath = './build/index.html';
|
||||
|
||||
var outputPath = filePath
|
||||
fs2.readFile(filePath, 'utf8', function (err,data) {
|
||||
if (err) {
|
||||
return console.log(err);
|
||||
}
|
||||
var result = data
|
||||
.replace('/js/create_kindeditor.js', '/react/build/js/create_kindeditor.js')
|
||||
.replace(/https:\/\/testeduplus2.educoder.net/g, '');
|
||||
// .replace(/http:\/\/testbdweb.educoder.net/g, '');
|
||||
|
||||
.replace('/css/css_min_all.css', '/react/build/css/css_min_all.css');
|
||||
|
||||
fs2.writeFile(outputPath, result, 'utf8', function (err) {
|
||||
if (err) return console.log(err);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
目前是判断域名的方式动态访问对应的cdn资源
|
||||
静态资源处理在build.js中,如下代码:
|
||||
if (window.location.host == 'pre-newweb.educoder.net') {
|
||||
_host = 'https://testali-cdn.educoder.net/react/build/'
|
||||
} else if (window.location.host == 'www.educoder.net') {
|
||||
_host = 'https://ali-cdn.educoder.net/react/build/'
|
||||
}
|
||||
|
||||
只对预上线和正式版做了处理
|
||||
|
||||
动态的chunk资源处理在public-path.js中,如下代码:
|
||||
if ( window.location.host == 'pre-newweb.educoder.net') {
|
||||
__webpack_public_path__ = 'https://testali-cdn.educoder.net/react/build/'
|
||||
} else if ( window.location.host == 'www.educoder.net') {
|
||||
__webpack_public_path__ = 'https://ali-cdn.educoder.net/react/build/'
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'development';
|
||||
process.env.NODE_ENV = 'development';
|
||||
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
|
||||
const fs = require('fs');
|
||||
const chalk = require('chalk');
|
||||
const webpack = require('webpack');
|
||||
const WebpackDevServer = require('webpack-dev-server');
|
||||
const clearConsole = require('react-dev-utils/clearConsole');
|
||||
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
||||
const {
|
||||
choosePort,
|
||||
createCompiler,
|
||||
prepareProxy,
|
||||
prepareUrls,
|
||||
} = require('react-dev-utils/WebpackDevServerUtils');
|
||||
const openBrowser = require('react-dev-utils/openBrowser');
|
||||
const paths = require('../config/paths');
|
||||
const config = require('../config/webpack.config.dev');
|
||||
const createDevServerConfig = require('../config/webpackDevServer.config');
|
||||
|
||||
const useYarn = fs.existsSync(paths.yarnLockFile);
|
||||
const isInteractive = process.stdout.isTTY;
|
||||
|
||||
const portSetting = require(paths.appPackageJson).port
|
||||
if ( portSetting ) {
|
||||
process.env.port = portSetting
|
||||
}
|
||||
|
||||
// Warn and crash if required files are missing
|
||||
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Tools like Cloud9 rely on this.
|
||||
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3007;
|
||||
const HOST = process.env.HOST || '0.0.0.0';
|
||||
|
||||
if (process.env.HOST) {
|
||||
console.log(
|
||||
chalk.cyan(
|
||||
`Attempting to bind to HOST environment variable: ${chalk.yellow(
|
||||
chalk.bold(process.env.HOST)
|
||||
)}`
|
||||
)
|
||||
);
|
||||
console.log(
|
||||
`If this was unintentional, check that you haven't mistakenly set it in your shell.`
|
||||
);
|
||||
console.log(`Learn more here: ${chalk.yellow('http://bit.ly/2mwWSwH')}`);
|
||||
console.log();
|
||||
}
|
||||
|
||||
// We attempt to use the default port but if it is busy, we offer the user to
|
||||
// run on a different port. `choosePort()` Promise resolves to the next free port.
|
||||
choosePort(HOST, DEFAULT_PORT)
|
||||
.then(port => {
|
||||
if (port == null) {
|
||||
// We have not found a port.
|
||||
return;
|
||||
}
|
||||
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
|
||||
const appName = require(paths.appPackageJson).name;
|
||||
const urls = prepareUrls(protocol, HOST, port);
|
||||
// Create a webpack compiler that is configured with custom messages.
|
||||
const compiler = createCompiler(webpack, config, appName, urls, useYarn);
|
||||
// Load proxy config
|
||||
const proxySetting = require(paths.appPackageJson).proxy;
|
||||
console.log('-------------------------proxySetting:', proxySetting)
|
||||
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
|
||||
// Serve webpack assets generated by the compiler over a web sever.
|
||||
const serverConfig = createDevServerConfig(
|
||||
proxyConfig,
|
||||
urls.lanUrlForConfig
|
||||
);
|
||||
const devServer = new WebpackDevServer(compiler, serverConfig);
|
||||
// Launch WebpackDevServer.
|
||||
devServer.listen(port, HOST, err => {
|
||||
if (err) {
|
||||
return console.log(err);
|
||||
}
|
||||
if (isInteractive) {
|
||||
clearConsole();
|
||||
}
|
||||
console.log(chalk.cyan('Starting the development server...\n'));
|
||||
openBrowser(urls.localUrlForBrowser);
|
||||
});
|
||||
|
||||
['SIGINT', 'SIGTERM'].forEach(function(sig) {
|
||||
process.on(sig, function() {
|
||||
devServer.close();
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
if (err && err.message) {
|
||||
console.log(err.message);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
|
@ -1,27 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'test';
|
||||
process.env.NODE_ENV = 'test';
|
||||
process.env.PUBLIC_URL = '';
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
|
||||
const jest = require('jest');
|
||||
const argv = process.argv.slice(2);
|
||||
|
||||
// Watch unless on CI or in coverage mode
|
||||
if (!process.env.CI && argv.indexOf('--coverage') < 0) {
|
||||
argv.push('--watch');
|
||||
}
|
||||
|
||||
|
||||
jest.run(argv);
|
|
@ -1,107 +0,0 @@
|
|||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #222;
|
||||
height: 150px;
|
||||
padding: 20px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-title {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.App-intro {
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 控制md编辑器列行的宽度
|
||||
见 codermirror maybeUpdateLineNumberWidth方法
|
||||
*/
|
||||
.editormd .CodeMirror-linenumbers {
|
||||
padding: 0;
|
||||
}
|
||||
.editormd-html-preview hr, .editormd-preview-container hr {
|
||||
/* 颜色加深 */
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
|
||||
/* 重置掉antd的一些样式 */
|
||||
html, body {
|
||||
-webkit-font-smoothing: auto !important;
|
||||
}
|
||||
|
||||
.ant-progress-textyes {
|
||||
color: #52c41a;
|
||||
}
|
||||
.ant-progress-textno{
|
||||
color: #f5222d;
|
||||
}
|
||||
/* md多空格 */
|
||||
.markdown-body p {
|
||||
white-space: pre-wrap;
|
||||
font-size: 16px!important
|
||||
}
|
||||
.markdown-body > p {
|
||||
line-height: 25px;
|
||||
}
|
||||
/* https://www.educoder.net/courses/2346/group_homeworks/34405/question */
|
||||
.renderAsHtml.markdown-body p {
|
||||
white-space: inherit;
|
||||
}
|
||||
/* resize */
|
||||
.editormd .CodeMirror {
|
||||
border-right: none !important;
|
||||
}
|
||||
.editormd-preview {
|
||||
border-left: 1px solid rgb(221, 221, 221);
|
||||
/* 某些情况下,被cm盖住了 */
|
||||
z-index: 99;
|
||||
}
|
||||
/* 图片点击放大的场景,隐藏图片链接 */
|
||||
.editormd-image-click-expand .editormd-image-dialog {
|
||||
height: 234px !important;
|
||||
}
|
||||
.editormd-image-click-expand .editormd-image-dialog .image-link {
|
||||
display: none;
|
||||
}
|
||||
/* 解决鼠标框选时,左边第一列没高亮的问题 */
|
||||
.CodeMirror .CodeMirror-lines pre.CodeMirror-line, .CodeMirror .CodeMirror-lines pre.CodeMirror-line-like {
|
||||
padding: 0 12px ;
|
||||
}
|
||||
|
||||
|
||||
/* antd扩展 */
|
||||
.formItemInline.ant-form-item {
|
||||
display: flex;
|
||||
}
|
||||
.formItemInline .ant-form-item-control-wrapper {
|
||||
flex: 1;
|
||||
}
|
||||
/* AutoComplete placeholder 不显示的问题 */
|
||||
.ant-select-auto-complete.ant-select .ant-select-selection__placeholder {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
|
||||
/* 兼容性 */
|
||||
/* 火狐有滚动条时高度问题 */
|
||||
@-moz-document url-prefix() {
|
||||
.newContainers {
|
||||
min-height: calc(100% - 60px) !important;
|
||||
}
|
||||
}
|
|
@ -1,909 +0,0 @@
|
|||
import React, {Component} from 'react';
|
||||
import './public-path';
|
||||
import logo from './logo.svg';
|
||||
import './App.css';
|
||||
import {ConfigProvider} from 'antd'
|
||||
import zhCN from 'antd/lib/locale-provider/zh_CN';
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
Route,
|
||||
Switch
|
||||
} from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
import '@icedesign/base/dist/ICEDesignBase.css';
|
||||
|
||||
import '@icedesign/base/index.scss';
|
||||
|
||||
import LoginDialog from './modules/login/LoginDialog';
|
||||
import Notcompletedysl from './modules/user/Notcompletedysl';
|
||||
import Trialapplicationysl from './modules/login/Trialapplicationysl';
|
||||
import Trialapplicationreview from './modules/user/Trialapplicationreview';
|
||||
import Addcourses from "./modules/courses/coursesPublic/Addcourses";
|
||||
import AccountProfile from "./modules/user/AccountProfile";
|
||||
import Accountnewprofile from './modules/user/Accountnewprofile';
|
||||
import Trialapplication from './modules/login/Trialapplication';
|
||||
import Certifiedprofessional from './modules/modals/Certifiedprofessional';
|
||||
import NotFoundPage from './NotFoundPage'
|
||||
|
||||
import Loading from './Loading'
|
||||
|
||||
import Loadable from 'react-loadable';
|
||||
|
||||
|
||||
import moment from 'moment'
|
||||
|
||||
import {MuiThemeProvider, createMuiTheme} from 'material-ui/styles';
|
||||
|
||||
// import './AppConfig'
|
||||
|
||||
import history from './history';
|
||||
|
||||
import {SnackbarHOC} from 'educoder'
|
||||
import {initAxiosInterceptors} from './AppConfig'
|
||||
import { Provider } from 'react-redux';
|
||||
import configureStore from './redux/stores/configureStore';
|
||||
// !!!tpi需要这个来加载css
|
||||
import {TPMIndexHOC} from './modules/tpm/TPMIndexHOC';
|
||||
const store = configureStore();
|
||||
|
||||
const theme = createMuiTheme({
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#4CACFF',
|
||||
contrastText: 'rgba(255, 255, 255, 0.87)'
|
||||
},
|
||||
secondary: {main: '#4CACFF'}, // #11cb5f This is just green.A700 as hex.
|
||||
},
|
||||
});
|
||||
//
|
||||
// const Trialapplication= Loadable({
|
||||
// loader: () =>import('./modules/login/Trialapplication'),
|
||||
// loading:Loading,
|
||||
// })
|
||||
//登入
|
||||
const EducoderLogin = Loadable({
|
||||
loader: () => import('./modules/login/EducoderLogin'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
|
||||
//微信登录
|
||||
const Otherlogin=Loadable({
|
||||
loader: () => import('./modules/login/Otherlogin'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
//微信登录
|
||||
const Loginqq=Loadable({
|
||||
loader: () => import('./modules/login/Loginqq'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
|
||||
const Otherloginstart=Loadable({
|
||||
loader: () => import('./modules/login/Otherloginstart'),
|
||||
loading: Loading,
|
||||
})
|
||||
const Otherloginsqq=Loadable({
|
||||
loader: () => import('./modules/login/Otherloginqq'),
|
||||
loading: Loading,
|
||||
})
|
||||
// const TestIndex = Loadable({
|
||||
// loader: () => import('./modules/test'),
|
||||
// loading: Loading,
|
||||
// })
|
||||
|
||||
const IndexWrapperComponent = Loadable({
|
||||
loader: () => import('./modules/page/IndexWrapper'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
const CommentComponent = Loadable({
|
||||
loader: () => import('./modules/comment/CommentContainer'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
// const TestMaterialDesignComponent = Loadable({
|
||||
// loader: () => import('./modules/test/md/TestMaterialDesign'),
|
||||
// loading: Loading,
|
||||
// })
|
||||
// const TestCodeMirrorComponent = Loadable({
|
||||
// loader: () => import('./modules/test/codemirror/TestCodeMirror'),
|
||||
// loading: Loading,
|
||||
// })
|
||||
|
||||
// const TestComponent = Loadable({
|
||||
// loader: () => import('./modules/test/TestRC'),
|
||||
// loading: Loading,
|
||||
// })
|
||||
// const TestUrlQueryComponent = Loadable({
|
||||
// loader: () => import('./modules/test/urlquery/TestUrlQuery'),
|
||||
// loading: Loading,
|
||||
// })
|
||||
|
||||
const TPMIndexComponent = Loadable({
|
||||
loader: () => import('./modules/tpm/TPMIndex'),
|
||||
loading: Loading,
|
||||
})
|
||||
const TPMShixunsIndexComponent = Loadable({
|
||||
loader: () => import('./modules/tpm/shixuns/ShixunsIndex'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
//实训课程(原实训路径)
|
||||
const ShixunPaths = Loadable({
|
||||
loader: () => import('./modules/paths/Index'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
//在线课堂
|
||||
const CoursesIndex = Loadable({
|
||||
loader: () => import('./modules/courses/Index'),
|
||||
loading: Loading,
|
||||
})
|
||||
const SearchPage = Loadable({
|
||||
loader: () => import('./search/SearchPage'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
// 课堂讨论
|
||||
// const BoardIndex = Loadable({
|
||||
// loader: () => import('./modules/courses/boards/BoardIndex'),
|
||||
// loading:Loading,
|
||||
// })
|
||||
|
||||
// //课堂普通作业&分组作业
|
||||
// const CoursesWorkIndex = Loadable({
|
||||
// loader: () => import('./modules/courses/busyWork/Index'),
|
||||
// loading:Loading,
|
||||
// })
|
||||
//
|
||||
|
||||
// const TPMShixunchildIndexComponent = Loadable({
|
||||
// loader: () => import('./modules/tpm/shixunchild/ShixunChildIndex'),
|
||||
// loading: Loading,
|
||||
// })
|
||||
|
||||
|
||||
// const TPMshixunfork_listIndexComponent = Loadable({
|
||||
// loader: () => import('./modules/tpm/shixunchild/Shixunfork_list'),
|
||||
// loading: Loading,
|
||||
// })
|
||||
|
||||
|
||||
const ForumsIndexComponent = Loadable({
|
||||
loader: () => import('./modules/forums/ForumsIndex'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
const ProjectIndex = Loadable({
|
||||
loader: () => import('./forge/Index'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
// trustie plus forum
|
||||
// const TPForumsIndexComponent = Loadable({
|
||||
// loader: () => import('./modules/tp-forums/TPForumsIndex'),
|
||||
// loading: Loading,
|
||||
// })
|
||||
|
||||
|
||||
// const TestPageComponent = Loadable({
|
||||
// loader: () => import('./modules/page/Index'),
|
||||
// loading: Loading,
|
||||
// })
|
||||
|
||||
|
||||
//新建实训
|
||||
const Newshixuns = Loadable({
|
||||
loader: () => import('./modules/tpm/newshixuns/Newshixuns'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
|
||||
//实训首页
|
||||
const ShixunsHome = Loadable({
|
||||
loader: () => import('./modules/home/shixunsHome'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
|
||||
const CompatibilityPageLoadable = Loadable({
|
||||
loader: () => import('./modules/common/CompatibilityPage'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
//403页面
|
||||
const Shixunauthority = Loadable({
|
||||
loader: () => import('./modules/403/Shixunauthority'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
|
||||
//404页面
|
||||
const Shixunnopage = Loadable({
|
||||
loader: () => import('./modules/404/Shixunnopage'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
//500页面
|
||||
const http500 = Loadable({
|
||||
loader: () => import('./modules/500/http500'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
// 登录注册
|
||||
const LoginRegisterPage = Loadable({
|
||||
loader: () => import('./modules/user/LoginRegisterPage'),
|
||||
loading: Loading,
|
||||
})
|
||||
const AccountPage = Loadable({
|
||||
loader: () => import('./modules/user/AccountPage'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
// 个人主页
|
||||
const UsersInfo = Loadable({
|
||||
loader: () => import('./modules/user/usersInfo/Infos'),
|
||||
loading: Loading,
|
||||
})
|
||||
const InfosIndex = Loadable({
|
||||
loader: () => import('./modules/user/usersInfo/InfosIndex'),
|
||||
loading: Loading,
|
||||
})
|
||||
// 题库
|
||||
const BanksIndex = Loadable({
|
||||
loader: () => import('./modules/user/usersInfo/banks/BanksIndex'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
|
||||
// 教学案例
|
||||
const MoopCases = Loadable({
|
||||
loader: () => import('./modules/moop_cases/index'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
// 兴趣页面
|
||||
const Interestpage = Loadable({
|
||||
loader: () => import('./modules/login/EducoderInteresse'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
//众包创新
|
||||
// const ProjectPackages=Loadable({
|
||||
// loader: () => import('./modules/projectPackages/ProjectPackageIndex'),
|
||||
// loading: Loading,
|
||||
// })
|
||||
|
||||
//竞赛
|
||||
const NewCompetitions=Loadable({
|
||||
loader: () => import('./modules/competitions/Competitions'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
//黑客松定制竞赛
|
||||
const Osshackathon=Loadable({
|
||||
loader: () => import('./modules/osshackathon/Osshackathon'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
const Messagerouting= Loadable({
|
||||
loader: () => import('./modules/message/js/Messagerouting'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
const Topicbank= Loadable({
|
||||
loader: () => import('./modules/topic_bank/Topic_bank'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
const Help = Loadable({
|
||||
loader: () => import('./modules/help/Help'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
const Ecs = Loadable({
|
||||
loader: () => import('./modules/ecs/Ecs'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
// 添加开发者社区
|
||||
const Developer = Loadable({
|
||||
loader: () => import('./modules/developer'),
|
||||
loading: Loading
|
||||
})
|
||||
// 试题库
|
||||
const Headplugselection = Loadable({
|
||||
loader: () => import('./modules/question/Question'),
|
||||
loading: Loading
|
||||
})
|
||||
|
||||
//试题库新建 //题库编辑
|
||||
const Questionitem_banks = Loadable({
|
||||
loader: () => import('./modules/question/Questionitem_banks'),
|
||||
loading: Loading
|
||||
})
|
||||
|
||||
//试卷库
|
||||
const Testpaperlibrary= Loadable({
|
||||
loader: () => import('./modules/testpaper/Testpaperlibrary'),
|
||||
loading: Loading
|
||||
})
|
||||
//试卷编辑
|
||||
const Paperlibraryeditid= Loadable({
|
||||
loader: () => import('./modules/testpaper/Paperlibraryeditid'),
|
||||
loading: Loading
|
||||
})
|
||||
//试卷查看
|
||||
const Paperlibraryseeid= Loadable({
|
||||
loader: () => import('./modules/testpaper/Paperlibraryseeid'),
|
||||
loading: Loading
|
||||
})
|
||||
//人工组卷
|
||||
const Paperreview= Loadable({
|
||||
loader: () => import('./modules/question/Paperreview'),
|
||||
loading: Loading
|
||||
})
|
||||
|
||||
//智能组卷
|
||||
const Integeneration= Loadable({
|
||||
loader: () => import('./modules/testpaper/Intecomponents'),
|
||||
loading: Loading
|
||||
})
|
||||
|
||||
// 学院统计
|
||||
const College = Loadable({
|
||||
loader: () => import('./college/College'),
|
||||
loading: Loading
|
||||
})
|
||||
|
||||
// 开发者编辑模块
|
||||
const NewOrEditTask = Loadable({
|
||||
loader: () => import('./modules/developer/newOrEditTask'),
|
||||
loading: Loading
|
||||
});
|
||||
// 学员学习
|
||||
const StudentStudy = Loadable({
|
||||
loader: () => import('./modules/developer/studentStudy'),
|
||||
loading: Loading
|
||||
});
|
||||
// 提交记录详情
|
||||
const RecordDetail = Loadable({
|
||||
loader: () => import('./modules/developer/recordDetail'),
|
||||
loading: Loading
|
||||
});
|
||||
// jupyter tpi
|
||||
const JupyterTPI = Loadable({
|
||||
loader: () => import('./modules/tpm/jupyter'),
|
||||
loading: Loading
|
||||
});
|
||||
// 微信代码编辑器
|
||||
// const WXCode = Loadable({
|
||||
// loader: () => import('./modules/wxcode'),
|
||||
// loading: Loading
|
||||
// });
|
||||
// //个人竞赛报名
|
||||
// const PersonalCompetit = Loadable({
|
||||
// loader: () => import('./modules/competition/personal/PersonalCompetit.js'),
|
||||
// loading: Loading,
|
||||
// });
|
||||
class App extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
Addcoursestype:false,
|
||||
Addcoursestypes:false,
|
||||
mydisplay:false,
|
||||
occupation:0,
|
||||
mygetHelmetapi: null,
|
||||
}
|
||||
|
||||
}
|
||||
HideAddcoursestypess=(i)=>{
|
||||
console.log("调用了");
|
||||
this.setState({
|
||||
Addcoursestype:false,
|
||||
Addcoursestypes:false,
|
||||
mydisplay:true,
|
||||
occupation:i,
|
||||
})
|
||||
};
|
||||
hideAddcoursestypes=()=>{
|
||||
this.setState({
|
||||
Addcoursestypes:false
|
||||
})
|
||||
};
|
||||
ModalCancelsy=()=>{
|
||||
this.setState({
|
||||
mydisplay:false,
|
||||
})
|
||||
window.location.href = "/";
|
||||
};
|
||||
ModalshowCancelsy=()=>{
|
||||
this.setState({
|
||||
mydisplay:true,
|
||||
})
|
||||
};
|
||||
|
||||
disableVideoContextMenu = () => {
|
||||
window.$( "body" ).on( "mousedown", "video", function(event) {
|
||||
if(event.which === 3) {
|
||||
window.$('video').bind('contextmenu',function () { return false; });
|
||||
} else {
|
||||
window.$('video').unbind('contextmenu');
|
||||
}
|
||||
});
|
||||
}
|
||||
componentDidMount() {
|
||||
document.title = "loading...";
|
||||
this.disableVideoContextMenu();
|
||||
// force an update if the URL changes
|
||||
history.listen(() => {
|
||||
this.forceUpdate()
|
||||
const $ = window.$
|
||||
// https://www.trustie.net/issues/21919 可能会有问题
|
||||
$("html").animate({ scrollTop: $('html').scrollTop() - 0 })
|
||||
});
|
||||
|
||||
initAxiosInterceptors(this.props);
|
||||
// 顶部和底部的动态设置
|
||||
// this.getAppdata();
|
||||
//
|
||||
// axios.interceptors.response.use((response) => {
|
||||
// // console.log("response"+response);
|
||||
// if(response!=undefined)
|
||||
// // console.log("response"+response.data.statu);
|
||||
// if (response&&response.data.status === 407) {
|
||||
// this.setState({
|
||||
// isRenders: true,
|
||||
// })
|
||||
// }
|
||||
// return response;
|
||||
// }, (error) => {
|
||||
// //TODO 这里如果样式变了会出现css不加载的情况
|
||||
// });
|
||||
|
||||
window.addEventListener('error', (event) => {
|
||||
const msg = `${event.type}: ${event.message}`;
|
||||
console.log(msg)
|
||||
});
|
||||
}
|
||||
//修改登录方法
|
||||
Modifyloginvalue=()=>{
|
||||
this.setState({
|
||||
isRender:false,
|
||||
})
|
||||
};
|
||||
|
||||
//获取数据为空的时候
|
||||
gettablogourlnull = () => {
|
||||
this.setState({
|
||||
mygetHelmetapi: undefined
|
||||
});
|
||||
document.title = "EduCoder";
|
||||
var link = document.createElement('link'),
|
||||
oldLink = document.getElementById('dynamic-favicon');
|
||||
link.id = 'dynamic-favicon';
|
||||
link.rel = 'shortcut icon';
|
||||
link.href = "/forgeplus-react/build/./favicon.ico";
|
||||
if (oldLink) {
|
||||
document.head.removeChild(oldLink);
|
||||
}
|
||||
document.head.appendChild(link);
|
||||
};
|
||||
|
||||
//获取数据的时候
|
||||
gettablogourldata = (response) => {
|
||||
document.title = response.data.setting.name;
|
||||
var link = document.createElement('link'),
|
||||
oldLink = document.getElementById('dynamic-favicon');
|
||||
link.id = 'dynamic-favicon';
|
||||
link.rel = 'shortcut icon';
|
||||
link.href = '/' + response.data.setting.tab_logo_url;
|
||||
if (oldLink) {
|
||||
document.head.removeChild(oldLink);
|
||||
}
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
//获取当前定制信息
|
||||
getAppdata=()=>{
|
||||
let url = "/setting.json";
|
||||
axios.get(url).then((response) => {
|
||||
// console.log("app.js开始请求/setting.json");
|
||||
// console.log("获取当前定制信息");
|
||||
if(response){
|
||||
if(response.data){
|
||||
this.setState({
|
||||
mygetHelmetapi:response.data.setting
|
||||
});
|
||||
//存储配置到游览器
|
||||
localStorage.setItem('chromesetting',JSON.stringify(response.data.setting));
|
||||
localStorage.setItem('chromesettingresponse',JSON.stringify(response));
|
||||
try {
|
||||
if (response.data.setting.tab_logo_url) {
|
||||
this.gettablogourldata(response);
|
||||
} else {
|
||||
this.gettablogourlnull();
|
||||
}
|
||||
} catch (e) {
|
||||
this.gettablogourlnull();
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
this.gettablogourlnull();
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
this.gettablogourlnull();
|
||||
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
this.gettablogourlnull();
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<ConfigProvider locale={zhCN}>
|
||||
<MuiThemeProvider theme={theme}>
|
||||
<Accountnewprofile {...this.props}{...this.state}/>
|
||||
<LoginDialog {...this.props} {...this.state} Modifyloginvalue={()=>this.Modifyloginvalue()}></LoginDialog>
|
||||
<Notcompletedysl {...this.props} {...this.state}></Notcompletedysl>
|
||||
<Trialapplicationysl {...this.props} {...this.state}></Trialapplicationysl>
|
||||
<Trialapplicationreview {...this.props} {...this.state}></Trialapplicationreview>
|
||||
<Addcourses {...this.props} {...this.state} HideAddcoursestypess={(i)=>this.HideAddcoursestypess(i)}/>
|
||||
<AccountProfile {...this.props} {...this.state} />
|
||||
<Certifiedprofessional {...this.props} {...this.state} ModalCancelsy={this.ModalCancelsy} ModalshowCancelsy={this.ModalshowCancelsy}/>
|
||||
<Router>
|
||||
<Switch>
|
||||
|
||||
{/* 项目 */}
|
||||
<Route path="/projects"
|
||||
render={
|
||||
(props) => {
|
||||
|
||||
return (<ProjectIndex {...this.props} {...props} {...this.state} />)
|
||||
}
|
||||
}></Route>
|
||||
|
||||
<Route exact path="/"
|
||||
// component={ShixunsHome}
|
||||
render={
|
||||
(props)=>(<ProjectIndex {...this.props} {...props} {...this.state}></ProjectIndex>)
|
||||
}
|
||||
/>
|
||||
{/*题库*/}
|
||||
<Route path="/topicbank/:username/:topicstype"
|
||||
render={
|
||||
(props) => {
|
||||
|
||||
return (<Topicbank {...this.props} {...props} {...this.state} />)
|
||||
}
|
||||
}></Route>
|
||||
{/*题库*/}
|
||||
<Route path="/topicbank/:topicstype"
|
||||
render={
|
||||
(props) => {
|
||||
|
||||
return (<Topicbank {...this.props} {...props} {...this.state} />)
|
||||
}
|
||||
}></Route>
|
||||
{/*/!*众包创新*!/*/}
|
||||
{/*<Route path={"/crowdsourcing"} component={ProjectPackages}/>*/}
|
||||
{/*竞赛*/}
|
||||
<Route path={"/competitions"}
|
||||
render={
|
||||
(props) => {
|
||||
|
||||
return (<NewCompetitions {...this.props} {...props} {...this.state} />)
|
||||
}
|
||||
}></Route>
|
||||
|
||||
{/*黑客松定制竞赛*/}
|
||||
<Route
|
||||
path={"/osshackathon"}
|
||||
render={
|
||||
(props)=>{
|
||||
return(
|
||||
<Osshackathon {...this.props} {...props} {...this.state} />
|
||||
)
|
||||
}
|
||||
}
|
||||
/>
|
||||
|
||||
{/*认证*/}
|
||||
<Route path="/account" component={AccountPage}/>
|
||||
|
||||
{/*403*/}
|
||||
<Route path="/403" component={Shixunauthority}/>
|
||||
|
||||
<Route path="/500" component={http500}/>
|
||||
|
||||
{/*404*/}
|
||||
<Route path="/nopage" component={Shixunnopage}/>
|
||||
|
||||
<Route path="/compatibility" component={CompatibilityPageLoadable}/>
|
||||
<Route
|
||||
path="/login"
|
||||
render={
|
||||
(props) => {
|
||||
|
||||
return (<EducoderLogin {...this.props} {...props} {...this.state} />)
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/register"
|
||||
render={
|
||||
(props) => {
|
||||
|
||||
return (<EducoderLogin {...this.props} {...props} {...this.state} />)
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/otherloginstart" component={Otherloginstart}
|
||||
/>
|
||||
<Route
|
||||
path={"/otherloginqq"} component={Otherloginsqq}
|
||||
/>
|
||||
<Route
|
||||
path="/otherlogin" component={Otherlogin}
|
||||
/>
|
||||
<Route
|
||||
path="/loginqq" component={Loginqq}
|
||||
/>
|
||||
|
||||
<Route path="/users/:username"
|
||||
render={
|
||||
(props) => {
|
||||
|
||||
return (<InfosIndex {...this.props} {...props} {...this.state} />)
|
||||
}
|
||||
}></Route>
|
||||
|
||||
<Route path="/banks"
|
||||
render={
|
||||
(props) => {
|
||||
return (<BanksIndex {...this.props} {...props} {...this.state} />)
|
||||
}
|
||||
}></Route>
|
||||
{/*<Route*/}
|
||||
{/*path="/personalcompetit"*/}
|
||||
{/*render={*/}
|
||||
{/*(props) => (<PersonalCompetit {...this.props} {...props} {...this.state}></PersonalCompetit>)*/}
|
||||
{/*}*/}
|
||||
{/*/>*/}
|
||||
<Route
|
||||
path="/changepassword"
|
||||
render={
|
||||
(props) => {
|
||||
|
||||
return (<EducoderLogin {...this.props} {...props} {...this.state} />)
|
||||
}
|
||||
}
|
||||
/>
|
||||
{/*<Route*/}
|
||||
{/* path="/interesse" component={Interestpage}*/}
|
||||
|
||||
{/*/>*/}
|
||||
<Route path="/shixuns/new" component={Newshixuns}>
|
||||
</Route>
|
||||
<Route path="/colleges/:id/statistics"
|
||||
render={
|
||||
(props) => (<College {...this.props} {...props} {...this.state} />)
|
||||
}/>
|
||||
{/* jupyter */}
|
||||
<Route path="/tasks/:identifier/jupyter/"
|
||||
render={
|
||||
(props) => {
|
||||
return (<JupyterTPI {...this.props} {...props} {...this.state}/>)
|
||||
}
|
||||
}
|
||||
/>
|
||||
|
||||
<Route path="/myproblems/record_detail/:id"
|
||||
render={
|
||||
(props) => (<RecordDetail {...this.props} {...props} {...this.state} />)
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/problems/:id/edit"
|
||||
render={
|
||||
(props) => (<NewOrEditTask {...this.props} {...props} {...this.state} />)
|
||||
} />
|
||||
<Route path="/Integeneration/:type/:id"
|
||||
render={
|
||||
(props) => (<Paperreview {...this.props} {...props} {...this.state} />)
|
||||
}/>
|
||||
<Route path="/paperreview/:type"
|
||||
render={
|
||||
(props) => (<Paperreview {...this.props} {...props} {...this.state} />)
|
||||
}/>
|
||||
<Route path="/paperlibrary/edit/:id"
|
||||
render={
|
||||
(props) => (<Paperlibraryeditid {...this.props} {...props} {...this.state} />)
|
||||
}/>
|
||||
|
||||
<Route path="/paperlibrary/see/:id"
|
||||
render={
|
||||
(props) => (<Paperlibraryseeid {...this.props} {...props} {...this.state} />)
|
||||
}/>
|
||||
|
||||
<Route path="/myproblems/:id/:tab?"
|
||||
render={
|
||||
(props) => (<StudentStudy {...this.props} {...props} {...this.state} />)
|
||||
} />
|
||||
<Route path="/question/edit/:id"
|
||||
render={
|
||||
(props) => (<Questionitem_banks {...this.props} {...props} {...this.state} />)
|
||||
} />
|
||||
|
||||
<Route path="/question/newitem"
|
||||
render={
|
||||
(props) => (<Questionitem_banks {...this.props} {...props} {...this.state} />)
|
||||
} />
|
||||
<Route path="/question/:type"
|
||||
render={
|
||||
(props) => (<Headplugselection {...this.props} {...props} {...this.state} />)
|
||||
} />
|
||||
<Route path="/paperlibrary"
|
||||
render={
|
||||
(props) => (<Testpaperlibrary {...this.props} {...props} {...this.state} />)
|
||||
}/>
|
||||
|
||||
<Route path="/Integeneration"
|
||||
render={
|
||||
(props) => (<Integeneration {...this.props} {...props} {...this.state} />)
|
||||
}/>
|
||||
|
||||
<Route path="/problems"
|
||||
render={
|
||||
(props) => (<Developer {...this.props} {...props} {...this.state} />)
|
||||
}/>
|
||||
|
||||
<Route path="/question"
|
||||
render={
|
||||
(props) => (<Headplugselection {...this.props} {...props} {...this.state} />)
|
||||
}/>
|
||||
{/*<Route path="/wxcode/:identifier?" component={WXCode}*/}
|
||||
{/* render={*/}
|
||||
{/* (props)=>(<WXCode {...this.props} {...props} {...this.state}></WXCode>)*/}
|
||||
{/* }*/}
|
||||
{/*/>*/}
|
||||
<Route exact path="/"
|
||||
// component={ShixunsHome}
|
||||
render={
|
||||
(props)=>(<ShixunsHome {...this.props} {...props} {...this.state}></ShixunsHome>)
|
||||
}
|
||||
/>
|
||||
<Route component={Shixunnopage}/>
|
||||
|
||||
</Switch>
|
||||
</Router>
|
||||
</MuiThemeProvider>
|
||||
</ConfigProvider>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// moment国际化,设置为中文
|
||||
moment.defineLocale('zh-cn', {
|
||||
months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'),
|
||||
monthsShort: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
|
||||
weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
|
||||
weekdaysShort: '周日_周一_周二_周三_周四_周五_周六'.split('_'),
|
||||
weekdaysMin: '日_一_二_三_四_五_六'.split('_'),
|
||||
longDateFormat: {
|
||||
LT: 'Ah点mm分',
|
||||
LTS: 'Ah点m分s秒',
|
||||
L: 'YYYY-MM-DD',
|
||||
LL: 'YYYY年MMMD日',
|
||||
LLL: 'YYYY年MMMD日Ah点mm分',
|
||||
LLLL: 'YYYY年MMMD日ddddAh点mm分',
|
||||
l: 'YYYY-MM-DD',
|
||||
ll: 'YYYY年MMMD日',
|
||||
lll: 'YYYY年MMMD日Ah点mm分',
|
||||
llll: 'YYYY年MMMD日ddddAh点mm分'
|
||||
},
|
||||
meridiemParse: /凌晨|早上|上午|中午|下午|晚上/,
|
||||
meridiemHour: function (hour, meridiem) {
|
||||
if (hour === 12) {
|
||||
hour = 0;
|
||||
}
|
||||
if (meridiem === '凌晨' || meridiem === '早上' ||
|
||||
meridiem === '上午') {
|
||||
return hour;
|
||||
} else if (meridiem === '下午' || meridiem === '晚上') {
|
||||
return hour + 12;
|
||||
} else {
|
||||
// '中午'
|
||||
return hour >= 11 ? hour : hour + 12;
|
||||
}
|
||||
},
|
||||
meridiem: function (hour, minute, isLower) {
|
||||
var hm = hour * 100 + minute;
|
||||
if (hm < 600) {
|
||||
return '凌晨';
|
||||
} else if (hm < 900) {
|
||||
return '早上';
|
||||
} else if (hm < 1130) {
|
||||
return '上午';
|
||||
} else if (hm < 1230) {
|
||||
return '中午';
|
||||
} else if (hm < 1800) {
|
||||
return '下午';
|
||||
} else {
|
||||
return '晚上';
|
||||
}
|
||||
},
|
||||
calendar: {
|
||||
sameDay: function () {
|
||||
return this.minutes() === 0 ? '[今天]Ah[点整]' : '[今天]LT';
|
||||
},
|
||||
nextDay: function () {
|
||||
return this.minutes() === 0 ? '[明天]Ah[点整]' : '[明天]LT';
|
||||
},
|
||||
lastDay: function () {
|
||||
return this.minutes() === 0 ? '[昨天]Ah[点整]' : '[昨天]LT';
|
||||
},
|
||||
nextWeek: function () {
|
||||
var startOfWeek, prefix;
|
||||
startOfWeek = moment().startOf('week');
|
||||
prefix = this.unix() - startOfWeek.unix() >= 7 * 24 * 3600 ? '[下]' : '[本]';
|
||||
return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm';
|
||||
},
|
||||
lastWeek: function () {
|
||||
var startOfWeek, prefix;
|
||||
startOfWeek = moment().startOf('week');
|
||||
prefix = this.unix() < startOfWeek.unix() ? '[上]' : '[本]';
|
||||
return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm';
|
||||
},
|
||||
sameElse: 'LL'
|
||||
},
|
||||
ordinalParse: /\d{1,2}(日|月|周)/,
|
||||
ordinal: function (number, period) {
|
||||
switch (period) {
|
||||
case 'd':
|
||||
case 'D':
|
||||
case 'DDD':
|
||||
return number + '日';
|
||||
case 'M':
|
||||
return number + '月';
|
||||
case 'w':
|
||||
case 'W':
|
||||
return number + '周';
|
||||
default:
|
||||
return number;
|
||||
}
|
||||
},
|
||||
relativeTime: {
|
||||
future: '%s内',
|
||||
past: '%s前',
|
||||
s: '几秒',
|
||||
m: '1分钟',
|
||||
mm: '%d分钟',
|
||||
h: '1小时',
|
||||
hh: '%d小时',
|
||||
d: '1天',
|
||||
dd: '%d天',
|
||||
M: '1个月',
|
||||
MM: '%d个月',
|
||||
y: '1年',
|
||||
yy: '%d年'
|
||||
},
|
||||
week: {
|
||||
// GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
|
||||
dow: 1, // Monday is the first day of the week.
|
||||
doy: 4 // The week that contains Jan 4th is the first week of the year.
|
||||
}
|
||||
});
|
||||
export default SnackbarHOC()(App) ;
|
|
@ -1,9 +0,0 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
|
@ -1,420 +0,0 @@
|
|||
import React from "react";
|
||||
|
||||
import axios from 'axios';
|
||||
import md5 from 'md5';
|
||||
import { requestProxy } from "./indexEduplus2RequestProxy";
|
||||
import { broadcastChannelOnmessage ,SetAppModel, isDev, queryString } from 'educoder';
|
||||
import { notification } from 'antd';
|
||||
import cookie from 'react-cookies';
|
||||
import './index.css';
|
||||
const $ = window.$;
|
||||
const opens ="79e33abd4b6588941ab7622aed1e67e8";
|
||||
let timestamp;
|
||||
let checkSubmitFlg = false;
|
||||
let message501=false;
|
||||
|
||||
broadcastChannelOnmessage('refreshPage', () => {
|
||||
window.location.reload()
|
||||
})
|
||||
|
||||
function locationurl(list){
|
||||
if (window.location.port === "3007") {
|
||||
|
||||
} else {
|
||||
window.location.href=list
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO 开发期多个身份切换
|
||||
let debugType =""
|
||||
if (isDev) {
|
||||
const _search = window.location.search;
|
||||
let parsed = {};
|
||||
if (_search) {
|
||||
parsed = queryString.parse(_search);
|
||||
}
|
||||
debugType = window.location.search.indexOf('debug=t') != -1 ? 'teacher' :
|
||||
window.location.search.indexOf('debug=s') != -1 ? 'student' :
|
||||
window.location.search.indexOf('debug=a') != -1 ? 'admin' : parsed.debug || 'admin'
|
||||
}
|
||||
// 超管
|
||||
// debugType="admin";
|
||||
// 老师
|
||||
//debugType="teacher";
|
||||
// 学生
|
||||
//debugType="student";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function clearAllCookie() {
|
||||
cookie.remove('_educoder_session', {path: '/'});
|
||||
cookie.remove('autologin_trustie', {path: '/'});
|
||||
setpostcookie()
|
||||
}
|
||||
clearAllCookie();
|
||||
function setpostcookie() {
|
||||
|
||||
const str =window.location.pathname;
|
||||
// console.log(str.indexOf("/wxcode"))
|
||||
let newdomain=".educoder.net"
|
||||
if(str.indexOf("/wxcode") !== -1){
|
||||
console.log("123")
|
||||
cookie.remove('_educoder_session', {path: '/'});
|
||||
cookie.remove('autologin_trustie', {path: '/'});
|
||||
// console.log("开始重写cookis");
|
||||
const _params = window.location.search;
|
||||
// console.log("1111");
|
||||
if (_params) {
|
||||
// console.log("22222");
|
||||
let _search = _params.split('?')[1];
|
||||
let _educoder_sessions= _search.split('&')[0].split('=');
|
||||
cookie.save('_educoder_session',_educoder_sessions[1], { domain:'.educoder.net', path: '/'});
|
||||
let autologin_trusties=_search.split('&')[1].split('=');
|
||||
cookie.save('autologin_trustie',autologin_trusties[1], { domain:'.educoder.net', path: '/'});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
setpostcookie();
|
||||
|
||||
|
||||
function railsgettimes(proxy) {
|
||||
|
||||
clearAllCookie()
|
||||
|
||||
if(timestamp&&checkSubmitFlg===false){
|
||||
$.ajax({url:proxy,async:false,success:function(data){
|
||||
if(data.status===0){
|
||||
timestamp=data.message;
|
||||
setpostcookie();
|
||||
}
|
||||
}})
|
||||
checkSubmitFlg=true
|
||||
window.setTimeout(()=>{
|
||||
checkSubmitFlg=false;
|
||||
}, 2000);
|
||||
}else if(checkSubmitFlg===false){
|
||||
$.ajax({url:proxy,async:false,success:function(data){
|
||||
if(data.status===0){
|
||||
timestamp=data.message;
|
||||
setpostcookie();
|
||||
}
|
||||
}})
|
||||
checkSubmitFlg=true
|
||||
window.setTimeout( ()=>{
|
||||
checkSubmitFlg=false;
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
window._debugType = debugType;
|
||||
export function initAxiosInterceptors(props) {
|
||||
initOnlineOfflineListener()
|
||||
|
||||
// TODO 避免重复的请求 https://github.com/axios/axios#cancellation
|
||||
// https://github.com/axios/axios/issues/1497
|
||||
|
||||
// TODO 读取到package.json中的配置?
|
||||
var proxy = "http://localhost:3000"
|
||||
// proxy = "http://testbdweb.trustie.net"
|
||||
// proxy = "http://testbdweb.educoder.net"
|
||||
// proxy = "https://testeduplus2.educoder.net"
|
||||
//proxy="http://47.96.87.25:48080"
|
||||
// proxy="https://pre-newweb.educoder.net"
|
||||
// proxy="https://test-newweb.educoder.net"
|
||||
// proxy="https://test-jupyterweb.educoder.net"
|
||||
// proxy="https://test-newweb.educoder.net"
|
||||
// proxy="https://test-jupyterweb.educoder.net"
|
||||
//proxy="http://192.168.2.63:3001"
|
||||
|
||||
var //proxy = "http://localhost:3000"
|
||||
// proxy="http://123.59.135.93:56666"
|
||||
proxy="http://localhost:3000"
|
||||
|
||||
// 在这里使用requestMap控制,避免用户通过双击等操作发出重复的请求;
|
||||
// 如果需要支持重复的请求,考虑config里面自定义一个allowRepeat参考来控制
|
||||
const requestMap = {};
|
||||
|
||||
window.setfalseInRequestMap = function(keyName) {
|
||||
requestMap[keyName] = false;
|
||||
}
|
||||
|
||||
//响应前的设置
|
||||
axios.interceptors.request.use(
|
||||
config => {
|
||||
setpostcookie()
|
||||
clearAllCookie()
|
||||
// config.headers['Content-Type']= 'no-cache'
|
||||
// if (token) { // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
|
||||
// config.headers.Authorization = token;
|
||||
// }
|
||||
|
||||
// --------------------------------------------- 測試3007连测试服的代码
|
||||
// if (url.indexOf('file_update') != -1 || url.indexOf('game_build') != -1 || url.indexOf('game_status') != -1) {
|
||||
// proxy = 'https://testbdweb.trustie.net'
|
||||
// } else {
|
||||
// proxy = 'http://localhost:3000'
|
||||
// }
|
||||
// ---------------------------------------------
|
||||
// console.log("开始请求了");
|
||||
// console.log(config.url);
|
||||
// console.log(window.location.pathname);
|
||||
//
|
||||
|
||||
// try {
|
||||
// const str =window.location.pathname;
|
||||
// if(str.indexOf("/wxcode") !== -1){
|
||||
// // console.log("开始重写cookis");
|
||||
// const _params = window.location.search;
|
||||
// // console.log("1111");
|
||||
// if (_params) {
|
||||
// // console.log("22222");
|
||||
// let _search = _params.split('?')[1];
|
||||
// var _educoder_sessionmys="";
|
||||
// var autologin_trusties="";
|
||||
// _search.split('&').forEach(item => {
|
||||
// const _arr = item.split('=');
|
||||
// if(_arr[0]==='_educoder_session'){
|
||||
// cookie.save('_educoder_session',_arr[1], { domain: '.educoder.net', path: '/'});
|
||||
// _educoder_sessionmys=_arr[1];
|
||||
// }else{
|
||||
// cookie.save('autologin_trustie',_arr[1], { domain: '.educoder.net', path: '/'});
|
||||
// autologin_trusties=_arr[1];
|
||||
// }
|
||||
// });
|
||||
// try {
|
||||
// const autlogins= `_educoder_session=${_educoder_sessionmys}; autologin_trustie=${autologin_trusties} `;
|
||||
// config.params = {'Cookie': autlogins}
|
||||
// config.headers['Cookie'] =autlogins;
|
||||
// // console.log("设置了cookis");
|
||||
// } catch (e) {
|
||||
//
|
||||
// }
|
||||
// try {
|
||||
// const autloginysls= `_educoder_session=${_educoder_sessionmys}; autologin_trustie=${autologin_trusties} `;
|
||||
// config.params = {'autloginysls': autloginysls}
|
||||
// config.headers['Cookie'] =autloginysls;
|
||||
// // console.log("设置了cookis");
|
||||
// }catch (e) {
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }catch (e) {
|
||||
//
|
||||
// }
|
||||
|
||||
|
||||
|
||||
if (config.url.indexOf(proxy) != -1 || config.url.indexOf(':') != -1) {
|
||||
return config
|
||||
}
|
||||
requestProxy(config)
|
||||
|
||||
let url = `/api${config.url}`;
|
||||
|
||||
//qq登录去掉api
|
||||
if(config.params&&config.params.redirect_uri!=undefined){
|
||||
if(config.params.redirect_uri.indexOf('otherloginqq')!=-1){
|
||||
url = `${config.url}`;
|
||||
}
|
||||
}
|
||||
if(`${config[0]}`!=`true`){
|
||||
let timestamp = Date.parse(new Date())/1000;
|
||||
if (window.location.port === "3007") {
|
||||
// let timestamp=railsgettimes(proxy);
|
||||
// console.log(timestamp)
|
||||
// `https://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp`
|
||||
railsgettimes( `${proxy}/api/main/first_stamp.json`);
|
||||
let newopens=md5(opens+timestamp)
|
||||
config.url = `${proxy}${url}`;
|
||||
if (config.url.indexOf('?') == -1) {
|
||||
config.url = `${config.url}?debug=${debugType}&randomcode=${timestamp}&client_key=${newopens}`;
|
||||
} else {
|
||||
config.url = `${config.url}&debug=${debugType}&randomcode=${timestamp}&client_key=${newopens}`;
|
||||
}
|
||||
} else {
|
||||
// 加api前缀
|
||||
// railsgettimes(`http://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp`);
|
||||
|
||||
railsgettimes( `/api/main/first_stamp.json`);
|
||||
let newopens=md5(opens+timestamp)
|
||||
config.url = url;
|
||||
if (config.url.indexOf('?') == -1) {
|
||||
config.url = `${config.url}?randomcode=${timestamp}&client_key=${newopens}`;
|
||||
} else {
|
||||
config.url = `${config.url}&randomcode=${timestamp}&client_key=${newopens}`;
|
||||
}
|
||||
}
|
||||
setpostcookie();
|
||||
}
|
||||
//
|
||||
// console.log(config);
|
||||
if (config.method === "post") {
|
||||
if (requestMap[config.url] === true) { // 避免重复的请求 导致页面f5刷新 也会被阻止 显示这个方法会影响到定制信息
|
||||
// console.log(config);
|
||||
// console.log(JSON.parse(config));
|
||||
// console.log(config.url);
|
||||
// console.log("被阻止了是重复请求=================================");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 非file_update请求
|
||||
if (config.url.indexOf('update_file') === -1) {
|
||||
requestMap[config.url] = true;
|
||||
|
||||
window.setTimeout("setfalseInRequestMap('"+config.url+"')", 900)
|
||||
}
|
||||
// setTimeout("setfalseInRequestMap(" + config.url + ")", 1200)
|
||||
return config;
|
||||
},
|
||||
err => {
|
||||
return Promise.reject(err);
|
||||
});
|
||||
|
||||
axios.interceptors.response.use(function (response) {
|
||||
|
||||
// console.log(".............")
|
||||
if(response===undefined){
|
||||
return
|
||||
}
|
||||
const config = response.config
|
||||
if (response.data.status === -1) {
|
||||
// console.error('error:', response.data.message)
|
||||
// throw new Error()
|
||||
|
||||
// https://github.com/axios/axios/issues?utf8=%E2%9C%93&q=cancel+request+in+response+interceptors+
|
||||
// https://github.com/axios/axios/issues/583
|
||||
// message.info(response.data.message || '服务端返回status -1,请联系管理员。');
|
||||
// props.showSnackbar( response.data.message || '服务器异常,请联系管理员。' )
|
||||
if (window.location.pathname.startsWith('/tasks/')) {
|
||||
props.showSnackbar( response.data.message || '服务器异常,请联系管理员。' )
|
||||
} else {
|
||||
notification.open({
|
||||
message:"提示",
|
||||
description: response.data.message || '服务器异常,请联系管理员。',
|
||||
style: {
|
||||
zIndex: 99999999
|
||||
},
|
||||
});
|
||||
// notification['error']({
|
||||
// message:"提示",
|
||||
// description: response.data.message || '服务器异常,请联系管理员。',
|
||||
// });
|
||||
}
|
||||
|
||||
throw new axios.Cancel('Operation canceled by the user.');
|
||||
} else {
|
||||
// hash跳转
|
||||
// var hash = window.location.hash;
|
||||
// if (hash) {
|
||||
// hashTimeout && window.clearTimeout(hashTimeout)
|
||||
// hashTimeout = setTimeout(() => {
|
||||
// var element = document.querySelector(hash);
|
||||
// if (element) {
|
||||
// element.scrollIntoView();
|
||||
// }
|
||||
// }, 400)
|
||||
// }
|
||||
}
|
||||
// if(response.data.status === 401){
|
||||
// console.log("401401401")
|
||||
// }
|
||||
if (response.data.status === 403||response.data.status === "403") {
|
||||
|
||||
locationurl('/403');
|
||||
}
|
||||
|
||||
if (response.data.status === 404) {
|
||||
locationurl('/nopage');
|
||||
}
|
||||
|
||||
if (response.data.status === 500) {
|
||||
locationurl('/500');
|
||||
}
|
||||
|
||||
if (response.data.status === 501) {
|
||||
if(message501===false){
|
||||
message501=true
|
||||
notification.open({
|
||||
message:"提示",
|
||||
description:response.data.message || '访问异常,请求不合理',
|
||||
style: {
|
||||
zIndex: 99999999
|
||||
}
|
||||
})
|
||||
}
|
||||
window.setTimeout(function () {
|
||||
message501=false
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
|
||||
// if (response.data.status === 402) {
|
||||
// console.log(response.data.status);
|
||||
// console.log(response.data);
|
||||
// // locationurl(402);
|
||||
// }
|
||||
|
||||
//
|
||||
// if (response.data.status === 401) {
|
||||
// console.log("161");
|
||||
// console.log(config);
|
||||
// return config;
|
||||
// }
|
||||
// if (response.data.status === 407) {
|
||||
// 在app js 中解决 Trialapplication
|
||||
// // </Trialapplication>
|
||||
// ///在appjs
|
||||
// notification.open({
|
||||
// message:"提示",
|
||||
// description: "账号未认证",
|
||||
// });
|
||||
// throw new axios.Cancel('Operation canceled by the user.');
|
||||
// //
|
||||
// }
|
||||
|
||||
requestMap[response.config.url] = false;
|
||||
setpostcookie();
|
||||
return response;
|
||||
}, function (error) {
|
||||
return Promise.reject(error);
|
||||
});
|
||||
// -----------------------------------------------------------------------------------
|
||||
|
||||
}
|
||||
|
||||
|
||||
function initOnlineOfflineListener() {
|
||||
const $ = window.$
|
||||
$(window).bind("online", () => {
|
||||
notification.destroy()
|
||||
notification.success({
|
||||
duration: 2,
|
||||
message: '网络恢复正常',
|
||||
description:
|
||||
'网络恢复正常,感谢使用。',
|
||||
})
|
||||
});
|
||||
$(window).bind("offline", () => {
|
||||
notification.destroy()
|
||||
|
||||
notification.warning({
|
||||
duration: null,
|
||||
message: '网络异常',
|
||||
description:
|
||||
'网络异常,请检测网络后重试。',
|
||||
})
|
||||
});
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
import Loadable from 'react-loadable';
|
||||
|
||||
import Loading from "./Loading";
|
||||
|
||||
const CustomLoadable = (loader, loading = Loading) => {
|
||||
return Loadable({
|
||||
loader,
|
||||
loading
|
||||
})
|
||||
}
|
||||
|
||||
export default CustomLoadable
|
|
@ -1,34 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
|
||||
|
||||
import { Spin } from 'antd';
|
||||
|
||||
class Loading extends Component {
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (!prevProps.error && this.props.error) {
|
||||
console.log(this.props.error)
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
// Loading
|
||||
return (
|
||||
<div className="App" style={{minHeight: '800px',width:"100%"}}>
|
||||
<style>
|
||||
{
|
||||
`
|
||||
.margintop{
|
||||
margin-top:20%;
|
||||
}
|
||||
`
|
||||
}
|
||||
</style>
|
||||
<Spin size="large" className={"margintop"}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Loading;
|
|
@ -1,41 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
|
||||
|
||||
class NotFoundPage extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="App">
|
||||
404 Page
|
||||
<br></br>
|
||||
|
||||
<Link to="/tasks/ixq5euhgrf7y">Index</Link>
|
||||
|
|
||||
<Link to="/shixuns/uznmbg54/challenges">tpm challenges</Link>
|
||||
|
|
||||
<Link to="/shixuns/uznmbg54/shixun_discuss">tpm discuss</Link>
|
||||
|
|
||||
<Link to="/forums/categories/all">forums</Link>
|
||||
|
|
||||
<Link to="/comment">Comment</Link>
|
||||
|
|
||||
<Link to="/testMaterial">testMaterial</Link>
|
||||
|
|
||||
<Link to="/testCodeMirror">testCodeMirror</Link>
|
||||
|
|
||||
<Link to="/taskList">taskList</Link>
|
||||
|
|
||||
<Link to="/testRCComponent">testRCComponent</Link>
|
||||
|
|
||||
<Link to="/tpforums">tpforums</Link>
|
||||
|
||||
|
||||
|
|
||||
<Link to="/testUrlQuery">url-query test</Link>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default NotFoundPage;
|
File diff suppressed because it is too large
Load Diff
|
@ -1,84 +0,0 @@
|
|||
import React, {Component} from "react";
|
||||
import {WordsBtn} from 'educoder';
|
||||
import {Table} from "antd";
|
||||
import {Link,Switch,Route,Redirect} from 'react-router-dom';
|
||||
const echarts = require('echarts');
|
||||
|
||||
|
||||
|
||||
function startechart(data,datanane){
|
||||
var effChart = echarts.init(document.getElementById('shixun_skill_chart'));
|
||||
|
||||
var option = {
|
||||
|
||||
tooltip : {
|
||||
trigger: 'item',
|
||||
formatter: "{d}% <br/>"
|
||||
},
|
||||
legend: {
|
||||
// orient: 'vertical',
|
||||
// top: 'middle',
|
||||
bottom: 50,
|
||||
left: 'center',
|
||||
data: datanane
|
||||
},
|
||||
series : [
|
||||
{
|
||||
type: 'pie',
|
||||
radius : '65%',
|
||||
center: ['50%', '35%'],
|
||||
selectedMode: 'single',
|
||||
data:data,
|
||||
itemStyle: {
|
||||
emphasis: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
effChart.setOption(option);
|
||||
}
|
||||
class Colleagechart extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
startechart(this.props.data,this.props.datanane)
|
||||
}
|
||||
|
||||
|
||||
componentDidUpdate = (prevProps) => {
|
||||
if (prevProps.data!= this.props.data) {
|
||||
startechart(this.props.data,this.props.datanane)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
let {data}=this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
<div
|
||||
style={{ width:'100%',height:'600px'}}
|
||||
id="shixun_skill_chart">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Colleagechart;
|
|
@ -1,149 +0,0 @@
|
|||
import React, {Component} from "react";
|
||||
import {WordsBtn} from 'educoder';
|
||||
import {Table} from "antd";
|
||||
import {Link,Switch,Route,Redirect} from 'react-router-dom';
|
||||
const echarts = require('echarts');
|
||||
|
||||
|
||||
|
||||
function startechart(names, values){
|
||||
var effChart = echarts.init(document.getElementById('shixun_skill_charts'));
|
||||
|
||||
var Color = ['#962e66', '#623363', '#CCCCCC', '#9A9A9A', '#FF8080', '#FF80C2', '#B980FF', '#80B9FF', '#6FE9FF', '#4DE8B4', '#F8EF63', '#FFB967'];
|
||||
|
||||
var option = {
|
||||
backgroundColor: '#fff',
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '8%',
|
||||
bottom: '15%',
|
||||
containLabel: true
|
||||
},
|
||||
|
||||
tooltip: {
|
||||
show: "true",
|
||||
trigger: 'item',
|
||||
formatter: '{c0}',
|
||||
backgroundColor: 'rgba(0,0,0,0.7)', // 背景
|
||||
padding: [8, 10], //内边距
|
||||
extraCssText: 'box-shadow: 0 0 3px rgba(255, 255, 255, 0.4);', //添加阴影
|
||||
axisPointer: { // 坐标轴指示器,坐标轴触发有效
|
||||
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#CCCCCC'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: false,
|
||||
lineStyle: {
|
||||
color: '#CCCCCC'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#656565',
|
||||
fontWeight: 'normal',
|
||||
fontSize: '12'
|
||||
},
|
||||
formatter: '{value}'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#cccccc'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
splitArea: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
inside: false,
|
||||
textStyle: {
|
||||
color: '#656565',
|
||||
fontWeight: 'normal',
|
||||
fontSize: '12'
|
||||
}
|
||||
},
|
||||
data: names
|
||||
},
|
||||
series: [{
|
||||
name: '',
|
||||
type: 'bar',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
show: true,
|
||||
color: function(params) {
|
||||
return Color[params.dataIndex]
|
||||
},
|
||||
barBorderRadius: 50,
|
||||
borderWidth: 0,
|
||||
borderColor: '#333'
|
||||
}
|
||||
},
|
||||
barGap: '0%',
|
||||
barCategoryGap: '50%',
|
||||
data: values
|
||||
}
|
||||
|
||||
]
|
||||
};
|
||||
effChart.setOption(option);
|
||||
}
|
||||
class Colleagechartzu extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
startechart(this.props.data,this.props.datavule)
|
||||
}
|
||||
|
||||
|
||||
componentDidUpdate = (prevProps) => {
|
||||
if (prevProps.data!= this.props.data) {
|
||||
startechart(this.props.data,this.props.datavule)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
let {data}=this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
<div
|
||||
style={{ width:'100%',height:'600px'}}
|
||||
id="shixun_skill_charts">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Colleagechartzu;
|
|
@ -1,213 +0,0 @@
|
|||
.yslstatistic-header {
|
||||
width: 100%;
|
||||
height: 240px;
|
||||
background-image: url('/images/educoder/statistics.jpg');
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.yslborder{
|
||||
border: 1px solid;
|
||||
}
|
||||
.yslstatistic-header-title{
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #4CACFF;
|
||||
font-size: 32px;
|
||||
}
|
||||
.yslstatistic-header-content{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
.yslstatistic-header-item{
|
||||
margin-bottom: 22px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
}
|
||||
.yslstatistic-header-item-label{
|
||||
color: #989898;
|
||||
}
|
||||
|
||||
.yslstatistic-base-item-label{
|
||||
width: 217px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
color: #686868;
|
||||
background: #F5F5F5;
|
||||
border-top: 1px solid #EBEBEB;
|
||||
}
|
||||
.yslstatistic-base-item-labels{
|
||||
width: 217px;
|
||||
text-align: center;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
background: #ffffff;
|
||||
border-top: 1px solid #EBEBEB;
|
||||
border-bottom: 1px solid #EBEBEB;
|
||||
}
|
||||
.yslstatistic-base-item-labelsp{
|
||||
color: #000000;
|
||||
font-size: 24px;
|
||||
}
|
||||
.yslstatistic-base-item-labelsspan{
|
||||
color: #000000;
|
||||
margin-left: 5px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.jibenshiyong100{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.yslstatistic-header-item-content{
|
||||
font-size: 24px;
|
||||
}
|
||||
/* 中间居中 */
|
||||
.intermediatecenter{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
/* 简单居中 */
|
||||
.intermediatecenterysls{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.spacearound{
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
|
||||
}
|
||||
.spacebetween{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
/* 头顶部居中 */
|
||||
.topcenter{
|
||||
display: -webkit-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* x轴正方向排序 */
|
||||
/* 一 二 三 四 五 六 七 八 */
|
||||
.sortinxdirection{
|
||||
display: flex;
|
||||
flex-direction:row;
|
||||
}
|
||||
/* x轴反方向排序 */
|
||||
/* 八 七 六 五 四 三 二 一 */
|
||||
.xaxisreverseorder{
|
||||
display: flex;
|
||||
flex-direction:row-reverse;
|
||||
}
|
||||
/* 垂直布局 正方向*/
|
||||
/* 一
|
||||
二
|
||||
三
|
||||
四
|
||||
五
|
||||
六
|
||||
七
|
||||
八 */
|
||||
.verticallayout{
|
||||
display: flex;
|
||||
flex-direction:column;
|
||||
}
|
||||
/* 垂直布局 反方向*/
|
||||
.reversedirection{
|
||||
display: flex;
|
||||
flex-direction:column-reverse;
|
||||
}
|
||||
|
||||
.h4{
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
.ysllinjibenshiyong{
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
padding: 2rem 1.25rem;
|
||||
border-bottom: unset;
|
||||
background:#fff;
|
||||
}
|
||||
.linjibenshiyong{
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
padding: 2rem 1.25rem;
|
||||
border-bottom: unset;
|
||||
background:#fff;
|
||||
box-shadow:0px 6px 12px 0px rgba(0,0,0,0.1);
|
||||
border-radius:2px;
|
||||
}
|
||||
.yslslinjibenshiyong{
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
border-bottom: unset;
|
||||
box-shadow:0px 6px 12px 0px rgba(0,0,0,0.1);
|
||||
border-radius:2px;
|
||||
}
|
||||
.yinyin{
|
||||
background: #fff;
|
||||
box-shadow:0px 6px 12px 0px rgba(0,0,0,0.1);
|
||||
border-radius:2px;
|
||||
}
|
||||
.edu-back-eeee{
|
||||
background:#EEEEEE !important;
|
||||
}
|
||||
.mt-4{
|
||||
margin-top: 1.5rem !important;
|
||||
}
|
||||
|
||||
.statistic-label{
|
||||
padding: 2rem 1.25rem;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
.mb50{
|
||||
padding-bottom: 50px !important;
|
||||
}
|
||||
.mt40{
|
||||
margin-top: 40px;
|
||||
}
|
||||
.mb80{
|
||||
margin-bottom: 80px;
|
||||
}
|
||||
.task-hide{overflow:hidden; white-space: nowrap; text-overflow:ellipsis;}
|
||||
a:hover{
|
||||
color:#0056b3;
|
||||
}
|
||||
.color-blue{
|
||||
color: #4CACFF;
|
||||
}
|
||||
|
||||
.color-huang{
|
||||
color:#ffc107 !important
|
||||
}
|
||||
.maxnamewidth105{
|
||||
max-width: 105px;
|
||||
overflow:hidden;
|
||||
text-overflow:ellipsis;
|
||||
white-space:nowrap;
|
||||
cursor: default;
|
||||
}
|
||||
.maxnamewidth247{
|
||||
max-width: 247px;
|
||||
overflow:hidden;
|
||||
text-overflow:ellipsis;
|
||||
white-space:nowrap;
|
||||
cursor: default;
|
||||
}
|
||||
.maxnamewidth340{
|
||||
max-width: 340px;
|
||||
overflow:hidden;
|
||||
text-overflow:ellipsis;
|
||||
white-space:nowrap;
|
||||
cursor: default;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
import md5 from 'md5';
|
||||
export function setmiyah(logins){
|
||||
const opens ="79e33abd4b6588941ab7622aed1e67e8";
|
||||
return md5(opens+logins);
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
/**
|
||||
EDU_ADMIN = 1 # 超级管理员
|
||||
EDU_BUSINESS = 2 # 运营人员
|
||||
EDU_SHIXUN_MANAGER = 3 # 实训管理员
|
||||
EDU_SHIXUN_MEMBER = 4 # 实训成员
|
||||
EDU_CERTIFICATION_TEACHER = 5 # 平台认证的老师
|
||||
EDU_GAME_MANAGER = 6 # TPI的创建者
|
||||
EDU_TEACHER = 7 # 平台老师,但是未认证
|
||||
EDU_NORMAL = 8 # 普通用户
|
||||
*/
|
||||
|
||||
export const EDU_ADMIN = 1 // 超级管理员
|
||||
export const EDU_BUSINESS = 2 // # 运营人员
|
||||
export const EDU_SHIXUN_MANAGER = 3 // 实训管理员
|
||||
export const EDU_SHIXUN_MEMBER = 4 // 实训成员
|
||||
export const EDU_CERTIFICATION_TEACHER = 5 // 平台认证的老师
|
||||
export const EDU_GAME_MANAGER = 6 // TPI的创建者
|
||||
export const EDU_TEACHER = 7 // 平台老师,但是未认证
|
||||
export const EDU_NORMAL = 8 // 普通用户
|
|
@ -1,69 +0,0 @@
|
|||
import moment from "moment";
|
||||
|
||||
// 处理整点 半点
|
||||
// 取传入时间往后的第一个半点
|
||||
export function handleDateString(dateString) {
|
||||
if (!dateString) return dateString;
|
||||
const ar = dateString.split(':')
|
||||
if (ar[1] == '00' || ar[1] == '30') {
|
||||
return dateString
|
||||
}
|
||||
const miniute = parseInt(ar[1]);
|
||||
if (miniute < 30 || miniute == 60) {
|
||||
return [ar[0], '30'].join(':')
|
||||
}
|
||||
if (miniute < 60) {
|
||||
// 加一个小时
|
||||
const tempStr = [ar[0], '00'].join(':');
|
||||
const format = "YYYY-MM-DD HH:mm";
|
||||
const _moment = moment(tempStr, format)
|
||||
_moment.add(1, 'hours')
|
||||
return _moment.format(format)
|
||||
}
|
||||
|
||||
return dateString
|
||||
}
|
||||
|
||||
// 给moment对象取下一个半点或整点
|
||||
export function getNextHalfHourOfMoment(moment) {
|
||||
if (!moment) {
|
||||
return moment
|
||||
}
|
||||
const minutes = moment.minutes()
|
||||
if (minutes < 30) {
|
||||
moment.minutes(30)
|
||||
} else if (minutes < 60) {
|
||||
moment.minutes(0).add(1, 'hours')
|
||||
}
|
||||
return moment
|
||||
}
|
||||
|
||||
export function formatDuring(mss){
|
||||
var days = parseInt(mss / (1000 * 60 * 60 * 24));
|
||||
var hours = parseInt((mss % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||
var minutes = parseInt((mss % (1000 * 60 * 60)) / (1000 * 60));
|
||||
// console.log("formatDuringformatDuring");
|
||||
// console.log(days);
|
||||
// console.log(hours);
|
||||
// console.log(minutes);
|
||||
// console.log(Math.abs(days));
|
||||
// console.log(Math.abs(hours));
|
||||
// console.log(Math.abs(minutes));
|
||||
|
||||
try {
|
||||
days = Math.abs(days);
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
try {
|
||||
hours = Math.abs(hours);
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
try {
|
||||
minutes = Math.abs(minutes);
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
return days + "天" + hours + "小时" + minutes + "分";
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
export function isDev() {
|
||||
return window.location.port === "3007";
|
||||
}
|
||||
|
||||
// const isMobile
|
||||
export const isMobile = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()));
|
||||
|
||||
// const isWeiXin = (/MicroMessenger/i.test(navigator.userAgent.toLowerCase()));
|
|
@ -1,70 +0,0 @@
|
|||
const $ = window.$;
|
||||
export function trigger(eventName, data) {
|
||||
$(window).trigger(eventName, data);
|
||||
}
|
||||
|
||||
export function on(eventName, callback) {
|
||||
$(window).on(eventName, (event, data)=>{
|
||||
callback && callback(event, data)
|
||||
});
|
||||
}
|
||||
|
||||
export function off(eventName) {
|
||||
$(window).off(eventName);
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/28230845/communication-between-tabs-or-windows
|
||||
const broadcastChannelMap = {}
|
||||
|
||||
const localStorageMap = {}
|
||||
function postMessageByLocalStorage(eventName, message) {
|
||||
console.log('storage event trigger:', eventName)
|
||||
localStorage.setItem(eventName, JSON.stringify(message));
|
||||
}
|
||||
function onMessageByLocalStorage(eventName, callback) {
|
||||
console.log('storage event register:', eventName)
|
||||
localStorageMap[eventName] = callback;
|
||||
}
|
||||
window.addEventListener("storage", function(ev) {
|
||||
const cb = localStorageMap[ev.key];
|
||||
// console.log('storage event:', ev)
|
||||
if (cb) {
|
||||
cb(JSON.parse(ev.newValue))
|
||||
}
|
||||
});
|
||||
export function broadcastChannelPostMessage(eventName, message) {
|
||||
if (!window.BroadcastChannel) {
|
||||
console.error('浏览器不支持BroadcastChannel')
|
||||
|
||||
postMessageByLocalStorage(eventName, message)
|
||||
return;
|
||||
}
|
||||
var bc;
|
||||
if (!broadcastChannelMap[eventName]) {
|
||||
bc = new window.BroadcastChannel(eventName);
|
||||
broadcastChannelMap[eventName] = bc
|
||||
} else {
|
||||
bc = broadcastChannelMap[eventName]
|
||||
}
|
||||
bc.postMessage(message); /* send */
|
||||
|
||||
}
|
||||
|
||||
export function broadcastChannelOnmessage(eventName, callback) {
|
||||
if (!window.BroadcastChannel) {
|
||||
console.error('浏览器不支持BroadcastChannel')
|
||||
onMessageByLocalStorage(eventName, callback)
|
||||
return;
|
||||
}
|
||||
var bc;
|
||||
if (!broadcastChannelMap[eventName]) {
|
||||
bc = new window.BroadcastChannel(eventName);
|
||||
broadcastChannelMap[eventName] = bc
|
||||
} else {
|
||||
bc = broadcastChannelMap[eventName]
|
||||
}
|
||||
bc.onmessage = function (ev) {
|
||||
console.log(ev);
|
||||
callback && callback(ev)
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
export function IEVersion(){
|
||||
var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串
|
||||
var isIE = userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1; //判断是否IE<11浏览器
|
||||
var isEdge = userAgent.indexOf("Edge") > -1 && !isIE; //判断是否IE的Edge浏览器
|
||||
var isIE11 = userAgent.indexOf('Trident') > -1 && userAgent.indexOf("rv:11.0") > -1;
|
||||
if(isIE) {
|
||||
var reIE = new RegExp("MSIE (\\d+\\.\\d+);");
|
||||
reIE.test(userAgent);
|
||||
var fIEVersion = parseFloat(RegExp["$1"]);
|
||||
if(fIEVersion == 7) {
|
||||
return 7;
|
||||
} else if(fIEVersion == 8) {
|
||||
return 8;
|
||||
} else if(fIEVersion == 9) {
|
||||
return 9;
|
||||
} else if(fIEVersion == 10) {
|
||||
return 10;
|
||||
} else {
|
||||
return 6;//IE版本<=7
|
||||
}
|
||||
} else if(isEdge) {
|
||||
return 'edge';//edge
|
||||
} else if(isIE11) {
|
||||
return 11; //IE11
|
||||
}else{
|
||||
return -1;//不是ie浏览器
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
import {Spin} from 'antd';
|
||||
class LoadingSpin extends Component{
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
render(){
|
||||
const { style } = this.props;
|
||||
return(
|
||||
<div className="edu-tab-con-box clearfix edu-txt-center" style={style}>
|
||||
<style>
|
||||
{`
|
||||
.edu-tab-con-box{
|
||||
padding:100px 0px;
|
||||
}
|
||||
.ant-modal-body .edu-tab-con-box{
|
||||
padding:0px!important;
|
||||
}
|
||||
img.edu-nodata-img{
|
||||
margin: 40px auto 20px;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
<Spin tip="正在获取相关数据..."/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default LoadingSpin;
|
|
@ -1,61 +0,0 @@
|
|||
import moment from 'moment'
|
||||
const log = require('loglevel');
|
||||
log.enableAll();
|
||||
|
||||
// 获取后可以改变日志级别
|
||||
window.getLog = () => {
|
||||
return log;
|
||||
}
|
||||
window._logWithTimeStamp = true;
|
||||
|
||||
const timeStamp = () => {
|
||||
if (window._logWithTimeStamp) {
|
||||
return `[${moment().format('hh:mm:ss')}] `
|
||||
}
|
||||
return ''
|
||||
}
|
||||
/*
|
||||
带trace的、默认折叠起来的控制台输出
|
||||
第一个参数最好传入string类型的标识,接着可以跟任意类型任意个数的参数,各个参数都会打印到控制台
|
||||
*/
|
||||
export function trace_collapse(content) {
|
||||
if (console.groupCollapsed) {
|
||||
console.groupCollapsed(typeof content == 'string' ? content : 'trace_collapse');
|
||||
log.trace(arguments);
|
||||
console.groupEnd();
|
||||
} else {
|
||||
trace(content)
|
||||
}
|
||||
}
|
||||
|
||||
export function trace(content) {
|
||||
log.trace(content);
|
||||
}
|
||||
export function debug(content) {
|
||||
log.debug(content);
|
||||
}
|
||||
export function info(content) {
|
||||
log.info(content);
|
||||
}
|
||||
export function warn(content) {
|
||||
log.warn(content);
|
||||
}
|
||||
export function error(content) {
|
||||
log.error(content);
|
||||
}
|
||||
|
||||
export function trace_c(content) {
|
||||
log.trace(`${timeStamp()}%c${content}`, 'color:magenta;');
|
||||
}
|
||||
export function debug_c(content) {
|
||||
log.debug(`${timeStamp()}%c${content}`, 'color:cyan;');
|
||||
}
|
||||
export function info_c(content) {
|
||||
log.info(`${timeStamp()}%c${content}`, 'color:blue;');
|
||||
}
|
||||
export function warn_c(content) {
|
||||
log.warn(`${timeStamp()}%c${content}`, 'color:crimson;');
|
||||
}
|
||||
export function error_c(content) {
|
||||
log.error(`${timeStamp()}%c${content}`, 'color:red;');
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
import { queryString } from 'educoder'
|
||||
export function updatePageParams(pageNum, props) {
|
||||
const url = props.match.url
|
||||
|
||||
const _search = props.location.search;
|
||||
let parsed = {};
|
||||
if (_search) {
|
||||
parsed = queryString.parse(_search);
|
||||
}
|
||||
|
||||
// 修改page參數
|
||||
parsed.page = pageNum
|
||||
|
||||
props.history.push(`${url}?${queryString.stringify(parsed)}`)
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
import { SnackbarHOC } from 'educoder';
|
||||
import { TPMIndexHOC } from '../modules/tpm/TPMIndexHOC';
|
||||
import {Spin,Alert} from 'antd';
|
||||
|
||||
class ShowSpin extends Component{
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
|
||||
|
||||
render() {
|
||||
let marigin={
|
||||
width: '100%',
|
||||
minHeight: '500px',
|
||||
}
|
||||
return (
|
||||
|
||||
<Spin style={marigin}>
|
||||
|
||||
<Alert
|
||||
style={marigin}
|
||||
type="info"
|
||||
/>
|
||||
|
||||
</Spin>
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default SnackbarHOC() ( TPMIndexHOC(ShowSpin) );
|
|
@ -1,91 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
import Snackbar from 'material-ui/Snackbar';
|
||||
import Fade from 'material-ui/transitions/Fade';
|
||||
import { notification } from 'antd'
|
||||
export function SnackbarHOC(options = {}) {
|
||||
return function wrap(WrappedComponent) {
|
||||
return class Wrapper extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.showSnackbar = this.showSnackbar.bind(this)
|
||||
this.state = {
|
||||
snackbarText: '',
|
||||
snackbarOpen: false,
|
||||
}
|
||||
}
|
||||
|
||||
handleSnackbarClose() {
|
||||
this.setState({
|
||||
snackbarOpen: false,
|
||||
snackbarVertical: '',
|
||||
snackbarHorizontal: '',
|
||||
})
|
||||
}
|
||||
|
||||
// 全局的snackbar this.props.showSnackbar调用即可
|
||||
// showSnackbar(description, message = "提示",icon) {
|
||||
// // this.setState({
|
||||
// // snackbarOpen: true,
|
||||
// // snackbarText: text,
|
||||
// // snackbarVertical: vertical,
|
||||
// // snackbarHorizontal: horizontal,
|
||||
// // })
|
||||
// const data = {
|
||||
// message,
|
||||
// description
|
||||
// }
|
||||
// if (icon) {
|
||||
// data.icon = icon;
|
||||
// }
|
||||
// notification.open(data);
|
||||
// }
|
||||
|
||||
showSnackbar(text, vertical, horizontal) {
|
||||
this.setState({
|
||||
snackbarOpen: true,
|
||||
snackbarText: text,
|
||||
snackbarVertical: vertical,
|
||||
snackbarHorizontal: horizontal,
|
||||
})
|
||||
}
|
||||
//个别情况需要走
|
||||
showNotification = (description, message = "提示", icon) => {
|
||||
const data = {
|
||||
message,
|
||||
description
|
||||
}
|
||||
if (icon) {
|
||||
data.icon = icon;
|
||||
}
|
||||
notification.open(data);
|
||||
}
|
||||
render() {
|
||||
const { snackbarOpen, snackbarText, snackbarHorizontal, snackbarVertical } = this.state;
|
||||
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Snackbar
|
||||
className={"rootSnackbar"}
|
||||
style={{zIndex:30000}}
|
||||
open={this.state.snackbarOpen}
|
||||
autoHideDuration={3000}
|
||||
anchorOrigin={{ vertical: this.state.snackbarVertical || 'top'
|
||||
, horizontal: this.state.snackbarHorizontal || 'center' }}
|
||||
onClose={() => this.handleSnackbarClose()}
|
||||
transition={Fade}
|
||||
SnackbarContentProps={{
|
||||
'aria-describedby': 'message-id',
|
||||
}}
|
||||
resumeHideDuration={2000}
|
||||
message={<span id="message-id">{this.state.snackbarText}</span>}
|
||||
/>
|
||||
<WrappedComponent {...this.props} showSnackbar={ this.showSnackbar } showNotification= { this.showNotification } >
|
||||
|
||||
</WrappedComponent>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import store from 'store'
|
||||
|
||||
export function toStore(key, val) {
|
||||
let _config = store.get('__ec');
|
||||
if (!_config) _config = {};
|
||||
_config[key] = val;
|
||||
store.set('__ec', _config)
|
||||
}
|
||||
|
||||
export function fromStore(key, defaultVal) {
|
||||
let _config = store.get('__ec');
|
||||
if (!_config) return defaultVal;
|
||||
return _config[key] === undefined ? defaultVal : _config[key];
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
import { bytesToSize, getUrl, getUrl2 } from 'educoder';
|
||||
const $ = window.$
|
||||
|
||||
export function isImageExtension(fileName) {
|
||||
return fileName ? !!(fileName.match(/.(jpg|jpeg|png|gif)$/i)) : false
|
||||
}
|
||||
|
||||
export function markdownToHTML(oldContent, selector) {
|
||||
window.$('#md_div').html('')
|
||||
// markdown to html
|
||||
if (selector && oldContent && oldContent.startsWith('<p')) { // 普通html处理
|
||||
window.$('#' + selector).addClass('renderAsHtml')
|
||||
window.$('#' + selector).html(oldContent)
|
||||
} else {
|
||||
try {
|
||||
$("#"+selector).html('')
|
||||
// selector ||
|
||||
var markdwonParser = window.editormd.markdownToHTML(selector || "md_div", {
|
||||
markdown: oldContent, // .replace(/▁/g,"▁▁▁"),
|
||||
emoji: true,
|
||||
htmlDecode: "style,script,iframe", // you can filter tags decode
|
||||
taskList: true,
|
||||
tex: true, // 默认不解析
|
||||
flowChart: true, // 默认不解析
|
||||
sequenceDiagram: true // 默认不解析
|
||||
});
|
||||
|
||||
} catch(e) {
|
||||
console.error(e)
|
||||
}
|
||||
// selector = '.' + selector
|
||||
if (selector) {
|
||||
return;
|
||||
}
|
||||
|
||||
const content = window.$('#md_div').html()
|
||||
if (selector) {
|
||||
window.$(selector).html(content)
|
||||
}
|
||||
return content
|
||||
}
|
||||
}
|
||||
function _doDownload(options) {
|
||||
$.fileDownload(getUrl() + "/api" + options.url, {
|
||||
successCallback: options.successCallback,
|
||||
failCallback: options.failCallback
|
||||
});
|
||||
}
|
||||
export function downloadFile(options) {
|
||||
if ($.fileDownload) {
|
||||
_doDownload(options)
|
||||
} else {
|
||||
const _url_origin = getUrl2()
|
||||
$.getScript(
|
||||
`${_url_origin}/javascripts/download/jquery.fileDownload.min.js`,
|
||||
(data, textStatus, jqxhr) => {
|
||||
_doDownload(options)
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function appendFileSizeToUploadFile(item) {
|
||||
return `${item.title}${uploadNameSizeSeperator}${item.filesize}`
|
||||
}
|
||||
export function appendFileSizeToUploadFileAll(fileList) {
|
||||
return fileList.map(item => {
|
||||
if (item.name.indexOf(uploadNameSizeSeperator) == -1) {
|
||||
return Object.assign({}, item, {name: `${item.name}${uploadNameSizeSeperator}${bytesToSize(item.size)}`})
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
export const uploadNameSizeSeperator = ' '
|
||||
|
||||
export const sortDirections = ["ascend", "descend", "ascend", "descend", "ascend", "descend", "ascend", "descend", "ascend", "descend", "ascend", "descend", "ascend", "descend",
|
||||
"ascend", "descend", "ascend", "descend", "ascend", "descend", "ascend", "descend", "ascend", "descend", "ascend", "descend", "ascend", "descend",
|
||||
"ascend", "descend", "ascend", "descend", "ascend", "descend", "ascend", "descend", "ascend", "descend", "ascend", "descend", "ascend", "descend",
|
||||
"ascend", "descend", "ascend", "descend", "ascend", "descend", "ascend", "descend", "ascend", "descend", "ascend", "descend", "ascend", "descend", ]
|
|
@ -1,6 +0,0 @@
|
|||
export function bytesToSize(bytes) {
|
||||
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
if (bytes == 0) return '0 Byte';
|
||||
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
|
||||
return parseFloat(bytes / Math.pow(1024, i), 2).toFixed(1) + ' ' + sizes[i];
|
||||
}
|
|
@ -1,187 +0,0 @@
|
|||
import React from "react";
|
||||
import md5 from 'md5';
|
||||
import {Input} from "antd";
|
||||
const { Search } = Input;
|
||||
|
||||
const $ = window.$;
|
||||
const isDev = window.location.port == 3007;
|
||||
export const TEST_HOST = "https://test-newweb.educoder.net"
|
||||
export function getImageUrl(path) {
|
||||
// https://www.educoder.net
|
||||
// https://testbdweb.trustie.net
|
||||
// const local = 'http://localhost:3000'
|
||||
const local = 'https://test-newweb.educoder.net'
|
||||
if (isDev) {
|
||||
return `${local}/${path}`
|
||||
}
|
||||
return `/${path}`;
|
||||
}
|
||||
|
||||
export function setImagesUrl(path){
|
||||
const local = 'https://test-newweb.educoder.net'
|
||||
let firstStr=path.substr(0,1);
|
||||
// console.log(firstStr);
|
||||
if(firstStr=="/"){
|
||||
return isDev?`${local}${path}`:`${path}`;
|
||||
}else{
|
||||
return isDev?`${local}/${path}`:`/${path}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function getUrl(path, goTest) {
|
||||
// https://www.educoder.net
|
||||
// https://testbdweb.trustie.net
|
||||
|
||||
// 如果想所有url定位到测试版,可以反注释掉下面这行
|
||||
//goTest = true
|
||||
// testbdweb.educoder.net testbdweb.trustie.net
|
||||
// const local = goTest ? 'https://testeduplus2.educoder.net' : 'http://localhost:3000'
|
||||
// const local = 'https://testeduplus2.educoder.net'
|
||||
const local = 'http://localhost:3007'
|
||||
if (isDev) {
|
||||
return `${local}${path?path:''}`
|
||||
}
|
||||
return `${path ? path: ''}`;
|
||||
}
|
||||
|
||||
export function getUrlmys(path, goTest) {
|
||||
// https://www.educoder.net
|
||||
// https://testbdweb.trustie.net
|
||||
|
||||
// 如果想所有url定位到测试版,可以反注释掉下面这行
|
||||
//goTest = true
|
||||
// testbdweb.educoder.net testbdweb.trustie.net
|
||||
// const local = goTest ? 'https://testeduplus2.educoder.net' : 'http://localhost:3000'
|
||||
// const local = 'https://testeduplus2.educoder.net'
|
||||
const local = 'https://test-jupyterweb.educoder.net'
|
||||
if (isDev) {
|
||||
return `${local}${path?path:''}`
|
||||
}
|
||||
return `${path ? path: ''}`;
|
||||
}
|
||||
export function getStaticUrl() {
|
||||
const local = TEST_HOST;
|
||||
if (isDev) {
|
||||
return local
|
||||
}
|
||||
// todo cdn
|
||||
return ''
|
||||
}
|
||||
export function getUrl2(path, goTest) {
|
||||
const local = 'http://localhost:3000'
|
||||
if (isDev) {
|
||||
return `${local}${path?path:''}`
|
||||
}
|
||||
return `${path ? path: ''}`;
|
||||
}
|
||||
const newopens ="79e33abd4b6588941ab7622aed1e67e8";
|
||||
let newtimestamp;
|
||||
let checkSubmitFlgs = false;
|
||||
function railsgettimess(proxy) {
|
||||
if(checkSubmitFlgs===false){
|
||||
$.ajax({url:proxy,
|
||||
async:false,success:function(data){
|
||||
if(data.status===0){
|
||||
newtimestamp=data.message;
|
||||
checkSubmitFlgs = true;
|
||||
}
|
||||
}})
|
||||
|
||||
window.setTimeout(function () {
|
||||
checkSubmitFlgs=false;
|
||||
}, 2500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export function Railsgettimes() {
|
||||
railsgettimess(`${getUrl()}/api/main/first_stamp.json`);
|
||||
// railsgettimess(`https://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp`);
|
||||
}
|
||||
export function getmyUrl(geturl) {
|
||||
|
||||
return `${getUrl()}${geturl}`;
|
||||
}
|
||||
|
||||
export function getUploadActionUrl(path, goTest) {
|
||||
Railsgettimes()
|
||||
let anewopens=md5(newopens+newtimestamp);
|
||||
return `${getUrl()}/api/attachments.json${isDev ? `?debug=${window._debugType || 'admin'}&randomcode=${newtimestamp}&client_key=${anewopens}` : `?randomcode=${newtimestamp}&client_key=${anewopens}`}`;
|
||||
}
|
||||
|
||||
export function getUploadActionUrltwo(id) {
|
||||
Railsgettimes()
|
||||
let anewopens=md5(newopens+newtimestamp);
|
||||
return `${getUrlmys()}/api/shixuns/${id}/upload_data_sets.json${isDev ? `?debug=${window._debugType || 'admin'}&randomcode=${newtimestamp}&client_key=${anewopens}` : `?randomcode=${newtimestamp}&client_key=${anewopens}`}`
|
||||
}
|
||||
|
||||
export function getUploadActionUrlthree() {
|
||||
Railsgettimes()
|
||||
let anewopens=md5(newopens+newtimestamp);
|
||||
return `${getUrlmys()}/api/jupyters/import_with_tpm.json${isDev ? `?debug=${window._debugType || 'admin'}&randomcode=${newtimestamp}&client_key=${anewopens}` : `?randomcode=${newtimestamp}&client_key=${anewopens}`}`
|
||||
}
|
||||
|
||||
export function getUploadActionUrlOfAuth(id) {
|
||||
Railsgettimes()
|
||||
let anewopens=md5(newopens+newtimestamp);
|
||||
return `${getUrl()}/api/users/accounts/${id}/auth_attachment.json${isDev ? `?debug=${window._debugType || 'admin'}&randomcode=${newtimestamp}&client_key=${anewopens}` : `?randomcode=${newtimestamp}&client_key=${anewopens}`}`
|
||||
}
|
||||
|
||||
export function getRandomNumber(type) {
|
||||
Railsgettimes()
|
||||
let anewopens=md5(newopens+newtimestamp);
|
||||
return type===true?`randomcode=${newtimestamp}&client_key=${anewopens}`:`?randomcode=${newtimestamp}&client_key=${anewopens}`
|
||||
}
|
||||
|
||||
export function test(path) {
|
||||
return `${path}`;
|
||||
}
|
||||
|
||||
export function toPath(path) {
|
||||
window.open(path, '_blank');
|
||||
}
|
||||
|
||||
|
||||
export function getTaskUrlById(id) {
|
||||
return `/tasks/${id}`
|
||||
}
|
||||
|
||||
export function getRandomcode(url) {
|
||||
Railsgettimes()
|
||||
let anewopens=md5(newopens+newtimestamp);
|
||||
|
||||
if (url.indexOf('?') == -1) {
|
||||
return `${url}?randomcode=${newtimestamp}&client_key=${anewopens}`
|
||||
}else {
|
||||
return `${url}&randomcode=${newtimestamp}&client_key=${anewopens}`
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function htmlEncode(str) {
|
||||
var s = "";
|
||||
if (str.length === 0) {
|
||||
return "";
|
||||
}
|
||||
s = str.replace(/&/g, "&");
|
||||
s = s.replace(/</g, "<");
|
||||
s = s.replace(/>/g, ">");
|
||||
s = s.replace(/ /g, " ");
|
||||
s = s.replace(/\'/g, "'");//IE下不支持实体名称
|
||||
s = s.replace(/\"/g, """);
|
||||
return s;
|
||||
}
|
||||
|
||||
export function publicSearchs(Placeholder,onSearch,onInputs,onChanges,loadings) {
|
||||
return(<Search
|
||||
placeholder= { Placeholder || "请输入内容进行搜索" }
|
||||
onSearch={onSearch}
|
||||
// value={searchValue}
|
||||
onInput={onInputs}
|
||||
onChange={onChanges}
|
||||
loading={loadings||false}
|
||||
allowClear={true}
|
||||
></Search>)
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
const queryString = {
|
||||
stringify: function(params) {
|
||||
let paramsUrl = '';
|
||||
for (let key in params) {
|
||||
// https://stackoverflow.com/questions/6566456/how-to-serialize-an-object-into-a-list-of-url-query-parameters
|
||||
if (params[key] != undefined) {
|
||||
if (params[key].constructor === Array) {
|
||||
for (let singleArrIndex of params[key]) {
|
||||
paramsUrl = paramsUrl + key + '[]=' + singleArrIndex + '&'
|
||||
}
|
||||
} else {
|
||||
paramsUrl += `${key}=${encodeURIComponent(params[key])}&`
|
||||
}
|
||||
}
|
||||
}
|
||||
if (paramsUrl == '') {
|
||||
return '';
|
||||
}
|
||||
paramsUrl = paramsUrl.substring(0, paramsUrl.length - 1);
|
||||
return paramsUrl;
|
||||
},
|
||||
parse: function(search) {
|
||||
// ?a=1&b=2
|
||||
if (!search) {
|
||||
return {}
|
||||
}
|
||||
if (search.startsWith('?')) {
|
||||
search = search.substring(1);
|
||||
}
|
||||
if (!search) {
|
||||
return {}
|
||||
}
|
||||
const keyValArray = search.split('&');
|
||||
const result = {}
|
||||
keyValArray.forEach(keyValItem => {
|
||||
const keyAndVal = keyValItem.split('=');
|
||||
result[keyAndVal[0]] = keyAndVal[1]
|
||||
})
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/*
|
||||
query-string用不了
|
||||
|
||||
Failed to minify the code from this file:
|
||||
|
||||
./node_modules/_query-string@6.1.0@query-string/index.js:8
|
||||
|
||||
Read more here: http://bit.ly/2tRViJ9
|
||||
*/
|
||||
module.exports = queryString
|
|
@ -1,28 +0,0 @@
|
|||
import React,{ Component } from "react";
|
||||
import { Modal,Input, Tooltip} from "antd";
|
||||
|
||||
class ConditionToolTip extends Component{
|
||||
constructor(props){
|
||||
super(props);
|
||||
this.state={
|
||||
}
|
||||
}
|
||||
render(){
|
||||
|
||||
let { condition } = this.props;
|
||||
return(
|
||||
<React.Fragment>
|
||||
{
|
||||
condition ?
|
||||
<Tooltip placement="bottom" {...this.props}>
|
||||
{this.props.children}
|
||||
</Tooltip> :
|
||||
<React.Fragment>
|
||||
{this.props.children}
|
||||
</React.Fragment>
|
||||
}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default ConditionToolTip;
|
|
@ -1,288 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
import { getUrl2, isDev } from 'educoder'
|
||||
const $ = window.$
|
||||
|
||||
let _url_origin = getUrl2()
|
||||
// let _url_origin = `http://47.96.87.25:48080`;
|
||||
|
||||
|
||||
|
||||
function save_avatar(){
|
||||
|
||||
// if($(img_lg).html().trim() == ""){
|
||||
// $("#avatar-name").html("请先选择图片上传").css("color", 'red');
|
||||
// } else {
|
||||
// $("#avatar-name").html("").css("color", '#333');
|
||||
const previewId = this.props.previewId
|
||||
var img_lg = document.getElementById(previewId || 'img-preview');
|
||||
// 截图小的显示框内的内容
|
||||
window.html2canvas(img_lg).then(function(canvas) {
|
||||
// for test
|
||||
// document.getElementById('canvasWrap').appendChild(canvas);
|
||||
|
||||
var dataUrl = canvas.toDataURL("image/jpeg");
|
||||
console.log(dataUrl)
|
||||
// TODO upload base64 image data to server
|
||||
});
|
||||
return
|
||||
|
||||
// 老版接口:
|
||||
// html2canvas(img_lg, {
|
||||
// allowTaint: true,
|
||||
// taintTest: false,
|
||||
// onrendered: function(canvas) {
|
||||
// canvas.id = "mycanvas";
|
||||
// //生成base64图片数据
|
||||
// var dataUrl = canvas.toDataURL("image/jpeg");
|
||||
// console.log(dataUrl)
|
||||
|
||||
// var newImg = document.getElementById("showImg");
|
||||
// newImg.src = dataUrl;
|
||||
// return;
|
||||
|
||||
// imagesAjax(dataUrl);
|
||||
// $(".avatar-save").attr("disabled","true");
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
/**
|
||||
props 说明:
|
||||
imageId 源图片标签的id
|
||||
previewId crop后预览dom的id
|
||||
imageSrc 源图片src
|
||||
width 数字格式
|
||||
height 数字格式
|
||||
*/
|
||||
class Cropper extends Component {
|
||||
state = {
|
||||
};
|
||||
|
||||
handleChange = (info) => {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.options = {
|
||||
aspectRatio: 1,
|
||||
crop(event) {
|
||||
// console.log(event.detail.x);
|
||||
// console.log(event.detail.y);
|
||||
// console.log(event.detail.width);
|
||||
// console.log(event.detail.height);
|
||||
// console.log(event.detail.rotate);
|
||||
// console.log(event.detail.scaleX);
|
||||
// console.log(event.detail.scaleY);
|
||||
},
|
||||
preview: this.props.previewId ? `#${this.props.previewId}` : '.img-preview',
|
||||
}
|
||||
|
||||
if (!window.Cropper) {
|
||||
$.ajaxSetup({
|
||||
cache: true
|
||||
});
|
||||
const _isDev = isDev()
|
||||
let _path = _isDev ? 'public' : 'build'
|
||||
|
||||
$('head').append($('<link rel="stylesheet" type="text/css" />')
|
||||
.attr('href', `${_url_origin}/react/${_path}/js/cropper/cropper.min.css`));
|
||||
|
||||
$.getScript(
|
||||
`${_url_origin}/react/${_path}/js/cropper/cropper.js`,
|
||||
(data, textStatus, jqxhr) => {
|
||||
|
||||
});
|
||||
$.getScript(
|
||||
`${_url_origin}/react/${_path}/js/cropper/html2canvas.min.js`,
|
||||
(data, textStatus, jqxhr) => {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
const image = document.getElementById(this.props.imageId || '__image');
|
||||
this.cropper = new window.Cropper(image, this.options);
|
||||
}, 1200)
|
||||
}
|
||||
|
||||
renew = (image) => {
|
||||
this.cropper && this.cropper.destroy();
|
||||
this.cropper = new window.Cropper(image, this.options);
|
||||
|
||||
}
|
||||
render() {
|
||||
|
||||
const { width, height, previewId, imageSrc } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* This rule is very important, please do not ignore this! */}
|
||||
<style>{`
|
||||
.wrapper {
|
||||
width: ${ width ? (width+'px') : '500px'};
|
||||
height: ${ height ? (height+'px') : '500px'};
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
.preview-lg {
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
}
|
||||
`}</style>
|
||||
<div className="wrapper">
|
||||
{/* http://localhost:3007/images/footNavLogo.png 图片转了后不对
|
||||
|| "/images/testPicture.jpg"
|
||||
|| "/images/shixun0.jpg"
|
||||
*/}
|
||||
<img id={this.props.imageId || "__image"} src={`${imageSrc }`}></img>
|
||||
</div>
|
||||
{/* background: 'aquamarine',
|
||||
'border-radius': '128px'
|
||||
*/}
|
||||
{!previewId && <div id="img-preview" className="img-preview preview-lg" style={{width: '128px', height: '128px', }}>
|
||||
</div>}
|
||||
|
||||
{/* <img id="showImg" src="http://localhost:3007/images/testPicture.jpg"></img> */}
|
||||
|
||||
{/* <div id="canvasWrap"></div> */}
|
||||
{/* <button onClick={save_avatar.bind(this)}>save </button> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Cropper;
|
||||
|
||||
|
||||
// function aaa () {
|
||||
// function showedit_headphoto() {
|
||||
// var html = `
|
||||
// <script src=\"../head/jquery.min.js\"><\/script>\n
|
||||
// <link href=\"../head/cropper.min.css\" rel=\"stylesheet\">\n
|
||||
// <link href=\"../head/sitelogo.css\" rel=\"stylesheet\">\n
|
||||
// <script src=\"../head/bootstrap.min.js\"><\/script>\n
|
||||
// <script src=\"../head/cropper.js\"><\/script>\n
|
||||
// <script src=\"../head/sitelogo.js\"><\/script>\n
|
||||
// <script src=\"../head/html2canvas.min.js\" type=\"text/javascript\" charset=\"utf-8\"><\/script>\n\n
|
||||
|
||||
// <div class=\"task-popup\" style=\"width: 550px;\">\n <div class=\"task-popup-title clearfix task-popup-bggrey\">上传头像<\/div>\n <div class=\"clearfix\">\n
|
||||
// <div class=\"modal fade\" style=\"outline: none;\" id=\"avatar-modal\" aria-hidden=\"true\" aria-labelledby=\"avatar-modal-label\" role=\"dialog\" tabindex=\"-1\">\n
|
||||
// <div class=\"modal-dialog modal-lg\">\n <div class=\"modal-content\">\n <form class=\"avatar-form\">\n <div class=\"modal-body\">\n
|
||||
// <div class=\"padding20\">\n <div class=\"avatar-upload\">\n <input class=\"avatar-src\" name=\"avatar_src\" type=\"hidden\">\n
|
||||
// <input class=\"avatar-data\" name=\"avatar_data\" type=\"hidden\">\n\n <span id=\"avatar-name\"><\/span>\n
|
||||
// <input class=\"avatar-input\" style=\"display:none;\" id=\"avatarInput\" value=\"avatars/User/116\" name=\"avatar_file\" type=\"file\">\n
|
||||
// <input type=\"hidden\" id=\"source_id\" value=\"116\"/>\n <input type=\"hidden\" id=\"source_type\" value=\"User\"/>\n <\/div>\n
|
||||
// <div class=\"row clearfix mt20 pl20\">\n <div class=\"task-form-45 fl panel-box-sizing uplaodImg\">\n
|
||||
// <div class=\"avatar-wrapper\" id=\"wrapper_image_show\">\n <!--<span style=\"display: block;\">\n 选择你要上传的图片<br/>
|
||||
// \n 仅支持JPG、GIF、PNG,且文件小于2M\n <\/span>-->\n <\/div>\n
|
||||
// <div class=\"row avatar-btns clearfix\">\n <a href=\"javascript:void(0);\" class=\"fl\" type=\"button\" onClick=\"$(\'input[id=avatarInput]\').click();\">重新上传<\/a>\n
|
||||
// <!--<div class=\"btn-group\">\n <a href=\"javascript:void(0);\" class=\"fa fa-repeat fr mt5 color-grey-9\" data-method=\"rotate\" data-option=\"90\"
|
||||
// type=\"button\" title=\"Rotate 90 degrees\">\n <\/a>\n <\/div>-->\n <\/div>\n <\/div>\n
|
||||
// <div class=\"task-form-50 panel-box-sizing fr color-grey pr20\" style=\"width: 128px;\">\n <div class=\"edu-txt-center\">\n
|
||||
// <div class=\"avatar-preview preview-lg radius\" id=\"imageHead\">\n
|
||||
// <img alt=\"头像\" height=\"128\" nhname=\"avatar_image\" src=\"/images/avatars/User/116?1556802838\" width=\"128\" />\n <\/div>\n
|
||||
// <span>头像预览<\/span>\n <\/div>\n
|
||||
// <p class=\"color-grey-9 font-12 mt110 justify\">仅支持JPG、GIF、PNG,且文件小于2M<\/p>\n <\/div>\n <\/div>\n
|
||||
// <\/div>\n <div class=\"clearfix edu-txt-center mb20 mt10\">\n
|
||||
// <a href=\"javascript:void(0);\" class=\"task-btn mr20\" onclick=\"hideModal()\">取消<\/a>\n
|
||||
// <a href=\"javascript:void(0);\" class=\"avatar-save task-btn task-btn-orange\" data-dismiss=\"modal\">确定<\/a>\n
|
||||
// <\/div>\n <\/div>\n <\/form>\n <\/div>\n <\/div>\n <\/div>\n <\/div>\n<\/div>\n\n<script>\n
|
||||
// $(function () {\n new CropAvatar($(\'#crop-avatar\'), 1/1);\n\n //---------------------------头像上传-----------------------------//\n //做个下简易的验证 大小 格式\n $(\'#avatarInput\').on(\'change\', function(e) {\n var filemaxsize = 1024 * 5;//5M\n var target = $(e.target);\n var Size = target[0].files[0].size / 1024;\n if(Size > filemaxsize) {\n alert(\'图片过大,请重新选择!\');\n $(\".avatar-wrapper\").children().remove;\n return false;\n }\n if(!this.files[0].type.match(/image.*/)) {\n alert(\'请选择正确的图片!\')\n } else {\n /*var filename = document.querySelector(\"#avatar-name\");*/\n var texts = document.querySelector(\"#avatarInput\").value;\n var teststr = texts; //你这里的路径写错了\n testend = teststr.match(/[^\\\\]+\\.[^\\(]+/i); //直接完整文件名的\n /*filename.innerHTML = testend;\n $(filename).css(\"color\", \'#333\');*/\n $(\".avatar-save\").removeClass(\"task-btn-grey\").addClass(\"task-btn-orange\");\n $(\".avatar-save\").on(\"click\", save_avatar);\n }\n\n });\n });\n\n function save_avatar(){\n var img_lg = document.getElementById(\'imageHead\');\n if($(img_lg).html().trim() == \"\"){\n $(\"#avatar-name\").html(\"请先选择图片上传\").css(\"color\", \'red\');\n } else {\n $(\"#avatar-name\").html(\"\").css(\"color\", \'#333\');\n // 截图小的显示框内的内容\n html2canvas(img_lg, {\n allowTaint: true,\n taintTest: false,\n onrendered: function(canvas) {\n canvas.id = \"mycanvas\";\n //生成base64图片数据\n var dataUrl = canvas.toDataURL(\"image/jpeg\");\n var newImg = document.createElement(\"img\");\n newImg.src = dataUrl;\n imagesAjax(dataUrl);\n $(\".avatar-save\").attr(\"disabled\",\"true\");\n }\n });\n }\n }\n\n function imagesAjax(src) {\n var data = {};\n data.img = src;\n data.source_id = $(\'#source_id\').val();\n data.source_type = $(\'#source_type\').val();\n data.is_direct = 0;\n $.ajax({\n url: \"/upload_avatar\",\n beforeSend: function(xhr) {xhr.setRequestHeader(\'X-CSRF-Token\', $(\'meta[name=\"csrf-token\"]\').attr(\'content\'))},\n data: data,\n type: \"POST\",\n success: function (re) {\n console.log(re);\n console.log(1562050370);\n if(re){\n var o = JSON.parse(re);\n if (o.status !=0 ){\n console.log(o.message);\n } else {\n var imgSpan = $(\"img[nhname=\'avatar_image\']\");\n imgSpan.attr({\"src\": o.url + \'?1562050370\'});\n $(\"#user_code\").html(o.grade);\n notice_box_redirect(\"/users/shitou\", \"上传成功\");\n }\n } else {\n notice_box(\"上传出错\");\n }\n\n },\n error: function (e) {\n alert(e);\n }\n });\n }\n
|
||||
// <\/script>`;
|
||||
// pop_box_new(html, 550, 510);
|
||||
// $("#imageHead img").attr({"src": $("#user_avatar_show").attr("src")});
|
||||
// $("#wrapper_image_show img").attr({"src": $("#user_avatar_show").attr("src")});
|
||||
|
||||
|
||||
// }
|
||||
|
||||
// $(function () {
|
||||
// new CropAvatar($('#crop-avatar'), 1/1);
|
||||
// //---------------------------头像上传-----------------------------//
|
||||
// //做个下简易的验证 大小 格式
|
||||
// $('#avatarInput').on('change', function(e) {
|
||||
// var filemaxsize = 1024 * 5;//5M
|
||||
// var target = $(e.target);
|
||||
// var Size = target[0].files[0].size / 1024;
|
||||
// if(Size > filemaxsize) {
|
||||
// alert('图片过大,请重新选择!');
|
||||
// $(".avatar-wrapper").children().remove;
|
||||
// return false;
|
||||
// }
|
||||
// if(!this.files[0].type.match(/image.*/)) {
|
||||
// alert('请选择正确的图片!')
|
||||
// } else {
|
||||
// /*var filename = document.querySelector("#avatar-name");*/
|
||||
// var texts = document.querySelector("#avatarInput").value;
|
||||
// var teststr = texts; //你这里的路径写错了
|
||||
// testend = teststr.match(/[^\\\\]+\\.[^\\(]+/i); //直接完整文件名的
|
||||
// /*filename.innerHTML = testend; $(filename).css("color", '#333');*/
|
||||
// $(".avatar-save").removeClass("task-btn-grey").addClass("task-btn-orange");
|
||||
// $(".avatar-save").on("click", save_avatar);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// function save_avatar(){
|
||||
// var img_lg = document.getElementById('imageHead');
|
||||
// if($(img_lg).html().trim() == ""){
|
||||
// $("#avatar-name").html("请先选择图片上传").css("color", 'red');
|
||||
// } else {
|
||||
// $("#avatar-name").html("").css("color", '#333');
|
||||
// // 截图小的显示框内的内容
|
||||
// html2canvas(img_lg, {
|
||||
// allowTaint: true,
|
||||
// taintTest: false,
|
||||
// onrendered: function(canvas) {
|
||||
// canvas.id = "mycanvas";
|
||||
// //生成base64图片数据
|
||||
// var dataUrl = canvas.toDataURL("image/jpeg");
|
||||
// var newImg = document.createElement("img");
|
||||
// newImg.src = dataUrl;
|
||||
// imagesAjax(dataUrl);
|
||||
// $(".avatar-save").attr("disabled","true");
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// function imagesAjax(src) {
|
||||
// var data = {};
|
||||
// data.img = src;
|
||||
// data.source_id = $('#source_id').val();
|
||||
// data.source_type = $('#source_type').val();
|
||||
// data.is_direct = 0;
|
||||
// $.ajax({
|
||||
// url: "/upload_avatar",
|
||||
// beforeSend: function(xhr) {
|
||||
// xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
|
||||
// },
|
||||
// data: data,
|
||||
// type: "POST",
|
||||
// success: function (re) {
|
||||
// console.log(re);
|
||||
// // console.log(1562050370);
|
||||
// if(re){
|
||||
// var o = JSON.parse(re);
|
||||
// if (o.status !=0 ){
|
||||
// console.log(o.message);
|
||||
// } else {
|
||||
// var imgSpan = $("img[nhname='avatar_image']");
|
||||
// imgSpan.attr({"src": o.url + '?1562050370'});
|
||||
// $("#user_code").html(o.grade);
|
||||
// notice_box_redirect("/users/shitou", "上传成功");
|
||||
// }
|
||||
// } else {
|
||||
// notice_box("上传出错");
|
||||
// }
|
||||
// },
|
||||
// error: function (e) {
|
||||
// alert(e);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
|
||||
// }
|
|
@ -1,116 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
const $ = window.jQuery
|
||||
const jQuery = $;
|
||||
// if () {
|
||||
// !$.drag
|
||||
// (function($){
|
||||
// $.fn.dragValidator = function(options){
|
||||
// var x, drag = this, isMove = false, defaults = {
|
||||
// };
|
||||
// var options = $.extend(defaults, options);
|
||||
// //添加背景,文字,滑块
|
||||
// var html = '<div class="drag_bg"></div>'+
|
||||
// '<div class="drag_text" onselectstart="return false;" unselectable="on">拖动滑块验证</div>'+
|
||||
// '<div class="handler handler_bg"></div>';
|
||||
// this.append(html);
|
||||
//
|
||||
// var handler = drag.find('.handler');
|
||||
// var drag_bg = drag.find('.drag_bg');
|
||||
// var text = drag.find('.drag_text');
|
||||
// var maxWidth = text.width() - handler.width(); //能滑动的最大间距
|
||||
// //鼠标按下时候的x轴的位置
|
||||
// handler.mousedown(function(e){
|
||||
// isMove = true;
|
||||
// x = e.pageX - parseInt(handler.css('left'), 10);
|
||||
// });
|
||||
//
|
||||
// //鼠标指针在上下文移动时,移动距离大于0小于最大间距,滑块x轴位置等于鼠标移动距离
|
||||
// $(document).mousemove(function(e){
|
||||
// var _x = e.pageX - x;
|
||||
// var handler_offset = handler.offset();
|
||||
// var lastX = e.clientX -x;
|
||||
// lastX = Math.max(0,Math.min(maxWidth,lastX));
|
||||
// if(isMove){
|
||||
// if(_x > 0 && _x <= maxWidth){
|
||||
// handler.css({'left': lastX});
|
||||
// drag_bg.css({'width': lastX});
|
||||
// }
|
||||
// else if(lastX > maxWidth - 5 && lastX < maxWidth + 5 ){ //鼠标指针移动距离达到最大时清空事件
|
||||
// dragOk();
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// handler.mouseup(function(e){
|
||||
// isMove = false;
|
||||
// var _x = e.pageX - x;
|
||||
// if(text.text() != '验证通过' && _x < maxWidth){ //鼠标松开时,如果没有达到最大距离位置,滑块就返回初始位置
|
||||
// handler.animate({'left': 0});
|
||||
// drag_bg.animate({'width': 0});
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// //清空事件
|
||||
// function dragOk(){
|
||||
// options.dragOkCallback && options.dragOkCallback()
|
||||
// var kuaiwidth=drag.width() - handler.width() - 2;
|
||||
// handler.removeClass('handler_bg').addClass('handler_ok_bg');
|
||||
// handler.css({'left':kuaiwidth+'px'})
|
||||
// text.css({'width':kuaiwidth+'px'});
|
||||
// text.text('验证通过');
|
||||
// drag.css({'color': '#fff'});
|
||||
// drag_bg.css({'width':kuaiwidth+'px'})
|
||||
// handler.unbind('mousedown');
|
||||
// $(document).unbind('mousemove');
|
||||
// $(document).unbind('mouseup');
|
||||
// $("#user_verification_notice").html("");
|
||||
// $('#user_verification_notice').parent().hide();
|
||||
// }
|
||||
// };
|
||||
// })(jQuery);
|
||||
// }
|
||||
|
||||
class DragValidator extends Component {
|
||||
componentDidMount () {
|
||||
// if($("#reg-drag").length>0 && IsPC()){
|
||||
// $("#reg-drag").dragValidator({
|
||||
// height: this.props.height,
|
||||
// dragOkCallback: () => {
|
||||
// this.props.dragOkCallback && this.props.dragOkCallback()
|
||||
// }
|
||||
// });
|
||||
// }else{
|
||||
// $("#reg-drag").empty();
|
||||
// }
|
||||
}
|
||||
empty() {
|
||||
// $("#reg-drag").empty();
|
||||
}
|
||||
render() {
|
||||
const height = this.props.height || 45;
|
||||
const className = this.props.className
|
||||
const successGreenColor = this.props.successGreenColor || '#29bd8b'
|
||||
// newMain clearfix
|
||||
return (
|
||||
<div id="reg-drag" style={{ width:"287px",}} className={`drag_slider ${className}`}>
|
||||
<style>{`
|
||||
.drag_slider .handler {
|
||||
height: 100%;
|
||||
}
|
||||
.drag_slider {
|
||||
height: ${height}px;
|
||||
line-height: ${height}px;
|
||||
}
|
||||
.drag_slider .drag_bg {
|
||||
height: ${height}px;
|
||||
background-color: ${successGreenColor};
|
||||
}
|
||||
`}</style>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ( DragValidator );
|
|
@ -1,31 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
// 登录后才能跳转
|
||||
class LinkAfterLogin extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
checkAuth = () => {
|
||||
if (this.props.checkIfLogin()) {
|
||||
if(this.props.checkProfileComplete){
|
||||
if(this.props.checkIfProfileCompleted()){
|
||||
this.props.history.push(this.props.to)
|
||||
}else{
|
||||
this.props.showProfileCompleteDialog();
|
||||
}
|
||||
}else{
|
||||
this.props.history.push(this.props.to)
|
||||
}
|
||||
} else {
|
||||
this.props.showLoginDialog()
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return(
|
||||
<a {...this.props} onClick={this.checkAuth}>{this.props.children}</a>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default LinkAfterLogin;
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* @Description:
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-12-13 10:28:15
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-12-13 10:37:17
|
||||
*/
|
||||
import { Modal } from 'antd';
|
||||
|
||||
export function ModalConfirm (
|
||||
title,
|
||||
content,
|
||||
handleOk,
|
||||
handleCancel
|
||||
) {
|
||||
|
||||
Modal.confirm({
|
||||
title,
|
||||
content,
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
onOk () {
|
||||
handleOk && handleOk();
|
||||
},
|
||||
onCancel () {
|
||||
handleCancel && handleCancel();
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Modal } from 'antd';
|
||||
|
||||
|
||||
export function ModalHOC(options = {}) {
|
||||
return function wrap(WrappedComponent) {
|
||||
return class Wrapper extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
titlemessage: '',
|
||||
Modallist: false,
|
||||
Modallisttype: false,
|
||||
singleButton: false
|
||||
}
|
||||
}
|
||||
|
||||
// 全局的modal this.props.showModal 调用即可
|
||||
showModal = (title, content, okCallback) => {
|
||||
this.okCallback = okCallback;
|
||||
this.setState({
|
||||
titlemessage: title,
|
||||
Modallist: content,
|
||||
Modallisttype: true,
|
||||
singleButton: false,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
showSingleButtonModal = (title, content) => {
|
||||
this.setState({
|
||||
titlemessage: title,
|
||||
Modallist: content,
|
||||
Modallisttype: true,
|
||||
singleButton: true,
|
||||
})
|
||||
}
|
||||
|
||||
onCancel = () => {
|
||||
this.setState({
|
||||
Modallisttype:false
|
||||
})
|
||||
}
|
||||
hidemodeldelete = () => {
|
||||
if (this.okCallback) {
|
||||
this.okCallback()
|
||||
}
|
||||
|
||||
this.onCancel()
|
||||
}
|
||||
render() {
|
||||
const { titlemessage, Modallisttype, Modallist, singleButton } = this.state;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Modal
|
||||
keyboard={false}
|
||||
title={titlemessage}
|
||||
// visible={modeldelet===true&&listid===list.id?true:false}
|
||||
visible={Modallisttype}
|
||||
className={"ecmodeldelet"}
|
||||
closable={false}
|
||||
footer={null}
|
||||
>
|
||||
<div className="task-popup-content" >
|
||||
<div className="task-popup-text-center font-14">{Modallist}</div>
|
||||
</div>
|
||||
{ singleButton ? <div className="task-popup-submit clearfix"
|
||||
style={{ textAlign: 'center' }}>
|
||||
<a className="task-btn task-btn-orange"
|
||||
onClick={this.onCancel}
|
||||
>知道啦</a>
|
||||
</div> : <div className="task-popup-submit clearfix">
|
||||
<a onClick={this.onCancel} className="task-btn fl">取消</a>
|
||||
<a className="task-btn task-btn-orange fr"
|
||||
onClick={this.hidemodeldelete}
|
||||
>确定</a>
|
||||
</div> }
|
||||
</Modal>
|
||||
<WrappedComponent {...this.props}
|
||||
showModal={ this.showModal }
|
||||
showSingleButtonModal={ this.showSingleButtonModal }
|
||||
>
|
||||
|
||||
</WrappedComponent>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
||||
import { ModalHOC } from '../common/ModalHOC'
|
||||
|
||||
export default ModalHOC() (XXXComponent) ;
|
||||
|
||||
this.props.showModal('提示', '确定要删除吗?', () => {
|
||||
this.remove(k)
|
||||
})
|
||||
|
||||
*/
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* @Description: 引入阿里图标库
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-12-10 09:03:48
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-12-12 10:53:47
|
||||
*/
|
||||
import { Icon } from 'antd';
|
||||
|
||||
const MyIcon = Icon.createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/font_1535266_i4ilpm93kp.js'
|
||||
});
|
||||
|
||||
export default MyIcon;
|
|
@ -1,48 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Modal} from 'antd';
|
||||
import axios from 'axios';
|
||||
import '../../modules/user/common.css';
|
||||
//完善个人资料
|
||||
class Notcompleted extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
}
|
||||
|
||||
modalCancel=()=>{
|
||||
window.location.href = "/";
|
||||
}
|
||||
|
||||
setDownload=()=>{
|
||||
window.location.href ='/account/profile';
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
console.log(this.props)
|
||||
return(
|
||||
<Modal
|
||||
keyboard={false}
|
||||
closable={false}
|
||||
footer={null}
|
||||
destroyOnClose={true}
|
||||
title="提示"
|
||||
centered={true}
|
||||
visible={this.props.modalsType===undefined?false:this.props.modalsType}
|
||||
width="530px"
|
||||
>
|
||||
<div className="educouddiv">
|
||||
<div className={"tabeltext-alignleft mt10"}><p>您尚未完善个人资料</p></div>
|
||||
<div className={"tabeltext-alignleft mt10"}><p>请在完成资料后,提交试用申请</p></div>
|
||||
<div className="clearfix mt30 edu-txt-center">
|
||||
<a className="task-btn mr30" onClick={()=>this.modalCancel()}>取消</a>
|
||||
<a className="task-btn task-btn-orange" onClick={()=>this.setDownload()}>立即完善资料</a>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Notcompleted;
|
|
@ -1,51 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Modal } from 'antd';
|
||||
export function SetAppModel(options={}) {
|
||||
return function wrap(WrappedComponent) {
|
||||
return class Wrapper extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
|
||||
}
|
||||
}
|
||||
modalCancel=()=>{
|
||||
window.location.href = "/";
|
||||
}
|
||||
|
||||
setDownload=()=>{
|
||||
window.location.href ='/account/profile';
|
||||
}
|
||||
componentDidMount(){
|
||||
console.log(this.props)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { titlemessage, Modallisttype, Modallist, singleButton } = this.state;
|
||||
return (
|
||||
<Modal
|
||||
keyboard={false}
|
||||
closable={false}
|
||||
footer={null}
|
||||
destroyOnClose={true}
|
||||
title="提示"
|
||||
centered={true}
|
||||
visible={true}
|
||||
width="530px"
|
||||
>
|
||||
<div className="educouddiv">
|
||||
<div className={"tabeltext-alignleft mt10"}><p>您尚未完善个人资料</p></div>
|
||||
<div className={"tabeltext-alignleft mt10"}><p>请在完成资料后,提交试用申请</p></div>
|
||||
<div className="clearfix mt30 edu-txt-center">
|
||||
<a className="task-btn mr30" onClick={()=>this.modalCancel()}>取消</a>
|
||||
<a className="task-btn task-btn-orange" onClick={()=>this.setDownload()}>立即完善资料</a>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
import React,{ Component } from "react";
|
||||
import { ConditionToolTip,getRandomNumber } from 'educoder'
|
||||
|
||||
class AttachmentsList extends Component{
|
||||
constructor(props){
|
||||
super(props);
|
||||
}
|
||||
render(){
|
||||
let { attachments } = this.props;
|
||||
return(
|
||||
<React.Fragment>
|
||||
{
|
||||
attachments.map((item,key)=>{
|
||||
return(
|
||||
<p key={key} className="clearfix mb3">
|
||||
<a className="color-grey fl">
|
||||
<i className="font-14 color-green iconfont icon-fujian mr8"></i>
|
||||
</a>
|
||||
<ConditionToolTip title={item.title} condition={item.title && item.title.length > 30 }>
|
||||
<a href={item.url+getRandomNumber()} className="mr12 fl task-hide" length="58" target={ item.is_pdf && item.is_pdf == true ? "_blank" : "_self" } style={{"maxWidth":"432px"}}>{item.title}</a>
|
||||
</ConditionToolTip>
|
||||
<span className="color-grey mt2 color-grey-6 font-12">{item.filesize}</span>
|
||||
</p>
|
||||
)
|
||||
})
|
||||
}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default AttachmentsList;
|
|
@ -1,139 +0,0 @@
|
|||
/*
|
||||
* @Description: 评论表单
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-12-17 17:32:55
|
||||
* @LastEditors : tangjiang
|
||||
* @LastEditTime : 2020-01-06 18:42:09
|
||||
*/
|
||||
import './index.scss';
|
||||
import React, { useState } from 'react';
|
||||
import { Form, Button, Input } from 'antd';
|
||||
import QuillForEditor from '../../quillForEditor';
|
||||
// import { QuillDeltaToHtmlConverter } from 'quill-delta-to-html'
|
||||
// import {formatDelta} from './util';
|
||||
const FormItem = Form.Item;
|
||||
|
||||
function CommentForm (props) {
|
||||
|
||||
const {
|
||||
onCancel,
|
||||
onSubmit,
|
||||
form,
|
||||
type
|
||||
} = props;
|
||||
|
||||
const { getFieldDecorator } = form;
|
||||
const [ctx, setCtx] = useState('');
|
||||
const [focus, setFocus] = useState(false);
|
||||
|
||||
const options = [
|
||||
// ['bold', 'italic', 'underline'],
|
||||
// [{header: [1,2,3,false]}],
|
||||
'code-block',
|
||||
'link',
|
||||
'image',
|
||||
'formula'
|
||||
];
|
||||
// const { form: { getFieldDecorator } } = props;
|
||||
const [showQuill, setShowQuill] = useState(false);
|
||||
// 点击输入框
|
||||
const handleInputClick = (type) => {
|
||||
setShowQuill(true);
|
||||
setFocus(true);
|
||||
}
|
||||
// 取消
|
||||
const handleCancle = () => {
|
||||
setShowQuill(false);
|
||||
setCtx('');
|
||||
props.form.resetFields();
|
||||
onCancel && onCancel();
|
||||
}
|
||||
|
||||
// 编辑器内容变化时
|
||||
const handleContentChange = (content) => {
|
||||
console.log('编辑器内容', content);
|
||||
setCtx(content);
|
||||
try {
|
||||
// const _html = new QuillDeltaToHtmlConverter(content.ops, {}).convert();
|
||||
// props.form.setFieldsValue({'comment': _html.replace(/<\/?[^>]*>/g, '')});
|
||||
props.form.setFieldsValue({'comment': content});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
// 发送
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
props.form.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
setShowQuill(false);
|
||||
const content = ctx;
|
||||
props.form.setFieldsValue({'comment': ''});
|
||||
setCtx('');
|
||||
// const _html = formatDelta(content.ops);
|
||||
// console.log('保存的内容=====》》》》', content);
|
||||
onSubmit && onSubmit(JSON.stringify(content));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const handleShowImage = (url) => {
|
||||
alert(url);
|
||||
}
|
||||
|
||||
// const _clazz = type === 'bottom' ? 'comment_form_bottom_area' : 'comment_form_area';
|
||||
let _clazz;
|
||||
if (type === 'bottom') {
|
||||
_clazz = showQuill ? 'comment_form_bottom_area active' : 'comment_form_bottom_area';
|
||||
} else {
|
||||
_clazz = 'comment_form_area';
|
||||
}
|
||||
return (
|
||||
<Form className={_clazz}>
|
||||
<FormItem>
|
||||
{
|
||||
getFieldDecorator('comment', {
|
||||
rules: [
|
||||
{ required: true, message: '评论内容不能为空'}
|
||||
],
|
||||
})(
|
||||
<Input
|
||||
onClick={() => handleInputClick(type)}
|
||||
placeholder="说点儿什么~"
|
||||
className={showQuill ? '' : 'show_input'}
|
||||
style={{
|
||||
height: showQuill ? '0px' : '40px',
|
||||
overflow: showQuill ? 'hidden' : 'auto',
|
||||
opacity: showQuill ? 0 : 1,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
<QuillForEditor
|
||||
imgAttrs={{width: '60px', height: '30px'}}
|
||||
wrapStyle={{
|
||||
height: showQuill ? 'auto' : '0px',
|
||||
opacity: showQuill ? 1 : 0,
|
||||
overflow: showQuill ? 'none' : 'hidden',
|
||||
transition: 'all 0.3s'
|
||||
}}
|
||||
autoFocus={focus}
|
||||
style={{ height: '150px' }}
|
||||
placeholder="说点儿什么~"
|
||||
options={options}
|
||||
value={ctx}
|
||||
showUploadImage={handleShowImage}
|
||||
onContentChange={handleContentChange}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem style={{ textAlign: 'right', display: showQuill ? 'block' : 'none' }}>
|
||||
<Button onClick={handleCancle}>取消</Button>
|
||||
<Button onClick={handleSubmit} type="primary" style={{ marginLeft: '10px'}}>发送</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
export default Form.create()(CommentForm);
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* @Description:
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-12-18 10:49:46
|
||||
* @LastEditors : tangjiang
|
||||
* @LastEditTime : 2019-12-25 10:03:21
|
||||
*/
|
||||
import './index.scss';
|
||||
import React from 'react';
|
||||
// import { Icon } from 'antd';
|
||||
// import MyIcon from '../MyIcon';
|
||||
function CommentIcon ({
|
||||
type, // 图标类型
|
||||
count, // 评论数
|
||||
iconClick,
|
||||
iconColor,
|
||||
theme,
|
||||
...props
|
||||
}) {
|
||||
|
||||
// 点击图标
|
||||
const handleSpanClick = () => {
|
||||
iconClick && iconClick();
|
||||
}
|
||||
|
||||
const _className = [undefined, null, ''].includes(count) ? 'comment_count_none' : 'comment_count';
|
||||
const _classIcon = `iconfont icon-${type} icon_font_size_14 comment_icon `;
|
||||
return (
|
||||
<span
|
||||
style={props.style}
|
||||
className={`comment_icon_count ${props.className}`}
|
||||
onClick={ handleSpanClick }
|
||||
>
|
||||
{/* <Icon className="comment_icon" type={type} style={{ color: iconColor }} theme={theme}/> */}
|
||||
<span className={_classIcon} style={{ color: iconColor }}></span>
|
||||
<span className={_className}>{ count }</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export default CommentIcon;
|
|
@ -1,257 +0,0 @@
|
|||
/*
|
||||
* @Description: 评论单列
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-12-17 17:35:17
|
||||
* @LastEditors : tangjiang
|
||||
* @LastEditTime : 2019-12-27 11:05:17
|
||||
*/
|
||||
import './index.scss';
|
||||
import 'quill/dist/quill.core.css'; // 核心样式
|
||||
import 'quill/dist/quill.snow.css'; // 有工具栏
|
||||
import 'quill/dist/quill.bubble.css'; // 无工具栏
|
||||
import 'katex/dist/katex.min.css'; // katex 表达式样式
|
||||
import React, { useState } from 'react';
|
||||
import CommentIcon from './CommentIcon';
|
||||
import { getImageUrl, CNotificationHOC } from 'educoder'
|
||||
import { Icon } from 'antd';
|
||||
import CommentForm from './CommentForm';
|
||||
import QuillForEditor from '../../quillForEditor';
|
||||
|
||||
function CommentItem ({
|
||||
isAdmin,
|
||||
options,
|
||||
confirm,
|
||||
comment,
|
||||
submitDeleteComment,
|
||||
submitChildComment,
|
||||
likeComment,
|
||||
showOrHideComment
|
||||
}) {
|
||||
// 显示评论输入框
|
||||
const [showQuill, setShowQuill] = useState(false);
|
||||
// 加载更多评论内容
|
||||
// const [showMore, setShowMore] = useState(false);
|
||||
// 显示子列数
|
||||
const [showItemCount, setShowItemCount] = useState(5);
|
||||
// 箭头方向
|
||||
const [arrow, setArrow] = useState(false);
|
||||
// 上传图片的ulr
|
||||
const [url, setUrl] = useState('');
|
||||
|
||||
const {
|
||||
author = {}, // 作者
|
||||
id, // 评论id
|
||||
content, // 回复内容
|
||||
time, // 回复时间
|
||||
hidden, // 是否隐藏
|
||||
// hack_id, // OJ的ID
|
||||
praise_count, // 点赞数
|
||||
user_praise, // 当前用户是否点赞
|
||||
can_delete,
|
||||
children = [] // 子回复
|
||||
} = comment;
|
||||
|
||||
// 删除评论 type: parent | child, id
|
||||
const deleteComment = (id) => {
|
||||
confirm({
|
||||
title: '提示',
|
||||
content: ('确定要删除该条回复吗?'),
|
||||
onOk () {
|
||||
console.log('点击了删除', id);
|
||||
submitDeleteComment && submitDeleteComment(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 评论头像
|
||||
const commentAvatar = (author) => (
|
||||
<img
|
||||
className="item-flex flex-image"
|
||||
src={author.image_url ? getImageUrl(`images/${author.image_url}`) : 'https://b-ssl.duitang.com/uploads/item/201511/13/20151113110434_kyReJ.jpeg'}
|
||||
alt=""
|
||||
/>
|
||||
);
|
||||
|
||||
// 评论信息
|
||||
const commentInfo = (id, author, time, can_delete) => {
|
||||
const _classNames = can_delete ? 'item-close' : 'item-close hide';
|
||||
return (
|
||||
<div className="item-header">
|
||||
<span className="item-name">{author.name || ''}</span>
|
||||
<span className="item-time">{time || ''}</span>
|
||||
<span className={_classNames}>
|
||||
<span className="iconfont icon-shanchu icon_font_size_14" onClick={() => deleteComment(id)}></span>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const handleShowUploadImage = (url) => {
|
||||
// console.log('==============>>>>>>>>>>>>',url);
|
||||
setUrl(url);
|
||||
}
|
||||
// 评论内容
|
||||
const commentCtx = (ctx) => {
|
||||
let _ctx = null;
|
||||
try {
|
||||
_ctx = JSON.parse(ctx);
|
||||
} catch (e) {
|
||||
_ctx = ctx;
|
||||
}
|
||||
return (
|
||||
<QuillForEditor
|
||||
readOnly={true}
|
||||
value={_ctx}
|
||||
showUploadImage={handleShowUploadImage}
|
||||
/>
|
||||
)};
|
||||
|
||||
// 加载更多
|
||||
const handleOnLoadMore = (len) => {
|
||||
setShowItemCount(!arrow ? len : 1);
|
||||
setArrow(!arrow);
|
||||
};
|
||||
|
||||
// 评论追加内容
|
||||
const commentAppend = (children = []) => {
|
||||
|
||||
const len = children.length;
|
||||
const _moreClass = len > showItemCount ? 'comment_item_loadmore show' : 'comment_item_loadmore'
|
||||
const lastTxt = len - showItemCount;
|
||||
const renderChild = (children) => {
|
||||
return children.map((child, i) => {
|
||||
const {
|
||||
id, // 评论id
|
||||
author = {},
|
||||
time,
|
||||
content,
|
||||
can_delete
|
||||
} = child;
|
||||
const showOrHide = i < showItemCount ? 'comment_item_show' : 'comment_item_hide';
|
||||
return (
|
||||
<li
|
||||
key={`child_${i}`}
|
||||
className={showOrHide}
|
||||
>
|
||||
<div className="comment_item_area comment_child_item_area">
|
||||
{commentAvatar(author)}
|
||||
<div className="item-flex item-desc">
|
||||
{commentInfo(id, author, time, can_delete)}
|
||||
{commentCtx(content)}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
const _clazz = len > 0 ? 'comment_item_append_list active' : 'comment_item_append_list';
|
||||
return (
|
||||
<ul className={_clazz}>
|
||||
{renderChild(children)}
|
||||
|
||||
<li className={_moreClass} onClick={() => handleOnLoadMore(len)}>
|
||||
<p className="loadmore-txt">展开其余{lastTxt}条评论</p>
|
||||
<p className="loadmore-icon">
|
||||
<Icon type={!arrow ? 'down' : 'up'}/>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
// 点击图标
|
||||
const handleShowOrHide = (id, hidden) => {
|
||||
showOrHideComment && showOrHideComment(id, hidden);
|
||||
}
|
||||
|
||||
// 点赞
|
||||
const handleClickLick = (id) => {
|
||||
likeComment && likeComment(id);
|
||||
}
|
||||
|
||||
// 点击评论icon
|
||||
const handleClickMessage = () => {
|
||||
setShowQuill(true);
|
||||
}
|
||||
|
||||
// 点击取消
|
||||
const handleClickCancel = () => {
|
||||
setShowQuill(false);
|
||||
}
|
||||
|
||||
// 点击保存
|
||||
const handleClickSubmit = (id) => {
|
||||
return (ctx) => {
|
||||
setShowQuill(false);
|
||||
submitChildComment && submitChildComment(id, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
setUrl('');
|
||||
}
|
||||
|
||||
return (
|
||||
<li className="comment_item_area">
|
||||
{commentAvatar(author)}
|
||||
<div className="item-flex item-desc">
|
||||
{commentInfo(id, author, time, can_delete)}
|
||||
{commentCtx(content)}
|
||||
|
||||
{commentAppend(children)}
|
||||
|
||||
<div className="comment_icon_area">
|
||||
<CommentIcon
|
||||
style={{ display: isAdmin ? 'inline-block' : 'none'}}
|
||||
className='comment-icon-margin'
|
||||
type={!hidden ? "xianshi" : 'yincang1'}
|
||||
iconClick={() => handleShowOrHide(id, !hidden ? 1 : 0)}
|
||||
/>
|
||||
|
||||
<CommentIcon
|
||||
style={{ display: can_delete ? 'inline-block' : 'none'}}
|
||||
className='comment-icon-margin'
|
||||
type={'shanchu'}
|
||||
iconClick={() => deleteComment(id)}
|
||||
/>
|
||||
{/* 回复 */}
|
||||
<CommentIcon
|
||||
className='comment-icon-margin'
|
||||
type="huifu1"
|
||||
count={children.length}
|
||||
iconClick={handleClickMessage}
|
||||
/>
|
||||
{/* 点赞 */}
|
||||
<CommentIcon
|
||||
iconColor={ user_praise ? '#5091FF' : '' }
|
||||
className='comment-icon-margin'
|
||||
theme={user_praise ? 'filled' : ''}
|
||||
type="dianzan"
|
||||
count={praise_count}
|
||||
iconClick={() => handleClickLick(id)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{ display: showQuill ? 'block' : 'none'}}
|
||||
className="comment_item_quill">
|
||||
<CommentForm
|
||||
onCancel={handleClickCancel}
|
||||
onSubmit={handleClickSubmit(id)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 显示上传的图片信息 */}
|
||||
<div className="show_upload_image" style={{ display: url ? 'block' : 'none'}}>
|
||||
<Icon type="close" className="image_close" onClick={handleClose}/>
|
||||
<div className="image_info">
|
||||
<img className="image" src={url} alt=""/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
export default CNotificationHOC() (CommentItem);
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* @Description: 评论列表页
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-12-17 17:34:00
|
||||
* @LastEditors : tangjiang
|
||||
* @LastEditTime : 2019-12-24 18:08:07
|
||||
*/
|
||||
import './index.scss';
|
||||
import React from 'react';
|
||||
import CommentItem from './CommentItem';
|
||||
import { Empty } from 'antd';
|
||||
function CommentList (props) {
|
||||
const {
|
||||
isAdmin,
|
||||
commentLists, // 评论列表
|
||||
submitChildComment,
|
||||
submitDeleteComment,
|
||||
likeComment,
|
||||
showOrHideComment
|
||||
} = props;
|
||||
|
||||
const {comments = []} = commentLists;
|
||||
|
||||
const renderLi = () => {
|
||||
if (comments.length > 0) {
|
||||
return comments.map((item, index) => {
|
||||
return (
|
||||
<CommentItem
|
||||
isAdmin={isAdmin}
|
||||
key={`item_${index}`}
|
||||
submitChildComment={submitChildComment}
|
||||
submitDeleteComment={submitDeleteComment}
|
||||
comment={item}
|
||||
likeComment={likeComment}
|
||||
showOrHideComment={showOrHideComment}
|
||||
/>
|
||||
);
|
||||
});
|
||||
} else {
|
||||
return (
|
||||
<div className="empty_comment">
|
||||
<Empty />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<ul className="comment_list_wrapper">
|
||||
{renderLi()}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
export default CommentList;
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* @Description: 评论组件
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-12-17 17:31:33
|
||||
* @LastEditors : tangjiang
|
||||
* @LastEditTime : 2019-12-24 18:03:21
|
||||
*/
|
||||
import React from 'react';
|
||||
// import CommentForm from './CommentForm';
|
||||
import CommentList from './CommentList';
|
||||
function Comment (props) {
|
||||
|
||||
const {
|
||||
commentLists,
|
||||
// addComment,
|
||||
// cancelComment,
|
||||
isAdmin,
|
||||
addChildComment,
|
||||
likeComment,
|
||||
showOrHideComment,
|
||||
submitDeleteComment
|
||||
} = props;
|
||||
|
||||
// const handleCancelComment = () => {
|
||||
// cancelComment && cancelComment();
|
||||
// };
|
||||
return (
|
||||
<React.Fragment>
|
||||
{/* <CommentForm
|
||||
onCancel={handleCancelComment}
|
||||
onSubmit={addComment}
|
||||
/> */}
|
||||
<CommentList
|
||||
isAdmin={isAdmin}
|
||||
likeComment={likeComment}
|
||||
showOrHideComment={showOrHideComment}
|
||||
commentLists={commentLists}
|
||||
submitChildComment={addChildComment}
|
||||
submitDeleteComment={submitDeleteComment}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default Comment;
|
|
@ -1,230 +0,0 @@
|
|||
$bdColor: rgba(244,244,244,1);
|
||||
$bgColor: rgba(250,250,250,1);
|
||||
$lh14: 14px;
|
||||
$lh22: 22px;
|
||||
$fz14: 14px;
|
||||
$fz12: 12px;
|
||||
$ml: 20px;
|
||||
|
||||
.comment_list_wrapper{
|
||||
box-sizing: border-box;
|
||||
// border-top: 1px solid $bdColor;
|
||||
.empty_comment{
|
||||
display: flex;
|
||||
height: calc(100vh - 200px);
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.comment_item_show{
|
||||
display: block;
|
||||
}
|
||||
.comment_item_hide{
|
||||
display: none;
|
||||
}
|
||||
.comment_item_area{
|
||||
display: flex;
|
||||
padding: 20px 0;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid $bdColor;
|
||||
|
||||
.comment_child_item_area:hover{
|
||||
.item-close{
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.flex-image{
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.item-desc{
|
||||
flex: 1;
|
||||
// margin-left: $ml;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.item-header{
|
||||
font-size: $fz14;
|
||||
line-height: $lh14;
|
||||
color: #333;
|
||||
margin-left: 15px;
|
||||
.item-time{
|
||||
font-size: $fz12;
|
||||
line-height: $lh14;
|
||||
margin-left: $ml;
|
||||
}
|
||||
.item-close{
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.item-close.hide{
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
.item-ctx{
|
||||
position: relative;
|
||||
line-height: $lh22;
|
||||
font-size: $fz12;
|
||||
color: #333;
|
||||
margin-top: 10px;
|
||||
vertical-align: top;
|
||||
}
|
||||
.comment_icon_area{
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 10px;
|
||||
|
||||
.comment-icon-margin{
|
||||
margin-left: 20px;
|
||||
}
|
||||
.comment-icon-margin-10{
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
// .comment_item_quill{
|
||||
// // margin-top: 10px;
|
||||
// }
|
||||
.show_upload_image{
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1000;
|
||||
&::before{
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width:100%;
|
||||
content: '';
|
||||
background: #000;
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.image_info{
|
||||
display: flex;
|
||||
position: absolute;
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
left: 10%;
|
||||
top: 10%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
// background: green;
|
||||
.image{
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.image_close{
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 20px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
.comment_icon_count{
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
|
||||
.comment_icon{
|
||||
color: #333;
|
||||
}
|
||||
.comment_count{
|
||||
color: #999999;
|
||||
margin-left: 10px;
|
||||
transition: color .3s;
|
||||
}
|
||||
.comment_count_none{
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&:hover{
|
||||
.comment_icon,
|
||||
.comment_count{
|
||||
color: #5091FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
.comment_item_append_list{
|
||||
display: none;
|
||||
position: relative;
|
||||
background-color: $bgColor;
|
||||
border-radius: 5px;
|
||||
padding: 0 15px 10px;
|
||||
margin: 15px 0;
|
||||
&.active{
|
||||
display: block;
|
||||
}
|
||||
&::before {
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
bottom: 100%;
|
||||
height: 0;
|
||||
width: 0;
|
||||
content: '';
|
||||
// border: 5px solid transparent;
|
||||
border: 10px solid transparent;
|
||||
border-bottom-color: $bgColor;
|
||||
}
|
||||
|
||||
.comment_item_loadmore{
|
||||
display: none;
|
||||
padding-top: 10px;
|
||||
cursor: pointer;
|
||||
.loadmore-txt,
|
||||
.loadmore-icon{
|
||||
color: #999;
|
||||
text-align: center;
|
||||
font-size: $fz12;
|
||||
}
|
||||
|
||||
&.show{
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon_font_size_14{
|
||||
font-size: 14px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.comment_form_area,
|
||||
.comment_form_bottom_area{
|
||||
width: 100%;
|
||||
}
|
||||
.comment_form_area{
|
||||
position: relative;
|
||||
background: #fff;
|
||||
// top: 10px;
|
||||
.ant-form-explain{
|
||||
padding-left: 0px;
|
||||
}
|
||||
.show_input{
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.comment_form_bottom_area{
|
||||
position: relative;
|
||||
background: #fff;
|
||||
top: 10px;
|
||||
|
||||
&.active{
|
||||
position: absolute;
|
||||
background: #fff;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
top: -230px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* @Description: quill delta -> html
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-12-24 08:51:25
|
||||
* @LastEditors : tangjiang
|
||||
* @LastEditTime : 2019-12-26 09:30:11
|
||||
*/
|
||||
export const formatDelta = (deltas) => {
|
||||
|
||||
let formatted = [];
|
||||
|
||||
deltas.forEach(element => {
|
||||
let text = null;
|
||||
// 没有图片时
|
||||
if (!element['insert']['image']) {
|
||||
text = element['insert']; // 获取插入的内容
|
||||
// 元素有属性时
|
||||
if (element['attributes']) {
|
||||
// 获取所有的key值
|
||||
const keys = Object.keys(element['attributes']);
|
||||
keys.forEach(key => {
|
||||
text = operate(text, key, element['attributes'][key]);
|
||||
});
|
||||
} else if (element['insert']['formula']) {
|
||||
text = element['insert']['formula'];
|
||||
}
|
||||
} else {
|
||||
const image = element['insert']['image'];
|
||||
const {url, alt} = image;
|
||||
if (url && (url.startsWith('http') || url.startsWith('https'))) {
|
||||
text = `
|
||||
<img
|
||||
src="${url}"
|
||||
style="{display: 'inline-block'}"
|
||||
width="60px"
|
||||
height="30px"
|
||||
alt="${alt}"
|
||||
/>
|
||||
`;
|
||||
// text = "<img src="+url+" width='60px' height='30px' onclick='' alt="+alt+"/>";
|
||||
}
|
||||
}
|
||||
|
||||
formatted.push(text);
|
||||
});
|
||||
console.log(formatted);
|
||||
return formatted.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {*} text 文本内容
|
||||
* @param {*} key 属性key
|
||||
* @param {*} value 属性key对应的值
|
||||
*/
|
||||
export const operate = (text, key, value) => {
|
||||
let operatedText = null;
|
||||
debugger;
|
||||
switch (key) {
|
||||
case 'bold':
|
||||
operatedText = `<strong>${text}</strong>`;
|
||||
break;
|
||||
case 'italic':
|
||||
operatedText = `<i>${text}</i>`;
|
||||
break;
|
||||
case 'strike':
|
||||
operatedText = `<s>${text}</s>`;
|
||||
break;
|
||||
case 'underline':
|
||||
operatedText = `<u>${text}</u>`;
|
||||
break;
|
||||
case 'link':
|
||||
operatedText = `<a href="${value}" style="color: #5091ff; text-decoration: underline;" target="bland">${text}</a>`;
|
||||
break;
|
||||
default:
|
||||
operatedText = text;
|
||||
}
|
||||
|
||||
return operatedText;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,68 +0,0 @@
|
|||
import React,{ Component } from "react";
|
||||
import { getImageUrl } from 'educoder'
|
||||
class PopInstruction extends Component{
|
||||
constructor(props){
|
||||
super(props);
|
||||
this.state={
|
||||
visible: false,
|
||||
}
|
||||
}
|
||||
setVisible = (visible) => {
|
||||
this.setState({ visible })
|
||||
}
|
||||
render(){
|
||||
const { visible } = this.state
|
||||
const { width, id } = this.props
|
||||
let _width = width || 92
|
||||
return(
|
||||
<div style={{position: 'relative', display: 'inline' }}>
|
||||
{/*
|
||||
width: ${width || 292}px!important;
|
||||
*/}
|
||||
<style>{`
|
||||
#repository_url_tip {
|
||||
top: 32px!important;
|
||||
left: 10px!important;
|
||||
z-index: 999;
|
||||
}
|
||||
.repository_url_tippostion {
|
||||
position: absolute;
|
||||
}
|
||||
#repository_url_tip.popInstruction${id} {
|
||||
width: ${_width}px !important;
|
||||
}
|
||||
|
||||
.top-black-trangleft {
|
||||
display: block;
|
||||
border-width: 8px;
|
||||
position: absolute;
|
||||
top: -16px;
|
||||
border-style: dashed solid dashed dashed;
|
||||
border-color: transparent transparent rgba(5,16,26,.6);
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
}
|
||||
`}</style>
|
||||
<a className="ml10" onClick={()=>this.setVisible(!visible)}>
|
||||
<img src={getImageUrl("images/educoder/problem.png")}
|
||||
style={{marginBottom: '2px'}}
|
||||
/>
|
||||
</a>
|
||||
<div className={`invite-tip popInstruction${id} clearfix repository_url_tippostion`} style={{display: visible===true?"block":"none"}}
|
||||
id="repository_url_tip"
|
||||
>
|
||||
<span className="top-black-trangleft"></span>
|
||||
<div className="padding20 invitecontent clearfix">
|
||||
{this.props.children}
|
||||
</div>
|
||||
<p className="inviteTipbtn with100">
|
||||
<a onClick={()=>this.setVisible(false)} >
|
||||
知道了
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default PopInstruction;
|
|
@ -1,12 +0,0 @@
|
|||
.hideMd {
|
||||
display: none !important;
|
||||
}
|
||||
.content_editorMd_show {
|
||||
height: auto;
|
||||
min-height: 38px;
|
||||
width: 800px;
|
||||
border: 1px solid rgba(234,234,234,1);
|
||||
margin-top: 2px;
|
||||
padding: 4px;
|
||||
width: 100%;
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
import React,{ Component } from "react";
|
||||
import TPMMDEditor from '../../../modules/tpm/challengesnew/TPMMDEditor'
|
||||
import {markdownToHTML} from 'educoder'
|
||||
import './DMDEditor.css'
|
||||
// 需要父组件通过toShowMode、toMDMode 来控制,一次只能打开一个DMDEditor
|
||||
class DMDEditor extends Component{
|
||||
constructor(props){
|
||||
super(props);
|
||||
this.mdRef = React.createRef()
|
||||
this.state={
|
||||
mdMode: false,
|
||||
// value: this.props.initValue
|
||||
}
|
||||
}
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
|
||||
}
|
||||
componentDidMount() {
|
||||
// if(this.props.initValue != this.mdRef.current.getValue()) {
|
||||
// this.mdRef.current.setValue(this.props.initValue)
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
toMDMode = () => {
|
||||
this.setState({mdMode: true}, () => {
|
||||
this.mdRef.current.resize()
|
||||
this.mdRef.current.setValue(this.props.initValue)
|
||||
})
|
||||
this.props.toMDMode(this)
|
||||
}
|
||||
toShowMode = () => {
|
||||
this.setState({mdMode: false})
|
||||
this.props.toShowMode && this.props.toShowMode(this)
|
||||
}
|
||||
onCMBlur = () => {
|
||||
this.toShowMode()
|
||||
}
|
||||
onChange = (val) => {
|
||||
// this.setState({ value: val })
|
||||
this.props.onChange(val)
|
||||
if (this.state.showError == true) {
|
||||
this.setState({showError: false})
|
||||
}
|
||||
}
|
||||
showError = () => {
|
||||
this.mdRef.current.showError()
|
||||
this.setState({showError: true})
|
||||
}
|
||||
render(){
|
||||
const { mdMode, showError } = this.state;
|
||||
const { initValue } = this.props;
|
||||
let _style = {}
|
||||
if (showError) {
|
||||
_style.border = '1px solid red'
|
||||
}
|
||||
_style = Object.assign(_style, {display: mdMode == true ? 'none' : '', color: initValue? '': '#999', alignItems: 'center', wordBreak: 'break-all'})
|
||||
return(
|
||||
<React.Fragment>
|
||||
<style>{`
|
||||
|
||||
`}</style>
|
||||
<div id="content_editorMd_show" className="new_li content_editorMd_show markdown-body"
|
||||
style={_style}
|
||||
dangerouslySetInnerHTML={{__html: initValue ? markdownToHTML(initValue):this.props.placeholder}}
|
||||
onClick={this.toMDMode}
|
||||
>
|
||||
|
||||
</div>
|
||||
{/*
|
||||
onCMBlur={this.onCMBlur} */}
|
||||
<TPMMDEditor
|
||||
ref={this.mdRef}
|
||||
{...this.props}
|
||||
initValue={initValue}
|
||||
className={`${this.props.className} ${mdMode == true ? '' : 'hideMd'}`}
|
||||
onChange={this.onChange}
|
||||
></TPMMDEditor>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default DMDEditor;
|
|
@ -1,6 +0,0 @@
|
|||
.markdownToHtml.editormd-html-preview, .markdownToHtml.editormd-preview-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
.markdownToHtml.editormd-html-preview p.editormd-tex, .markdownToHtml.editormd-preview-container p.editormd-tex {
|
||||
text-align: left;
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
import React,{ Component } from "react";
|
||||
import { markdownToHTML } from 'educoder'
|
||||
import './MarkdownToHtml.css'
|
||||
/**
|
||||
selector 需要传入唯一的selector作为id,不然会引起冲突
|
||||
*/
|
||||
class MarkdownToHtml extends Component{
|
||||
constructor(props){
|
||||
super(props);
|
||||
this.state={
|
||||
}
|
||||
}
|
||||
_markdownToHTML = (content, selector) => {
|
||||
markdownToHTML(content, selector)
|
||||
}
|
||||
componentDidUpdate = (prevProps) => {
|
||||
if (this.props.content) {
|
||||
if ( prevProps.content != this.props.content ) {
|
||||
this._markdownToHTML(this.props.content, `markdown_to_html_${this.props.selector || ''}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
componentDidMount () {
|
||||
this.props.content && this._markdownToHTML(this.props.content, `markdown_to_html_${this.props.selector || ''}`)
|
||||
|
||||
}
|
||||
render(){
|
||||
const { style, className } = this.props
|
||||
let _selector = `markdown_to_html_${this.props.selector || ''}`
|
||||
|
||||
return(
|
||||
<div id={_selector } className={`markdownToHtml new_li markdown-body ${className} ${_selector}`}
|
||||
// dangerouslySetInnerHTML={{__html: markdownToHTML(this.props.content)}}
|
||||
style={style}
|
||||
>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default MarkdownToHtml;
|
|
@ -1,237 +0,0 @@
|
|||
import React, { useState, useEffect, memo } from 'react';
|
||||
|
||||
import { getUrl2, isDev } from 'educoder'
|
||||
import axios from 'axios'
|
||||
const $ = window.$
|
||||
let _url_origin = getUrl2()
|
||||
let _path = isDev() ? 'public' : 'build'
|
||||
|
||||
let uploader
|
||||
|
||||
let _testHost = '' ; '192.168.2.63:3001/api'
|
||||
|
||||
|
||||
const login = 'innov'
|
||||
|
||||
function createUploader () {
|
||||
uploader = new window.AliyunUpload.Vod({
|
||||
timeout: $('#timeout').val() || 60000,
|
||||
partSize: $('#partSize').val() || 1048576,
|
||||
parallel: $('#parallel').val() || 5,
|
||||
retryCount: $('#retryCount').val() || 3,
|
||||
retryDuration: $('#retryDuration').val() || 2,
|
||||
region: $('#region').val() || 'ap-southeast-1',
|
||||
userId: $('#userId').val() || 1829848226361863, //, // 1303984639806000,
|
||||
// 添加文件成功
|
||||
addFileSuccess: function (uploadInfo) {
|
||||
console.log('addFileSuccess')
|
||||
$('#authUpload').attr('disabled', false)
|
||||
$('#resumeUpload').attr('disabled', false)
|
||||
$('#status').text('添加文件成功, 等待上传...')
|
||||
console.log("addFileSuccess: " + uploadInfo.file.name)
|
||||
|
||||
$('#pauseUpload').attr('disabled', false)
|
||||
uploader.startUpload()
|
||||
},
|
||||
// 开始上传
|
||||
onUploadstarted: function (uploadInfo) {
|
||||
// 如果是 UploadAuth 上传方式, 需要调用 uploader.setUploadAuthAndAddress 方法
|
||||
// 如果是 UploadAuth 上传方式, 需要根据 uploadInfo.videoId是否有值,调用点播的不同接口获取uploadauth和uploadAddress
|
||||
// 如果 uploadInfo.videoId 有值,调用刷新视频上传凭证接口,否则调用创建视频上传凭证接口
|
||||
// 注意: 这里是测试 demo 所以直接调用了获取 UploadAuth 的测试接口, 用户在使用时需要判断 uploadInfo.videoId 存在与否从而调用 openApi
|
||||
// 如果 uploadInfo.videoId 存在, 调用 刷新视频上传凭证接口(https://help.aliyun.com/document_detail/55408.html)
|
||||
// 如果 uploadInfo.videoId 不存在,调用 获取视频上传地址和凭证接口(https://help.aliyun.com/document_detail/55407.html)
|
||||
if (!uploadInfo.videoId) {
|
||||
// var createUrl = 'https://demo-vod.cn-shanghai.aliyuncs.com/voddemo/CreateUploadVideo?Title=testvod1&FileName=aa.mp4&BusinessType=vodai&TerminalType=pc&DeviceModel=iPhone9,2&UUID=59ECA-4193-4695-94DD-7E1247288&AppVersion=1.0.0&VideoId=5bfcc7864fc14b96972842172207c9e6'
|
||||
// $.get(createUrl, function (data) {
|
||||
// var uploadAuth = data.UploadAuth
|
||||
// var uploadAddress = data.UploadAddress
|
||||
// var videoId = data.VideoId
|
||||
// uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress,videoId)
|
||||
// }, 'json')
|
||||
|
||||
var createUrl = `${_testHost}/users/${login}/video_auths.json?debug=true`
|
||||
|
||||
axios.post(createUrl, {
|
||||
title: 'testvod1',
|
||||
file_name: 'aa.mp4'
|
||||
}).then((response) => {
|
||||
// if (response.data.status == )
|
||||
const data = response.data.data
|
||||
var uploadAuth = data.UploadAuth
|
||||
var uploadAddress = data.UploadAddress
|
||||
var videoId = data.VideoId
|
||||
uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress, videoId)
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
|
||||
|
||||
$('#status').text('文件开始上传...')
|
||||
console.log("onUploadStarted:" + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object)
|
||||
} else {
|
||||
// 如果videoId有值,根据videoId刷新上传凭证
|
||||
// https://help.aliyun.com/document_detail/55408.html?spm=a2c4g.11186623.6.630.BoYYcY
|
||||
// var refreshUrl = 'https://demo-vod.cn-shanghai.aliyuncs.com/voddemo/RefreshUploadVideo?BusinessType=vodai&TerminalType=pc&DeviceModel=iPhone9,2&UUID=59ECA-4193-4695-94DD-7E1247288&AppVersion=1.0.0&Title=haha1&FileName=xxx.mp4&VideoId=' + uploadInfo.videoId
|
||||
// $.get(refreshUrl, function (data) {
|
||||
// var uploadAuth = data.UploadAuth
|
||||
// var uploadAddress = data.UploadAddress
|
||||
// var videoId = data.VideoId
|
||||
// uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress,videoId)
|
||||
// }, 'json')
|
||||
|
||||
var refreshUrl = `${_testHost}/users/${login}/video_auths.json?debug=true`
|
||||
|
||||
axios.put(refreshUrl, {
|
||||
video_id: uploadInfo.videoId,
|
||||
}).then((response) => {
|
||||
const data = response.data.data
|
||||
var uploadAuth = data.UploadAuth
|
||||
var uploadAddress = data.UploadAddress
|
||||
var videoId = data.VideoId
|
||||
uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress, videoId)
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 文件上传成功
|
||||
onUploadSucceed: function (uploadInfo) {
|
||||
console.log("onUploadSucceed: " + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object)
|
||||
$('#status').text('文件上传成功!')
|
||||
},
|
||||
// 文件上传失败
|
||||
onUploadFailed: function (uploadInfo, code, message) {
|
||||
console.log("onUploadFailed: file:" + uploadInfo.file.name + ",code:" + code + ", message:" + message)
|
||||
$('#status').text('文件上传失败!')
|
||||
},
|
||||
// 取消文件上传
|
||||
onUploadCanceled: function (uploadInfo, code, message) {
|
||||
console.log("Canceled file: " + uploadInfo.file.name + ", code: " + code + ", message:" + message)
|
||||
$('#status').text('文件上传已暂停!')
|
||||
},
|
||||
// 文件上传进度,单位:字节, 可以在这个函数中拿到上传进度并显示在页面上
|
||||
onUploadProgress: function (uploadInfo, totalSize, progress) {
|
||||
console.log("onUploadProgress:file:" + uploadInfo.file.name + ", fileSize:" + totalSize + ", percent:" + Math.ceil(progress * 100) + "%")
|
||||
var progressPercent = Math.ceil(progress * 100)
|
||||
$('#auth-progress').text(progressPercent)
|
||||
$('#status').text('文件上传中...')
|
||||
},
|
||||
// 上传凭证超时
|
||||
onUploadTokenExpired: function (uploadInfo) {
|
||||
// 上传大文件超时, 如果是上传方式一即根据 UploadAuth 上传时
|
||||
// 需要根据 uploadInfo.videoId 调用刷新视频上传凭证接口(https://help.aliyun.com/document_detail/55408.html)重新获取 UploadAuth
|
||||
// 然后调用 resumeUploadWithAuth 方法, 这里是测试接口, 所以我直接获取了 UploadAuth
|
||||
$('#status').text('文件上传超时!')
|
||||
|
||||
// let refreshUrl = 'https://demo-vod.cn-shanghai.aliyuncs.com/voddemo/RefreshUploadVideo?BusinessType=vodai&TerminalType=pc&DeviceModel=iPhone9,2&UUID=59ECA-4193-4695-94DD-7E1247288&AppVersion=1.0.0&Title=haha1&FileName=xxx.mp4&VideoId=' + uploadInfo.videoId
|
||||
// $.get(refreshUrl, function (data) {
|
||||
// var uploadAuth = data.UploadAuth
|
||||
// uploader.resumeUploadWithAuth(uploadAuth)
|
||||
// console.log('upload expired and resume upload with uploadauth ' + uploadAuth)
|
||||
// }, 'json')
|
||||
|
||||
var refreshUrl = `${_testHost}/users/${login}/video_auths.json?debug=true`
|
||||
|
||||
axios.put(refreshUrl, {
|
||||
video_id: uploadInfo.videoId,
|
||||
}).then((response) => {
|
||||
const data = response.data.data
|
||||
var uploadAuth = data.UploadAuth
|
||||
uploader.resumeUploadWithAuth(uploadAuth)
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
},
|
||||
// 全部文件上传结束
|
||||
onUploadEnd: function (uploadInfo) {
|
||||
$('#status').text('文件上传完毕!')
|
||||
console.log("onUploadEnd: uploaded all the files")
|
||||
}
|
||||
})
|
||||
return uploader
|
||||
}
|
||||
function AliyunUploader(props) {
|
||||
|
||||
useEffect(() => {
|
||||
if (window.AliyunUpload && window.AliyunUpload.Vod) {
|
||||
|
||||
} else {
|
||||
$.getScript(
|
||||
`${_url_origin}/react/${_path}/js/aliyun-upload/lib/es6-promise.min.js`,
|
||||
(data, textStatus, jqxhr) => {
|
||||
$.getScript(
|
||||
`${_url_origin}/react/${_path}/js/aliyun-upload/lib/aliyun-oss-sdk-5.3.1.min.js`,
|
||||
(data, textStatus, jqxhr) => {
|
||||
$.getScript(
|
||||
`${_url_origin}/react/${_path}/js/aliyun-upload/aliyun-upload-sdk-1.5.0.min.js`,
|
||||
(data, textStatus, jqxhr) => {
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$('#fileUpload').on('change', function (e) {
|
||||
var file = e.target.files[0]
|
||||
if (!file) {
|
||||
alert("请先选择需要上传的文件!")
|
||||
return
|
||||
}
|
||||
var Title = file.name
|
||||
var userData = '{"Vod":{}}'
|
||||
if (uploader) {
|
||||
uploader.stopUpload()
|
||||
$('#auth-progress').text('0')
|
||||
$('#status').text("")
|
||||
}
|
||||
if (!uploader) {
|
||||
uploader = createUploader()
|
||||
}
|
||||
// 首先调用 uploader.addFile(event.target.files[i], null, null, null, userData)
|
||||
console.log(uploader)
|
||||
let result = uploader.addFile(file, null, null, null, userData)
|
||||
$('#authUpload').attr('disabled', false)
|
||||
// $('#pauseUpload').attr('disabled', true)
|
||||
// $('#resumeUpload').attr('disabled', true)
|
||||
})
|
||||
return () => {
|
||||
$('#fileUpload').off('change')
|
||||
}
|
||||
}, [])
|
||||
|
||||
let { source, id, className, type } = props;
|
||||
|
||||
function onStop() {
|
||||
$('#resumeUpload').attr('disabled', false)
|
||||
// $('#pauseUpload').attr('disabled', true)
|
||||
uploader.stopUpload()
|
||||
}
|
||||
function onResume() {
|
||||
// $('#resumeUpload').attr('disabled', true)
|
||||
// $('#pauseUpload').attr('disabled', false)
|
||||
uploader.startUpload()
|
||||
}
|
||||
return(
|
||||
<React.Fragment>
|
||||
<div>
|
||||
<input type="file" id="fileUpload"></input>
|
||||
<label class="status">上传状态: <span id="status"></span></label>
|
||||
</div>
|
||||
<div class="upload-type">
|
||||
上传方式一, 使用 UploadAuth 上传:
|
||||
{/* <button id="authUpload" disabled="true">开始上传</button>
|
||||
<button id="pauseUpload" disabled="true" onClick={onStop}>暂停</button>
|
||||
<button id="resumeUpload" disabled="true" onClick={onResume}>恢复上传</button> */}
|
||||
<button id="authUpload" >开始上传</button>
|
||||
<button id="pauseUpload" onClick={onStop}>暂停</button>
|
||||
<button id="resumeUpload" onClick={onResume}>恢复上传</button>
|
||||
<span class="progress">上传进度: <i id="auth-progress">0</i> %</span>
|
||||
<span></span>
|
||||
</div>
|
||||
|
||||
</React.Fragment>
|
||||
)
|
||||
|
||||
}
|
||||
export default AliyunUploader
|
|
@ -1,188 +0,0 @@
|
|||
import React, { useState, useEffect, memo } from 'react';
|
||||
|
||||
import { getUrl2, isDev } from 'educoder'
|
||||
|
||||
const $ = window.$
|
||||
let _url_origin = getUrl2()
|
||||
let _path = _isDev ? 'public' : 'build'
|
||||
|
||||
let uploader
|
||||
|
||||
function createUploader () {
|
||||
uploader = new AliyunUpload.Vod({
|
||||
timeout: $('#timeout').val() || 60000,
|
||||
partSize: $('#partSize').val() || 1048576,
|
||||
parallel: $('#parallel').val() || 5,
|
||||
retryCount: $('#retryCount').val() || 3,
|
||||
retryDuration: $('#retryDuration').val() || 2,
|
||||
region: $('#region').val() || 'ap-southeast-1',
|
||||
userId: $('#userId').val() || 1202060945918292, // 1303984639806000,
|
||||
// 添加文件成功
|
||||
addFileSuccess: function (uploadInfo) {
|
||||
console.log('addFileSuccess')
|
||||
$('#authUpload').attr('disabled', false)
|
||||
$('#resumeUpload').attr('disabled', false)
|
||||
$('#status').text('添加文件成功, 等待上传...')
|
||||
console.log("addFileSuccess: " + uploadInfo.file.name)
|
||||
|
||||
$('#pauseUpload').attr('disabled', false)
|
||||
uploader.startUpload()
|
||||
},
|
||||
// 开始上传
|
||||
onUploadstarted: function (uploadInfo) {
|
||||
// 如果是 UploadAuth 上传方式, 需要调用 uploader.setUploadAuthAndAddress 方法
|
||||
// 如果是 UploadAuth 上传方式, 需要根据 uploadInfo.videoId是否有值,调用点播的不同接口获取uploadauth和uploadAddress
|
||||
// 如果 uploadInfo.videoId 有值,调用刷新视频上传凭证接口,否则调用创建视频上传凭证接口
|
||||
// 注意: 这里是测试 demo 所以直接调用了获取 UploadAuth 的测试接口, 用户在使用时需要判断 uploadInfo.videoId 存在与否从而调用 openApi
|
||||
// 如果 uploadInfo.videoId 存在, 调用 刷新视频上传凭证接口(https://help.aliyun.com/document_detail/55408.html)
|
||||
// 如果 uploadInfo.videoId 不存在,调用 获取视频上传地址和凭证接口(https://help.aliyun.com/document_detail/55407.html)
|
||||
if (!uploadInfo.videoId) {
|
||||
var createUrl = 'https://demo-vod.cn-shanghai.aliyuncs.com/voddemo/CreateUploadVideo?Title=testvod1&FileName=aa.mp4&BusinessType=vodai&TerminalType=pc&DeviceModel=iPhone9,2&UUID=59ECA-4193-4695-94DD-7E1247288&AppVersion=1.0.0&VideoId=5bfcc7864fc14b96972842172207c9e6'
|
||||
$.get(createUrl, function (data) {
|
||||
var uploadAuth = data.UploadAuth
|
||||
var uploadAddress = data.UploadAddress
|
||||
var videoId = data.VideoId
|
||||
uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress,videoId)
|
||||
}, 'json')
|
||||
$('#status').text('文件开始上传...')
|
||||
console.log("onUploadStarted:" + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object)
|
||||
} else {
|
||||
// 如果videoId有值,根据videoId刷新上传凭证
|
||||
// https://help.aliyun.com/document_detail/55408.html?spm=a2c4g.11186623.6.630.BoYYcY
|
||||
var refreshUrl = 'https://demo-vod.cn-shanghai.aliyuncs.com/voddemo/RefreshUploadVideo?BusinessType=vodai&TerminalType=pc&DeviceModel=iPhone9,2&UUID=59ECA-4193-4695-94DD-7E1247288&AppVersion=1.0.0&Title=haha1&FileName=xxx.mp4&VideoId=' + uploadInfo.videoId
|
||||
$.get(refreshUrl, function (data) {
|
||||
var uploadAuth = data.UploadAuth
|
||||
var uploadAddress = data.UploadAddress
|
||||
var videoId = data.VideoId
|
||||
uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress,videoId)
|
||||
}, 'json')
|
||||
}
|
||||
},
|
||||
// 文件上传成功
|
||||
onUploadSucceed: function (uploadInfo) {
|
||||
console.log("onUploadSucceed: " + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object)
|
||||
$('#status').text('文件上传成功!')
|
||||
},
|
||||
// 文件上传失败
|
||||
onUploadFailed: function (uploadInfo, code, message) {
|
||||
console.log("onUploadFailed: file:" + uploadInfo.file.name + ",code:" + code + ", message:" + message)
|
||||
$('#status').text('文件上传失败!')
|
||||
},
|
||||
// 取消文件上传
|
||||
onUploadCanceled: function (uploadInfo, code, message) {
|
||||
console.log("Canceled file: " + uploadInfo.file.name + ", code: " + code + ", message:" + message)
|
||||
$('#status').text('文件上传已暂停!')
|
||||
},
|
||||
// 文件上传进度,单位:字节, 可以在这个函数中拿到上传进度并显示在页面上
|
||||
onUploadProgress: function (uploadInfo, totalSize, progress) {
|
||||
console.log("onUploadProgress:file:" + uploadInfo.file.name + ", fileSize:" + totalSize + ", percent:" + Math.ceil(progress * 100) + "%")
|
||||
var progressPercent = Math.ceil(progress * 100)
|
||||
$('#auth-progress').text(progressPercent)
|
||||
$('#status').text('文件上传中...')
|
||||
},
|
||||
// 上传凭证超时
|
||||
onUploadTokenExpired: function (uploadInfo) {
|
||||
// 上传大文件超时, 如果是上传方式一即根据 UploadAuth 上传时
|
||||
// 需要根据 uploadInfo.videoId 调用刷新视频上传凭证接口(https://help.aliyun.com/document_detail/55408.html)重新获取 UploadAuth
|
||||
// 然后调用 resumeUploadWithAuth 方法, 这里是测试接口, 所以我直接获取了 UploadAuth
|
||||
$('#status').text('文件上传超时!')
|
||||
|
||||
let refreshUrl = 'https://demo-vod.cn-shanghai.aliyuncs.com/voddemo/RefreshUploadVideo?BusinessType=vodai&TerminalType=pc&DeviceModel=iPhone9,2&UUID=59ECA-4193-4695-94DD-7E1247288&AppVersion=1.0.0&Title=haha1&FileName=xxx.mp4&VideoId=' + uploadInfo.videoId
|
||||
$.get(refreshUrl, function (data) {
|
||||
var uploadAuth = data.UploadAuth
|
||||
uploader.resumeUploadWithAuth(uploadAuth)
|
||||
console.log('upload expired and resume upload with uploadauth ' + uploadAuth)
|
||||
}, 'json')
|
||||
},
|
||||
// 全部文件上传结束
|
||||
onUploadEnd: function (uploadInfo) {
|
||||
$('#status').text('文件上传完毕!')
|
||||
console.log("onUploadEnd: uploaded all the files")
|
||||
}
|
||||
})
|
||||
return uploader
|
||||
}
|
||||
function AliyunUploaderDemo(props) {
|
||||
|
||||
useEffect(() => {
|
||||
if (window.AliyunUpload && window.AliyunUpload.Vod) {
|
||||
|
||||
} else {
|
||||
$.getScript(
|
||||
`${_url_origin}/react/${_path}/js/aliyun-upload/lib/es6-promise.min.js`,
|
||||
(data, textStatus, jqxhr) => {
|
||||
$.getScript(
|
||||
`${_url_origin}/react/${_path}/js/aliyun-upload/lib/aliyun-oss-sdk-5.3.1.min.js`,
|
||||
(data, textStatus, jqxhr) => {
|
||||
$.getScript(
|
||||
`${_url_origin}/react/${_path}/js/aliyun-upload/aliyun-upload-sdk-1.5.0.min.js`,
|
||||
(data, textStatus, jqxhr) => {
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$('#fileUpload').on('change', function (e) {
|
||||
var file = e.target.files[0]
|
||||
if (!file) {
|
||||
alert("请先选择需要上传的文件!")
|
||||
return
|
||||
}
|
||||
var Title = file.name
|
||||
var userData = '{"Vod":{}}'
|
||||
if (uploader) {
|
||||
uploader.stopUpload()
|
||||
$('#auth-progress').text('0')
|
||||
$('#status').text("")
|
||||
}
|
||||
if (!uploader) {
|
||||
uploader = createUploader()
|
||||
}
|
||||
// 首先调用 uploader.addFile(event.target.files[i], null, null, null, userData)
|
||||
console.log(uploader)
|
||||
uploader.addFile(file, null, null, null, userData)
|
||||
$('#authUpload').attr('disabled', false)
|
||||
// $('#pauseUpload').attr('disabled', true)
|
||||
// $('#resumeUpload').attr('disabled', true)
|
||||
})
|
||||
// return () => {
|
||||
|
||||
// }
|
||||
}, [])
|
||||
|
||||
let { source, id, className, type } = props;
|
||||
|
||||
function onStop() {
|
||||
$('#resumeUpload').attr('disabled', false)
|
||||
// $('#pauseUpload').attr('disabled', true)
|
||||
uploader.stopUpload()
|
||||
}
|
||||
function onResume() {
|
||||
// $('#resumeUpload').attr('disabled', true)
|
||||
// $('#pauseUpload').attr('disabled', false)
|
||||
uploader.startUpload()
|
||||
}
|
||||
return(
|
||||
<React.Fragment>
|
||||
<div>
|
||||
<input type="file" id="fileUpload"></input>
|
||||
<label class="status">上传状态: <span id="status"></span></label>
|
||||
</div>
|
||||
<div class="upload-type">
|
||||
上传方式一, 使用 UploadAuth 上传:
|
||||
{/* <button id="authUpload" disabled="true">开始上传</button>
|
||||
<button id="pauseUpload" disabled="true" onClick={onStop}>暂停</button>
|
||||
<button id="resumeUpload" disabled="true" onClick={onResume}>恢复上传</button> */}
|
||||
<button id="authUpload" >开始上传</button>
|
||||
<button id="pauseUpload" onClick={onStop}>暂停</button>
|
||||
<button id="resumeUpload" onClick={onResume}>恢复上传</button>
|
||||
<span class="progress">上传进度: <i id="auth-progress">0</i> %</span>
|
||||
<span></span>
|
||||
</div>
|
||||
|
||||
</React.Fragment>
|
||||
)
|
||||
|
||||
}
|
||||
export default AliyunUploaderDemo
|
|
@ -1,110 +0,0 @@
|
|||
import React,{ Component } from "react";
|
||||
import { getUrl2 } from "educoder";
|
||||
import ReactPlayer from 'react-player'
|
||||
|
||||
const $ = window.$
|
||||
let _url_origin = getUrl2()
|
||||
|
||||
class Clappr extends Component{
|
||||
constructor(props){
|
||||
super(props);
|
||||
this.state={
|
||||
}
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this['player'+this.props.id] && this['player'+this.props.id].destroy()
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
return;
|
||||
|
||||
const source = this.props.source || "http://your.video/here.mp4"
|
||||
const { id, type } = this.props
|
||||
const _id = `#_player${id}`
|
||||
|
||||
if (!window['Clappr'] && window['ClapprLoading'] == true) {
|
||||
setTimeout(() => {
|
||||
this.componentDidMount()
|
||||
}, 300)
|
||||
return;
|
||||
}
|
||||
// && window['clappr-playback-rate-plugin']
|
||||
if (window['Clappr'] ) {
|
||||
// https://github.com/clappr/clappr/issues/1839
|
||||
// http://clappr.github.io/classes/Player.html#method_mute
|
||||
this['player'+id] = new window.Clappr.Player({
|
||||
source: source, parentId: _id,
|
||||
height: type == 'mp3' ? 60 : 360,
|
||||
hideMediaControl: type == 'mp3' ? false : true,
|
||||
// plugins: {
|
||||
// 'core': [window.Clappr.MediaControl, window['clappr-playback-rate-plugin'].default]
|
||||
// }
|
||||
});
|
||||
} else {
|
||||
window['ClapprLoading'] = true;
|
||||
$.getScript(
|
||||
`${_url_origin}/javascripts/media/clappr.min.js`,
|
||||
(data, textStatus, jqxhr) => {
|
||||
window.clappr = window.Clappr
|
||||
// $.getScript(
|
||||
// `${_url_origin}/javascripts/media/clappr-playback-rate-plugin.min.js`,
|
||||
// (data, textStatus, jqxhr) => {
|
||||
this['player'+id] = new window.Clappr.Player({
|
||||
source: source, parentId: _id,
|
||||
height: type == 'mp3' ? 60 : 360,
|
||||
hideMediaControl: type == 'mp3' ? false : true,
|
||||
// plugins: {
|
||||
// 'core': [window.Clappr.MediaControl, window['clappr-playback-rate-plugin'].default]
|
||||
// }
|
||||
});
|
||||
|
||||
// })
|
||||
|
||||
});
|
||||
|
||||
//
|
||||
// $.when(
|
||||
// $.getScript( `${_url_origin}/javascripts/media/clappr.min.js` ),
|
||||
// // $.getScript( `${_url_origin}/javascripts/media/clappr-thumbnails-plugin.js` ),
|
||||
// $.getScript( `${_url_origin}/javascripts/media/clappr-playback-rate-plugin.min.js` ),
|
||||
// $.Deferred(function( deferred ){
|
||||
// $( deferred.resolve );
|
||||
// })
|
||||
// ).done(function(){
|
||||
// //place your code here, the scripts are all loaded
|
||||
// const player = new window.Clappr.Player({
|
||||
// source: source, parentId: _id,
|
||||
// plugins: {
|
||||
// 'core': [window.Clappr.MediaControl, window.Clappr.Playback]
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
|
||||
let { source, id, className, type } = this.props;
|
||||
const _id = `_player${id}`
|
||||
return(
|
||||
<React.Fragment>
|
||||
{/* https://github.com/CookPete/react-player/issues/686 */}
|
||||
<ReactPlayer url={source} playing={false} controls={true} width={400} height={ type == 'mp3' ? 55 : 290}/>
|
||||
|
||||
{/* <style>{`
|
||||
.playback_rate {
|
||||
margin-right: 16px;
|
||||
}
|
||||
`}</style>
|
||||
<div id={_id} className={className + ' ' + type}></div> */}
|
||||
|
||||
{/* 原生 */}
|
||||
{/* { type == 'mp3' ? <audio src={source} preload controls></audio>
|
||||
: <video src={source} controls="controls">
|
||||
您的浏览器不支持 video 标签。
|
||||
</video>} */}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default Clappr;
|
|
@ -1,20 +0,0 @@
|
|||
import React from 'react'
|
||||
|
||||
export const themes = {
|
||||
light: {
|
||||
foreground: '#000000',
|
||||
background: '#eeeeee',
|
||||
foreground_select: '#4CACFF',
|
||||
foreground_orange1: '#FF6800',
|
||||
foreground_tip: '#333',
|
||||
|
||||
},
|
||||
dark: {
|
||||
foreground: '#ffffff',
|
||||
background: '#222222',
|
||||
},
|
||||
};
|
||||
|
||||
export const ThemeContext = React.createContext(
|
||||
themes.light // default value
|
||||
);
|
|
@ -1,29 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
import {Link} from 'react-router-dom'
|
||||
|
||||
const map={"blue":"blueFull","greyBack":"greyBack","grey":"greyWidthFixed","green":"greenBack",'greyLine':"greyLine",'orangeLine':"orangeLine",
|
||||
'colorBlue': 'colorBlue', // 蓝字白底
|
||||
}
|
||||
class ActionBtn extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
let{to, children, style, className, ...others}=this.props
|
||||
return(
|
||||
<React.Fragment>
|
||||
{
|
||||
to==undefined ?
|
||||
<a href="javascript:void(0)" onClick={this.props.onClick}
|
||||
{...others}
|
||||
className={"Actionbtn "+`${map[style || 'blue']} ${this.props.className}`}
|
||||
>{children}</a>
|
||||
:
|
||||
<Link to={to} className={"btn "+`${map[this.props.style]} ${this.props.className}`} {...others}>{this.props.children}</Link>
|
||||
}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default ActionBtn;
|
|
@ -1,31 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
import {Link} from 'react-router-dom'
|
||||
|
||||
const map={"blue":"colorblue","white":"colorwhite","grey":"colorgrey", 'orange': "color-orange",'colorgrey9':'color-grey-9'}
|
||||
class WordsBtn extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
let{to, href,targets, style2, style, className, ...others }=this.props
|
||||
return(
|
||||
<React.Fragment>
|
||||
{
|
||||
to==undefined&&targets==undefined ?
|
||||
<a href={href || "javascript:void(0)"} onClick={this.props.onClick} className={"btn "+`${map[this.props.style]} ${this.props.className}`}
|
||||
style={style2} {...others}
|
||||
>{this.props.children}</a>:
|
||||
targets!=undefined? <a href={to} target="_blank" className={"btn "+`${map[this.props.style]} ${this.props.className}`}
|
||||
style={style2} {...others}
|
||||
>{this.props.children}</a>
|
||||
:
|
||||
<Link to={to} className={"btn "+`${map[this.props.style]} ${this.props.className}`}
|
||||
style={style2} {...others}
|
||||
>{this.props.children}</Link>
|
||||
}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default WordsBtn;
|
|
@ -1,77 +0,0 @@
|
|||
//import { from } from '_array-flatten@2.1.2@array-flatten';
|
||||
|
||||
// export { default as OrderStateUtil } from '../routes/Order/components/OrderStateUtil';
|
||||
|
||||
export { getImageUrl as getImageUrl,getmyUrl as getmyUrl, getRandomNumber as getRandomNumber,getUrl as getUrl, publicSearchs as publicSearchs,getRandomcode as getRandomcode,getUrlmys as getUrlmys, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
|
||||
, getUploadActionUrl as getUploadActionUrl,getUploadActionUrltwo as getUploadActionUrltwo ,getUploadActionUrlthree as getUploadActionUrlthree, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth
|
||||
, getTaskUrlById as getTaskUrlById, TEST_HOST ,htmlEncode as htmlEncode ,getupload_git_file as getupload_git_file} from './UrlTool';
|
||||
|
||||
export {setmiyah as setmiyah} from './Component';
|
||||
export { default as queryString } from './UrlTool2';
|
||||
|
||||
export { SnackbarHOC as SnackbarHOC } from './SnackbarHOC';
|
||||
|
||||
export { trigger as trigger, on as on, off as off
|
||||
, broadcastChannelPostMessage, broadcastChannelOnmessage } from './EventUtil';
|
||||
|
||||
export { updatePageParams as updatePageParams } from './RouterUtil';
|
||||
|
||||
export { bytesToSize as bytesToSize } from './UnitUtil';
|
||||
|
||||
export { markdownToHTML, uploadNameSizeSeperator, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll, isImageExtension,
|
||||
downloadFile, sortDirections } from './TextUtil'
|
||||
export { handleDateString, getNextHalfHourOfMoment,formatDuring } from './DateUtil'
|
||||
|
||||
export { configShareForIndex, configShareForPaths, configShareForShixuns, configShareForCourses, configShareForCustom } from './util/ShareUtil'
|
||||
|
||||
export { isDev as isDev, isMobile } from './Env'
|
||||
|
||||
export { toStore as toStore, fromStore as fromStore } from './Store'
|
||||
|
||||
export { trace_collapse, trace, debug, info, warn, error, trace_c, debug_c, info_c, warn_c, error_c } from './LogUtil'
|
||||
|
||||
export { EDU_ADMIN, EDU_BUSINESS, EDU_SHIXUN_MANAGER, EDU_SHIXUN_MEMBER, EDU_CERTIFICATION_TEACHER
|
||||
, EDU_GAME_MANAGER, EDU_TEACHER, EDU_NORMAL} from './Const'
|
||||
|
||||
|
||||
export { default as AttachmentList } from './components/attachment/AttachmentList'
|
||||
|
||||
export { themes, ThemeContext } from './context/ThemeContext'
|
||||
|
||||
export { ModalHOC } from './components/ModalHOC'
|
||||
|
||||
export { SetAppModel } from './components/SetAppModel'
|
||||
|
||||
export { default as LinkAfterLogin } from './components/LinkAfterLogin'
|
||||
export { default as Cropper } from './components/Cropper'
|
||||
export { default as ConditionToolTip } from './components/ConditionToolTip'
|
||||
// export { default as DragValidator } from './components/DragValidator'
|
||||
|
||||
export { default as PopInstruction } from './components/instruction/PopInstruction'
|
||||
|
||||
export { default as City } from './components/form/City'
|
||||
|
||||
|
||||
// course
|
||||
export { default as WordsBtn } from './course/WordsBtn'
|
||||
|
||||
export { default as ActionBtn } from './course/ActionBtn'
|
||||
|
||||
export { default as MarkdownToHtml } from './components/markdown/MarkdownToHtml'
|
||||
|
||||
export { default as DMDEditor } from './components/markdown/DMDEditor'
|
||||
|
||||
export { default as Clappr } from './components/media/Clappr'
|
||||
export { default as AliyunUploader } from './components/media/AliyunUploader'
|
||||
|
||||
|
||||
export { default as ImageLayer2 } from './hooks/ImageLayer2'
|
||||
|
||||
// 外部
|
||||
export { default as CBreadcrumb } from '../modules/courses/common/CBreadcrumb'
|
||||
export { CNotificationHOC as CNotificationHOC } from '../modules/courses/common/CNotificationHOC'
|
||||
export { default as ModalWrapper } from '../modules/courses/common/ModalWrapper'
|
||||
export { default as NoneData } from '../modules/courses/coursesPublic/NoneData'
|
||||
|
||||
export {default as WordNumberTextarea} from '../modules/modals/WordNumberTextarea'
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
import React, { useState, useEffect, memo } from 'react';
|
||||
import ImageLayer from '../../modules/page/layers/ImageLayer';
|
||||
import { isImageExtension } from 'educoder';
|
||||
const $ = window.$;
|
||||
function ImageLayer2(props) {
|
||||
const [showImage, setShowImage] = useState(false)
|
||||
const [imageSrc, setImageSrc] = useState('')
|
||||
|
||||
const { parentSel, childSel, watchPropsArray } = props
|
||||
|
||||
const onImageLayerClose = () => {
|
||||
setShowImage(false)
|
||||
setImageSrc('')
|
||||
}
|
||||
const onDelegateClick = (event) => {
|
||||
const imageSrc = event.target.src || event.target.getAttribute('src') || event.target.getAttribute('href')
|
||||
// 判断imageSrc是否是图片
|
||||
const fileName = event.target.innerHTML.trim()
|
||||
if (isImageExtension(imageSrc.trim()) || isImageExtension(fileName) || event.target.tagName == 'IMG' || imageSrc.indexOf('base64,') != -1) {
|
||||
// 非回复里的头像图片; 非emoticons
|
||||
if (imageSrc.indexOf('/images/avatars/User') === -1 &&
|
||||
imageSrc.indexOf('kindeditor/plugins/emoticons') === -1 ) {
|
||||
setShowImage(true)
|
||||
setImageSrc(imageSrc)
|
||||
}
|
||||
event.stopPropagation()
|
||||
event.preventDefault && event.preventDefault()
|
||||
event.originalEvent.preventDefault()
|
||||
// event.originalEvent.stopPropagation()
|
||||
// event.originalEvent.cancelBubble = true
|
||||
return false;
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
$(parentSel)
|
||||
.delegate(childSel, "click", onDelegateClick);
|
||||
|
||||
return () => {
|
||||
$(parentSel).undelegate(childSel, "click", onDelegateClick )
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<ImageLayer showImage={showImage} imageSrc={imageSrc} onImageLayerClose={onImageLayerClose}></ImageLayer>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(ImageLayer2)
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* @Description: 填空
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2020-01-06 09:02:29
|
||||
* @LastEditors : tangjiang
|
||||
* @LastEditTime : 2020-02-05 10:44:01
|
||||
*/
|
||||
import Quill from 'quill';
|
||||
let Inline = Quill.import('blots/inline');
|
||||
// const BlockEmbed = Quill.import('blots/embed');
|
||||
class FillBlot extends Inline {
|
||||
static create (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.nodeValue = value.text;
|
||||
return node;
|
||||
}
|
||||
|
||||
static value (node) {
|
||||
return {
|
||||
// dataSet: node.getAttribute('data-fill'),
|
||||
data_index: node.getAttribute('data_index')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FillBlot.blotName = "fill";
|
||||
FillBlot.tagName = "span";
|
||||
|
||||
export default FillBlot;
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* @Description: 重写图片
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-12-16 15:50:45
|
||||
* @LastEditors : tangjiang
|
||||
* @LastEditTime : 2019-12-31 13:59:02
|
||||
*/
|
||||
import Quill from "quill";
|
||||
|
||||
const BlockEmbed = Quill.import('blots/block/embed');
|
||||
|
||||
export default class ImageBlot extends BlockEmbed {
|
||||
|
||||
static create(value) {
|
||||
|
||||
const node = super.create();
|
||||
node.setAttribute('alt', value.alt);
|
||||
node.setAttribute('src', value.url);
|
||||
// console.log('~~~~~~~~~~~', node, value);
|
||||
node.addEventListener('click', function () {
|
||||
value.onclick(value.url);
|
||||
}, false);
|
||||
if (value.width) {
|
||||
node.setAttribute('width', value.width);
|
||||
}
|
||||
if (value.height) {
|
||||
node.setAttribute('height', value.height);
|
||||
}
|
||||
|
||||
if (value.id) {
|
||||
node.setAttribute('id', value.id);
|
||||
}
|
||||
// 宽度和高度都不存在时,
|
||||
if (!value.width && !value.height) {
|
||||
// node.setAttribute('display', 'block');
|
||||
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;
|
||||
}
|
||||
|
||||
// 获取节点值
|
||||
static value (node) {
|
||||
|
||||
return {
|
||||
alt: node.getAttribute('alt'),
|
||||
url: node.getAttribute('src'),
|
||||
onclick: node.onclick,
|
||||
width: node.width,
|
||||
height: node.height,
|
||||
display: node.getAttribute('display'),
|
||||
id: node.id,
|
||||
// style: node.style
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
ImageBlot.blotName = 'image';
|
||||
ImageBlot.tagName = 'img';
|
|
@ -1,95 +0,0 @@
|
|||
<!--
|
||||
* @Description:
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2020-01-06 16:20:03
|
||||
* @LastEditors : tangjiang
|
||||
* @LastEditTime : 2020-01-09 09:45:29
|
||||
-->
|
||||
## QuillForEditor 使用 [https://quilljs.com/]
|
||||
|
||||
### 导入
|
||||
|
||||
- 目录 src/common/quillForEditor (默认加载当前文件夹下的 index.js 文件)
|
||||
|
||||
### 参数
|
||||
|
||||
| 字段 | 描述 |
|
||||
| ----- | ----- |
|
||||
| placeholder | 提示信息 |
|
||||
| readOnly | 只读(只读取模式时,没有 工具栏且内容不可编辑,通常用于展示quill内容) |
|
||||
| autoFocus | 自动获得焦点 |
|
||||
| options | 配置参数, 指定工具栏内容 |
|
||||
| value | 文本编辑器内容 |
|
||||
| imgAttrs | 指定上传图片的尺寸 { width: 'xxpx}, height: 'xxpx'|
|
||||
| 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>
|
||||
````
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
function deepEqual (prev, current) {
|
||||
if (prev === current) { // 基本类型比较,值,类型都相同 或者同为 null or undefined
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((!prev && current)
|
||||
|| (prev && !current)
|
||||
|| (!prev && !current)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(prev)) {
|
||||
if (!Array.isArray(current)) return false;
|
||||
if (prev.length !== current.length) return false;
|
||||
|
||||
for (let i = 0; i < prev.length; i++) {
|
||||
if (!deepEqual(current[i], prev[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof current === 'object') {
|
||||
if (typeof prev !== 'object') return false;
|
||||
const prevKeys = Object.keys(prev);
|
||||
const curKeys = Object.keys(current);
|
||||
|
||||
if (prevKeys.length !== curKeys.length) return false;
|
||||
|
||||
prevKeys.sort();
|
||||
curKeys.sort();
|
||||
|
||||
for (let i = 0; i < prevKeys.length; i++) {
|
||||
if (prevKeys[i] !== curKeys[i]) return false;
|
||||
const key = prevKeys[i];
|
||||
if (!deepEqual(prev[key], current[key])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export default deepEqual;
|
|
@ -1,280 +0,0 @@
|
|||
/*
|
||||
* @Description: quill 编辑器
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-12-18 08:49:30
|
||||
* @LastEditors : tangjiang
|
||||
* @LastEditTime : 2020-02-05 11:23:03
|
||||
*/
|
||||
import './index.scss';
|
||||
import 'quill/dist/quill.core.css'; // 核心样式
|
||||
import 'quill/dist/quill.snow.css'; // 有工具栏
|
||||
import 'quill/dist/quill.bubble.css'; // 无工具栏
|
||||
import 'katex/dist/katex.min.css'; // katex 表达式样式
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import Quill from 'quill';
|
||||
import katex from 'katex';
|
||||
import deepEqual from './deepEqual.js'
|
||||
import { fetchUploadImage } from '../../services/ojService.js';
|
||||
import { getImageUrl } from 'educoder'
|
||||
import ImageBlot from './ImageBlot';
|
||||
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'];
|
||||
|
||||
window.Quill = Quill;
|
||||
window.katex = katex;
|
||||
Quill.register(ImageBlot);
|
||||
Quill.register(Size);
|
||||
Quill.register(Font, true);
|
||||
// Quill.register({'modules/toolbar': Toolbar});
|
||||
Quill.register({
|
||||
'formats/fill': FillBlot
|
||||
});
|
||||
// Quill.register(Color);
|
||||
|
||||
|
||||
function QuillForEditor ({
|
||||
placeholder,
|
||||
readOnly,
|
||||
autoFocus = false,
|
||||
options,
|
||||
value,
|
||||
imgAttrs = {}, // 指定图片的宽高
|
||||
style = {},
|
||||
wrapStyle = {},
|
||||
showUploadImage,
|
||||
onContentChange,
|
||||
addFill, // 点击填空成功的回调
|
||||
deleteFill // 删除填空,返回删除的下标
|
||||
// getQuillContent
|
||||
}) {
|
||||
// toolbar 默认值
|
||||
const defaultConfig = [
|
||||
'bold', 'italic', 'underline',
|
||||
{size: ['12px', '14px', '16px', '18px', '20px']},
|
||||
{align: []}, {list: 'ordered'}, {list: 'bullet'}, // 列表
|
||||
{script: 'sub'}, {script: 'super'},
|
||||
{ 'color': [] }, { 'background': [] },
|
||||
{header: [1,2,3,4,5,false]},
|
||||
'blockquote', 'code-block',
|
||||
'link', 'image', 'video',
|
||||
'formula',
|
||||
'clean'
|
||||
];
|
||||
|
||||
const editorRef = useRef(null);
|
||||
// quill 实例
|
||||
const [quill, setQuill] = useState(null);
|
||||
const [selection, setSelection] = useState(null);
|
||||
const [fillCount, setFillCount] = useState(0);
|
||||
const [quillCtx, setQuillCtx] = useState({});
|
||||
|
||||
// 文本内容变化时
|
||||
const handleOnChange = content => {
|
||||
// getQuillContent && getQuillContent(quill);
|
||||
onContentChange && onContentChange(content, quill);
|
||||
};
|
||||
|
||||
const renderOptions = options || defaultConfig;
|
||||
|
||||
const bindings = {
|
||||
tab: {
|
||||
key: 9,
|
||||
handler: function () {
|
||||
console.log('调用了tab=====>>>>');
|
||||
}
|
||||
},
|
||||
backspace: {
|
||||
key: 'Backspace',
|
||||
/**
|
||||
* @param {*} range
|
||||
* { index, // 删除元素的位置
|
||||
* length // 删除元素的个数, 当删除一个时, length=0, 其它等于删除的元素的个数
|
||||
* }
|
||||
* @param {*} context 上下文
|
||||
*/
|
||||
handler: function (range, context) {
|
||||
/**
|
||||
* index: 删除元素的位置
|
||||
* length: 删除元素的个数
|
||||
*/
|
||||
const {index, length} = range;
|
||||
const _start = length === 0 ? index - 1 : index;
|
||||
const _length = length || 1;
|
||||
let delCtx = this.quill.getText(_start, _length); // 删除的元素
|
||||
// aa
|
||||
const reg = /▁/g;
|
||||
const delArrs = delCtx.match(reg);
|
||||
if (delArrs) {
|
||||
const r = window.confirm('确定要删除吗?');
|
||||
if (r) {
|
||||
let leaveCtx; // 获取删除元素之前的内容
|
||||
if (length === 0) {
|
||||
leaveCtx = this.quill.getText(0, index - 1);
|
||||
} else {
|
||||
leaveCtx = this.quill.getText(0, index);
|
||||
}
|
||||
const leaveArrs = leaveCtx.match(reg);
|
||||
const leaveLen = (leaveArrs || []).length;
|
||||
let delIndexs = [];
|
||||
// 获取删除元素的下标
|
||||
delArrs.forEach((item, i) => {
|
||||
leaveLen === 0 ? delIndexs.push(i) : delIndexs.push(leaveLen + i);
|
||||
});
|
||||
deleteFill && deleteFill(delIndexs); // 调用删除回调, 返回删除的元素下标[]
|
||||
return true
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
// quill 配置信息
|
||||
const quillOption = {
|
||||
modules: {
|
||||
toolbar: renderOptions,
|
||||
keyboard: {
|
||||
bindings: bindings
|
||||
}
|
||||
// toolbar: {
|
||||
// container: renderOptions
|
||||
// }
|
||||
},
|
||||
readOnly,
|
||||
placeholder,
|
||||
theme: readOnly ? 'bubble' : 'snow',
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const quillNode = document.createElement('div');
|
||||
editorRef.current.appendChild(quillNode);
|
||||
const _quill = new Quill(editorRef.current, quillOption);
|
||||
|
||||
setQuill(_quill);
|
||||
// 处理图片上传功能
|
||||
_quill.getModule('toolbar').addHandler('image', (e) => {
|
||||
const input = document.createElement('input');
|
||||
input.setAttribute('type', 'file');
|
||||
input.setAttribute('accept', 'image/*');
|
||||
input.click();
|
||||
|
||||
input.onchange = async (e) => {
|
||||
const file = input.files[0]; // 获取文件信息
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const range = _quill.getSelection(true);
|
||||
let fileUrl = ''; // 保存上传成功后图片的url
|
||||
// 上传文件
|
||||
const result = await fetchUploadImage(formData);
|
||||
// 获取上传图片的url
|
||||
if (result.data && result.data.id) {
|
||||
fileUrl = getImageUrl(`api/attachments/${result.data.id}`);
|
||||
}
|
||||
// 根据id获取文件路径
|
||||
const { width, height } = imgAttrs;
|
||||
// console.log('上传图片的url:', fileUrl);
|
||||
if (fileUrl) {
|
||||
_quill.insertEmbed(range.index, 'image', {
|
||||
url: fileUrl,
|
||||
alt: '图片信息',
|
||||
onClick: showUploadImage,
|
||||
width,
|
||||
height
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
// 处理填空
|
||||
_quill.getModule('toolbar').addHandler('fill', (e) => {
|
||||
// alert(1111);
|
||||
setFillCount(fillCount + 1);
|
||||
const range = _quill.getSelection(true);
|
||||
_quill.insertText(range.index, '▁');
|
||||
addFill && addFill(); // 调用添加回调
|
||||
});
|
||||
}, []);
|
||||
|
||||
// 设置值
|
||||
useEffect(() => {
|
||||
if (!quill) return
|
||||
|
||||
const previous = quill.getContents()
|
||||
|
||||
if (value && value.hasOwnProperty('ops')) {
|
||||
// console.log(value.ops);
|
||||
const ops = value.ops || [];
|
||||
ops.forEach((item, i) => {
|
||||
if (item.insert['image']) {
|
||||
item.insert['image'] = Object.assign({}, item.insert['image'], {style: { cursor: 'pointer' }, onclick: (url) => showUploadImage(url)});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const current = value
|
||||
if (!deepEqual(previous, current)) {
|
||||
setSelection(quill.getSelection())
|
||||
if (typeof value === 'string' && value) {
|
||||
// debugger
|
||||
quill.clipboard.dangerouslyPasteHTML(value, 'api');
|
||||
if (autoFocus) {
|
||||
quill.focus();
|
||||
} else {
|
||||
quill.blur();
|
||||
}
|
||||
} else {
|
||||
quill.setContents(value)
|
||||
if (autoFocus) quill.focus();
|
||||
}
|
||||
}
|
||||
}, [quill, value, setQuill, autoFocus]);
|
||||
|
||||
// 清除选择区域
|
||||
useEffect(() => {
|
||||
if (quill && selection) {
|
||||
quill.setSelection(selection)
|
||||
setSelection(null)
|
||||
}
|
||||
}, [quill, selection, setSelection]);
|
||||
|
||||
// 设置placeholder值
|
||||
useEffect(() => {
|
||||
if (!quill || !quill.root) return;
|
||||
quill.root.dataset.placeholder = placeholder;
|
||||
}, [quill, placeholder]);
|
||||
|
||||
// 处理内容变化
|
||||
useEffect(() => {
|
||||
if (!quill) return;
|
||||
if (typeof handleOnChange !== 'function') return;
|
||||
let handler;
|
||||
quill.on(
|
||||
'text-change',
|
||||
(handler = (delta, oldDelta, source) => {
|
||||
const _ctx = quill.getContents();
|
||||
setQuillCtx(_ctx);
|
||||
handleOnChange(quill.getContents()); // getContents: 检索编辑器内容
|
||||
})
|
||||
);
|
||||
return () => {
|
||||
quill.off('text-change', handler);
|
||||
}
|
||||
}, [quill, handleOnChange]);
|
||||
|
||||
// 返回结果
|
||||
return (
|
||||
<div className='quill_editor_for_react_area' style={wrapStyle}>
|
||||
<div ref={editorRef} style={style}></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default QuillForEditor;
|
|
@ -1,132 +0,0 @@
|
|||
.quill_editor_for_react_area{
|
||||
// background: #fff;
|
||||
// margin: 0 15px;
|
||||
.ql-editing{
|
||||
left: 0 !important;
|
||||
}
|
||||
.ql-editor{
|
||||
img{
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="12px"]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="12px"]::before {
|
||||
content: '12px';
|
||||
font-size: 12px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="14px"]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="14px"]::before {
|
||||
content: '14px';
|
||||
font-size: 14px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="16px"]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="16px"]::before {
|
||||
content: '16px';
|
||||
font-size: 16px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="18px"]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="18px"]::before {
|
||||
content: '18px';
|
||||
font-size: 18px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="20px"]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="20px"]::before {
|
||||
content: '20px';
|
||||
font-size: 20px;
|
||||
}
|
||||
//默认的样式
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
|
||||
content: '14px';
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimSun]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimSun]::before {
|
||||
content: "宋体";
|
||||
font-family: "SimSun";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimHei]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimHei]::before {
|
||||
content: "黑体";
|
||||
font-family: "SimHei";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Microsoft-YaHei]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Microsoft-YaHei]::before {
|
||||
content: "微软雅黑";
|
||||
font-family: "Microsoft YaHei";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=KaiTi]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=KaiTi]::before {
|
||||
content: "楷体";
|
||||
font-family: "KaiTi";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=FangSong]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=FangSong]::before {
|
||||
content: "仿宋";
|
||||
font-family: "FangSong";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Arial]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Arial]::before {
|
||||
content: "Arial";
|
||||
font-family: "Arial";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Times-New-Roman]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Times-New-Roman]::before {
|
||||
content: "Times New Roman";
|
||||
font-family: "Times New Roman";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=sans-serif]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=sans-serif]::before {
|
||||
content: "sans-serif";
|
||||
font-family: "sans-serif";
|
||||
}
|
||||
|
||||
.ql-font-SimSun {
|
||||
font-family: "SimSun";
|
||||
}
|
||||
.ql-font-SimHei {
|
||||
font-family: "SimHei";
|
||||
}
|
||||
.ql-font-Microsoft-YaHei {
|
||||
font-family: "Microsoft YaHei";
|
||||
}
|
||||
.ql-font-KaiTi {
|
||||
font-family: "KaiTi";
|
||||
}
|
||||
.ql-font-FangSong {
|
||||
font-family: "FangSong";
|
||||
}
|
||||
.ql-font-Arial {
|
||||
font-family: "Arial";
|
||||
}
|
||||
.ql-font-Times-New-Roman {
|
||||
font-family: "Times New Roman";
|
||||
}
|
||||
.ql-font-sans-serif {
|
||||
font-family: "sans-serif";
|
||||
}
|
||||
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* @Description: 重写图片
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-12-16 15:50:45
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-12-17 16:44:48
|
||||
*/
|
||||
import Quill from "quill";
|
||||
|
||||
const BlockEmbed = Quill.import('blots/block/embed');
|
||||
|
||||
export default class ImageBlot extends BlockEmbed {
|
||||
|
||||
static create(value) {
|
||||
|
||||
const node = super.create();
|
||||
|
||||
node.setAttribute('alt', value.alt);
|
||||
node.setAttribute('src', value.url);
|
||||
|
||||
if (value.width) {
|
||||
node.setAttribute('width', value.width);
|
||||
}
|
||||
if (value.height) {
|
||||
node.setAttribute('height', value.height);
|
||||
}
|
||||
// 宽度和高度都不存在时,
|
||||
if (!value.width && !value.height) {
|
||||
node.setAttribute('display', 'block');
|
||||
node.setAttribute('width', '100%');
|
||||
}
|
||||
// 给图片添加点击事件
|
||||
node.onclick = () => {
|
||||
value.onClick && value.onClick(value.url);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static value (node) {
|
||||
|
||||
return {
|
||||
alt: node.getAttribute('alt'),
|
||||
url: node.getAttribute('src'),
|
||||
onclick: node.onclick,
|
||||
// width: node.width,
|
||||
// height: node.height,
|
||||
display: node.getAttribute('display')
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
ImageBlot.blotName = 'image';
|
||||
ImageBlot.tagName = 'img';
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* @Description:
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-12-09 09:09:42
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-12-18 08:46:20
|
||||
*/
|
||||
import 'quill/dist/quill.core.css'; // 核心样式
|
||||
import 'quill/dist/quill.snow.css'; // 有工具栏
|
||||
import 'quill/dist/quill.bubble.css'; // 无工具栏
|
||||
import 'katex/dist/katex.min.css'; // katex 表达式样式
|
||||
import React, { useState, useReducer, useEffect } from 'react';
|
||||
import useQuill from './useQuill';
|
||||
|
||||
function ReactQuill ({
|
||||
disallowColors, // 不可见时颜色
|
||||
placeholder, // 提示信息
|
||||
uploadImage, // 图片上传
|
||||
onChange, // 内容变化时
|
||||
options, // 配置信息
|
||||
value, // 显示的内容
|
||||
style,
|
||||
showUploadImage // 显示上传图片
|
||||
}) {
|
||||
|
||||
const [element, setElement] = useState(); // quill 渲染节点
|
||||
|
||||
useQuill({
|
||||
disallowColors,
|
||||
placeholder,
|
||||
uploadImage,
|
||||
onChange,
|
||||
options,
|
||||
value,
|
||||
showUploadImage,
|
||||
element
|
||||
});
|
||||
|
||||
return (
|
||||
<div className='react_quill_area' ref={setElement} style={style}/>
|
||||
);
|
||||
}
|
||||
|
||||
export default ReactQuill;
|
|
@ -1,47 +0,0 @@
|
|||
function deepEqual (prev, current) {
|
||||
if (prev === current) { // 基本类型比较,值,类型都相同 或者同为 null or undefined
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((!prev && current)
|
||||
|| (prev && !current)
|
||||
|| (!prev && !current)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(prev)) {
|
||||
if (!Array.isArray(current)) return false;
|
||||
if (prev.length !== current.length) return false;
|
||||
|
||||
for (let i = 0; i < prev.length; i++) {
|
||||
if (!deepEqual(current[i], prev[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof current === 'object') {
|
||||
if (typeof prev !== 'object') return false;
|
||||
const prevKeys = Object.keys(prev);
|
||||
const curKeys = Object.keys(current);
|
||||
|
||||
if (prevKeys.length !== curKeys.length) return false;
|
||||
|
||||
prevKeys.sort();
|
||||
curKeys.sort();
|
||||
|
||||
for (let i = 0; i < prevKeys.length; i++) {
|
||||
if (prevKeys[i] !== curKeys[i]) return false;
|
||||
const key = prevKeys[i];
|
||||
if (!deepEqual(prev[key], current[key])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export default deepEqual;
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* @Description: 将多维数组转变成一维数组
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-12-09 09:35:01
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-12-16 11:36:22
|
||||
*/
|
||||
function flatten (array) {
|
||||
return flatten.rec(array, []);
|
||||
}
|
||||
|
||||
flatten.rec = function flatten (array, result) {
|
||||
|
||||
for (let item of array) {
|
||||
if (Array.isArray(item)) {
|
||||
flatten(item, result);
|
||||
} else {
|
||||
result.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export default flatten;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue