Compare commits

..

No commits in common. "master" and "master" have entirely different histories.

1136 changed files with 172464 additions and 143885 deletions

View File

@ -1,16 +0,0 @@
{
"presets": [
"es2015",
"react",
"stage-2"
],
"plugins": [[
"transform-runtime",
{
"helpers": false,
"polyfill": false,
"regenerator": true,
"moduleName": "babel-runtime"
}
]]
}

6
.gitignore vendored
View File

@ -12,11 +12,6 @@ pids
*.seed
*.pid.lock
# Ignore master key for decrypting credentials and more.
/config/master.key
/config/database.yml
/.idea/*
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
@ -85,4 +80,3 @@ typings/
.dynamodb/
.DS_Store
.idea/*

View File

@ -1,2 +0,0 @@
{
}

137
LICENSE
View File

@ -1,124 +1,21 @@
木兰宽松许可证, 第2版
MIT License
2020年1月 http://license.coscl.org.cn/MulanPSL2
Copyright (c) [year] [fullname]
您对“软件”的复制、使用、修改及分发受木兰宽松许可证第2版“本许可证”的如下条款的约束
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:
0. 定义
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
“软件” 是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。
“贡献” 是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。
“贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。
“法人实体” 是指提交贡献的机构及其“关联实体”。
“关联实体” 是指对“本许可证”下的行为方而言控制、受控制或与其共同受控制的机构此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
1. 授予版权许可
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。
2. 授予专利许可
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。
3. 无商标许可
“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可但您为满足第4条规定的声明义务而必须使用除外。
4. 分发限制
您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。
5. 免责声明与责任限制
“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。
6. 语言
“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。
条款结束
如何将木兰宽松许可证第2版应用到您的软件
如果您希望将木兰宽松许可证第2版应用到您的新软件为了方便接收者查阅建议您完成如下三步
1 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
2 请您在软件包的一级目录下创建以“LICENSE”为名的文件将整个许可证文本放入该文件中
3 请将如下声明文本放入每个源文件的头部注释中。
Copyright (c) [Year] [name of copyright holder]
[Software Name] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
Mulan Permissive Software LicenseVersion 2
Mulan Permissive Software LicenseVersion 2 (Mulan PSL v2)
January 2020 http://license.coscl.org.cn/MulanPSL2
Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions:
0. Definition
Software means the program and related documents which are licensed under this License and comprise all Contribution(s).
Contribution means the copyrightable work licensed by a particular Contributor under this License.
Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License.
Legal Entity means the entity making a Contribution and all its Affiliates.
Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, control means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity.
1. Grant of Copyright License
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not.
2. Grant of Patent License
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken.
3. No Trademark License
No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in section 4.
4. Distribution Restriction
You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software.
5. Disclaimer of Warranty and Limitation of Liability
THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW ITS CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
6. Language
THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL.
END OF THE TERMS AND CONDITIONS
How to Apply the Mulan Permissive Software LicenseVersion 2 (Mulan PSL v2) to Your Software
To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps:
Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner;
Create a file named "LICENSE" which contains the whole context of this License in the first directory of your software package;
Attach the statement to the appropriate annotated syntax at the beginning of each source file.
Copyright (c) [Year] [name of copyright holder]
[Software Name] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
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.

View File

@ -1,16 +0,0 @@
<h3>前端react环境安装</h3>
<p>1、 安装node v6.9.x此安装包含了node和npm。</p>
<p>2、 安装cnpm命令行 npm install -g cnpm --registry=https://registry.npm.taobao.org</p>
<p>3、 安装依赖的js库public/react目录下<即项目package.json所在目录>,开启命令行): cnpm install</p>
<p>4、 如果你的ruby服务使用的是3000端口则需要在package.json中修改"port"参数的值</p>
<p>5、 启动服务(命令行-目录同3 npm start</p>
<p>6、 build初始化 npm run build</p>
<h3>分支信息:</h3>
<p>相关代码提交到对应分支能上线的代码先提交到develop分支上测试版测试通过后合并提交到master分支上线正式版</p>
<p>master:开发环境(正式环境)</p>
<p>develop:测试环境</p>
<p>dev_local:本地版本</p>
<p>dev_chain:含有区块链相关内容的分支</p>
<p>PS:新增加的需求功能先新建新分支开发在测试版测试没问题后再分别合并到develop和master分支</p>

View File

@ -76,7 +76,7 @@ function getClientEnvironment(publicUrl) {
// 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: publicUrl,
PUBLIC_URL: '/forgeplus-react/build/.',
}
);
// Stringify all values so we can feed into Webpack DefinePlugin

View File

@ -1,55 +1,44 @@
"use strict";
'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 ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
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');
const getClientEnvironment = require("./env");
const paths = require("./paths");
const publicPath = "/";
const env = getClientEnvironment("/");
// 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 = {
optimization: {
splitChunks: {
chunks: "async",
// 大于30KB才单独分离成chunk
minSize: 30000,
maxAsyncRequests: 5,
maxInitialRequests: 3,
name: true,
cacheGroups: {
default: {
priority: -20,
reuseExistingChunk: true,
},
vendors: {
name: "vendors",
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: "all",
},
},
},
runtimeChunk: true,
},
mode: "development",
// 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", // 开启调试
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"),
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
@ -60,7 +49,7 @@ module.exports = {
// 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"),
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
@ -70,25 +59,24 @@ module.exports = {
output: {
// Add /* filename */ comments to generated require()s in the output.
pathinfo: true,
globalObject: "this",
// 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",
filename: 'static/js/bundle.js',
// There are also additional JS chunk files if you use code splitting.
chunkFilename: "static/js/[name].chunk.js",
chunkFilename: 'static/js/[name].chunk.js',
// This is the URL that app is served from. We use "/" in development.
publicPath,
publicPath: publicPath,
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: (info) =>
path.resolve(info.absoluteResourcePath).replace(/\\/g, "/"),
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(
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)
),
@ -98,12 +86,13 @@ module.exports = {
// 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"],
extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'],
alias: {
educoder: __dirname + "/../src/common/educoder.js",
"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",
'react-native': 'react-native-web',
},
plugins: [
// Prevents users from importing files from outside of src/ (or node_modules/).
@ -112,11 +101,36 @@ module.exports = {
// 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
@ -127,33 +141,23 @@ module.exports = {
// A missing `test` is equivalent to a match.
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve("url-loader"),
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: "static/media/[name].[hash:8].[ext]",
name: 'static/media/[name].[hash:8].[ext]',
},
},
// Process JS with Babel.
{
test: /\.(js|jsx|mjs)$/,
include: paths.appSrc,
exclude: /node_modules/,
loader: require.resolve("babel-loader"),
loader: require.resolve('babel-loader'),
options: {
// This is a feature of `babel-loader` for webpack (not Babel itself).
// 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,
plugins: [
[
"import",
{
libraryName: "antd",
libraryDirectory: "es",
style: "css",
},
],
],
},
},
// "postcss" loader applies autoprefixer to our CSS.
@ -164,29 +168,29 @@ module.exports = {
{
test: /\.css$/,
use: [
require.resolve("style-loader"),
require.resolve('style-loader'),
{
loader: require.resolve("css-loader"),
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
},
},
{
loader: require.resolve("postcss-loader"),
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: "postcss",
ident: 'postcss',
plugins: () => [
require("postcss-flexbugs-fixes"),
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
">1%",
"last 4 versions",
"Firefox ESR",
"not ie < 9", // React doesn't support IE8 anyway
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: "no-2009",
flexbox: 'no-2009',
}),
],
},
@ -204,8 +208,8 @@ module.exports = {
},
},
{
loader: require.resolve("sass-loader"),
},
loader: require.resolve("sass-loader")
}
],
},
// "file" loader makes sure those assets get served by WebpackDevServer.
@ -219,9 +223,9 @@ module.exports = {
// 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"),
loader: require.resolve('file-loader'),
options: {
name: "static/media/[name].[hash:8].[ext]",
name: 'static/media/[name].[hash:8].[ext]',
},
},
],
@ -235,13 +239,12 @@ module.exports = {
// 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: false,
inject: true,
template: paths.appHtml,
}),
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
// 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:
@ -264,19 +267,16 @@ module.exports = {
// 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({
features: ["coreCommands", "find"],
languages: ['plaintext','apex', 'azcli', 'bat', 'clojure', 'coffee', 'cpp', 'csharp', 'csp', 'css', 'dockerfile', 'fsharp', 'go', 'handlebars', 'html', 'ini', 'java', 'javascript', 'json', 'less', 'lua', 'markdown', 'msdax', 'mysql', 'objective', 'perl', 'pgsql', 'php', 'postiats', 'powerquery', 'powershell', 'pug', 'python', 'r', 'razor', 'redis', 'redshift', 'ruby', 'rust', 'sb', 'scheme', 'scss', 'shell', 'solidity', 'sql', 'st', 'swift', 'typescript', 'vb', 'xml', 'yaml']
}),
],
// 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",
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

View File

@ -1,326 +1,391 @@
"use strict";
const autoprefixer = require("autoprefixer");
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-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 MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
'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 TerserJSPlugin = require("terser-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const paths = require('./paths');
const getClientEnvironment = require('./env');
const paths = require("./paths");
const getClientEnvironment = require("./env");
let publicPath = "/react/build/";
// 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);
// const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false";
const shouldUseSourceMap = process.env.NODE_ENV !== "production";
const env = getClientEnvironment(publicPath);
// 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: {
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
splitChunks: {
chunks: "async",
// 大于30KB才单独分离成chunk
minSize: 30000,
maxAsyncRequests: 5,
maxInitialRequests: 3,
name: true,
cacheGroups: {
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
// vendors: {
// name: 'vendors',
// test: /[\\/]node_modules[\\/]/,
// priority: -10,
// chunks: "all"
// }
},
},
runtimeChunk: true,
},
bail: true,
mode: "production",
// devtool: false, //测试版
devtool: shouldUseSourceMap?'source-map':false,
entry: [require.resolve("./polyfills"), paths.appIndexJs],
output: {
path: paths.appBuild,
globalObject: "this",
filename: "./static/js/[name].[contenthash:8].js",
chunkFilename: "./static/js/[name].[contenthash:8].chunk.js",
publicPath,
// 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: [
{
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]",
},
},
{
test: /\.(js|jsx|mjs)$/,
include: paths.appSrc,
exclude: /node_modules/,
loader: require.resolve("babel-loader"),
options: {
compact: true,
plugins: [
[
"import",
{
libraryName: "antd",
libraryDirectory: "es",
style: "css",
},
],
],
},
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath,
},
},
{
loader: require.resolve("css-loader"),
options: {
importLoaders: 1,
sourceMap: shouldUseSourceMap,
},
},
{
loader: require.resolve("postcss-loader"),
options: {
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: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath,
},
},
{
loader: require.resolve("css-loader"),
options: {
importLoaders: 1,
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].[contenthash: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.
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({
inject: false,
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
// 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),
// 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 } },
new MiniCssExtractPlugin({
filename: "static/css/[name].[contenthash:8].css",
chunkFilename: "static/css/[name].[contenthash:8].chunk.css",
}),
// 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({
features: ["coreCommands", "find"],
languages: ['plaintext','apex', 'azcli', 'bat', 'clojure', 'coffee', 'cpp', 'csharp', 'csp', 'css', 'dockerfile', 'fsharp', 'go', 'handlebars', 'html', 'ini', 'java', 'javascript', 'json', 'less', 'lua', 'markdown', 'msdax', 'mysql', 'objective', 'perl', 'pgsql', 'php', 'postiats', 'powerquery', 'powershell', 'pug', 'python', 'r', 'razor', 'redis', 'redshift', 'ruby', 'rust', 'sb', 'scheme', 'scss', 'shell', 'solidity', 'sql', 'st', 'swift', 'typescript', 'vb', 'xml', 'yaml']
}),
new webpack.NamedChunksPlugin(),
new webpack.HashedModuleIdsPlugin(),
],
// 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",
},
// 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',
},
};

46
index.js Normal file
View File

@ -0,0 +1,46 @@
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) });
}

21321
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,132 +1,118 @@
{
"name": "forge",
"version": "3.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",
"actioncable": "^5.2.4-3",
"antd": "^3.26.15",
"antd": "^3.23.2",
"array-flatten": "^2.1.2",
"autoprefixer": "7.1.6",
"axios": "^0.18.1",
"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-polyfill": "^6.26.0",
"babel-preset-react-app": "^3.1.1",
"babel-runtime": "6.26.0",
"bizcharts": "^3.5.8",
"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.8",
"code-prettify": "^0.1.0",
"codemirror": "^5.53.0",
"clipboard": "^2.0.4",
"codemirror": "^5.46.0",
"connected-react-router": "4.4.1",
"cross-env": "^7.0.3",
"css-loader": "^3.5.2",
"dompurify": "^2.0.15",
"css-loader": "0.28.7",
"dotenv": "4.0.0",
"dotenv-expand": "4.2.0",
"echarts": "^4.9.0",
"echarts-wordcloud": "^2.0.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": "^4.0.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",
"file-loader": "^6.0.0",
"flv.js": "^1.5.0",
"extract-text-webpack-plugin": "3.0.2",
"file-loader": "1.1.5",
"fs-extra": "3.0.1",
"html-webpack-plugin": "^4.0.4",
"html-webpack-plugin": "2.29.0",
"immutability-helper": "^2.6.6",
"install": "^0.12.2",
"jest": "20.0.4",
"js-base64": "^2.5.2",
"js2wordcloud": "^1.1.12",
"js-base64": "^2.5.1",
"katex": "^0.11.1",
"lodash": "^4.17.15",
"loglevel": "^1.6.8",
"marked": "^1.0.0",
"lodash": "^4.17.5",
"loglevel": "^1.6.1",
"material-ui": "^1.0.0-beta.40",
"md5": "^2.2.1",
"mini-css-extract-plugin": "^0.9.0",
"moment": "^2.23.0",
"monaco-editor": "^0.20.0",
"monaco-editor-webpack-plugin": "^1.9.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",
"qrcode.react": "^1.0.1",
"qs": "^6.9.3",
"qs": "^6.6.0",
"quill": "^1.3.7",
"quill-delta-to-html": "^0.11.0",
"raf": "3.4.0",
"rc-form": "^2.4.11",
"rc-pagination": "^1.21.0",
"rc-rate": "^2.6.0",
"rc-form": "^2.1.7",
"rc-pagination": "^1.16.2",
"rc-rate": "^2.4.0",
"rc-select": "^8.0.12",
"rc-tree": "^1.15.3",
"rc-upload": "^2.9.4",
"react": "^16.13.1",
"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.1",
"react-codemirror2": "^6.0.0",
"react-color": "^2.18.0",
"react-content-loader": "^3.1.1",
"react-cookies": "^0.1.1",
"react-datepicker": "^2.14.1",
"react-dev-utils": "^9.2.0-next.80",
"react-dom": "^16.13.1",
"react-hot-loader": "^4.12.20",
"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.37",
"react-player": "^1.15.3",
"react-monaco-editor": "^0.25.1",
"react-player": "^1.11.1",
"react-redux": "5.0.7",
"react-resizable": "^1.10.1",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-slick": "^0.28.1",
"react-split-pane": "^0.1.91",
"react-url-query": "^1.5.0",
"react-zmage": "^0.8.5-beta.31",
"redux": "^4.0.5",
"react-split-pane": "^0.1.89",
"react-url-query": "^1.4.0",
"redux": "^4.0.0",
"redux-thunk": "2.3.0",
"rsuite": "^4.3.4",
"sass-loader": "7.3.1",
"save-dev": "0.0.1-security",
"scroll-into-view": "^1.14.2",
"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.8.0",
"slick-carousel": "^1.8.1",
"showdown-katex": "^0.6.0",
"store": "^2.0.12",
"style-loader": "0.19.0",
"styled-components": "^4.4.1",
"styled-components": "^4.1.3",
"sw-precache-webpack-plugin": "0.11.4",
"url-loader": "0.6.2",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3",
"webpack-manifest-plugin": "^2.2.0",
"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",
"xterm": "4.8.1",
"xterm-addon-fit": "0.4.0"
"wrap-md-editor": "^0.2.20"
},
"scripts": {
"start": "node --max_old_space_size=15360 scripts/start.js",
"build": "cross-env NODE_ENV=production node --max_old_space_size=15360 scripts/build.js",
"test-build": "cross-env NODE_ENV=testBuild node --max_old_space_size=15360 scripts/build.js",
"pre-build": "NODE_ENV=preBuild node --max_old_space_size=15360 scripts/build.js",
"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",
@ -187,31 +173,18 @@
"eslintConfig": {
"extends": "react-app"
},
"proxy": "http://localhost:3001",
"proxy": "http://localhost:3000",
"port": "3007",
"devDependencies": {
"@babel/runtime": "7.0.0-beta.51",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-plugin-import": "^1.13.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-polyfill": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
"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",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"purgecss": "^2.1.2",
"react-json-view": "^1.21.3",
"reqwest": "^2.0.5",
"resize-observer-polyfill": "^1.5.1",
"terser-webpack-plugin": "^2.3.5",
"uglifyjs-webpack-plugin": "^2.2.0",
"webpack": "^4.42.1",
"webpack-bundle-analyzer": "^3.7.0"
"webpack-bundle-analyzer": "^3.0.3",
"webpack-parallel-uglify-plugin": "^1.1.0"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,239 +0,0 @@
/*******************************************************************************
* KindEditor - WYSIWYG HTML Editor for Internet
* Copyright (C) 2006-2011 kindsoft.net
*
* @author Roddy <luolonghao@gmail.com>
* @site http://www.kindsoft.net/
* @licence http://www.kindsoft.net/license.php
*******************************************************************************/
KindEditor.lang({
imagedirectupload:'本地图片',
more: '切换到更多功能',
less: '切换到简单功能',
source : 'HTML代码',
preview : '预览',
undo : '后退(Ctrl+Z)',
redo : '前进(Ctrl+Y)',
cut : '剪切(Ctrl+X)',
copy : '复制(Ctrl+C)',
paste : '粘贴(Ctrl+V)',
plainpaste : '粘贴为无格式文本',
wordpaste : '从Word粘贴',
selectall : '全选(Ctrl+A)',
justifyleft : '左对齐',
justifycenter : '居中',
justifyright : '右对齐',
justifyfull : '两端对齐',
insertorderedlist : '编号',
insertunorderedlist : '项目符号',
indent : '增加缩进',
outdent : '减少缩进',
subscript : '下标',
superscript : '上标',
formatblock : '段落',
fontname : '字体',
fontsize : '文字大小',
forecolor : '文字颜色',
hilitecolor : '文字背景',
bold : '粗体(Ctrl+B)',
italic : '斜体(Ctrl+I)',
underline : '下划线(Ctrl+U)',
strikethrough : '删除线',
removeformat : '删除格式',
image : '图片',
multiimage : '批量图片上传',
flash : 'Flash',
media : '视音频',
table : '表格',
tablecell : '单元格',
hr : '插入横线',
emoticons : '插入表情',
link : '超级链接',
unlink : '取消超级链接',
fullscreen : '全屏显示',
about : '关于',
print : '打印(Ctrl+P)',
filemanager : '文件空间',
code : '插入程序代码',
map : 'Google地图',
baidumap : '百度地图',
lineheight : '行距',
clearhtml : '清理HTML代码',
pagebreak : '插入分页符',
quickformat : '一键排版',
insertfile : '插入文件',
template : '插入模板',
anchor : '锚点',
yes : '确定',
no : '取消',
close : '关闭',
editImage : '图片属性',
deleteImage : '删除图片',
editFlash : 'Flash属性',
deleteFlash : '删除Flash',
editMedia : '视音频属性',
deleteMedia : '删除视音频',
editLink : '超级链接属性',
deleteLink : '取消超级链接',
editAnchor : '锚点属性',
deleteAnchor : '删除锚点',
tableprop : '表格属性',
tablecellprop : '单元格属性',
tableinsert : '插入表格',
tabledelete : '删除表格',
tablecolinsertleft : '左侧插入列',
tablecolinsertright : '右侧插入列',
tablerowinsertabove : '上方插入行',
tablerowinsertbelow : '下方插入行',
tablerowmerge : '向下合并单元格',
tablecolmerge : '向右合并单元格',
tablerowsplit : '拆分行',
tablecolsplit : '拆分列',
tablecoldelete : '删除列',
tablerowdelete : '删除行',
noColor : '无颜色',
pleaseSelectFile : '请选择文件。',
invalidImg : "请输入有效的URL地址。\n只允许jpg,gif,bmp,png格式。",
invalidMedia : "请输入有效的URL地址。\n只允许swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb格式。",
invalidWidth : "宽度必须为数字。",
invalidHeight : "高度必须为数字。",
invalidBorder : "边框必须为数字。",
invalidUrl : "请输入有效的URL地址。",
invalidRows : '行数为必选项只允许输入大于0的数字。',
invalidCols : '列数为必选项只允许输入大于0的数字。',
invalidPadding : '边距必须为数字。',
invalidSpacing : '间距必须为数字。',
invalidJson : '服务器发生故障。',
uploadSuccess : '上传成功。',
cutError : '您的浏览器安全设置不允许使用剪切操作,请使用快捷键(Ctrl+X)来完成。',
copyError : '您的浏览器安全设置不允许使用复制操作,请使用快捷键(Ctrl+C)来完成。',
pasteError : '您的浏览器安全设置不允许使用粘贴操作,请使用快捷键(Ctrl+V)来完成。',
ajaxLoading : '加载中,请稍候 ...',
uploadLoading : '上传中,请稍候 ...',
uploadError : '上传错误',
'plainpaste.comment' : '请使用快捷键(Ctrl+V)把内容粘贴到下面的方框里。',
'wordpaste.comment' : '请使用快捷键(Ctrl+V)把内容粘贴到下面的方框里。',
'code.pleaseInput' : '请输入程序代码。',
'link.url' : 'URL',
'link.linkType' : '打开类型',
'link.newWindow' : '新窗口',
'link.selfWindow' : '当前窗口',
'flash.url' : 'URL',
'flash.width' : '宽度',
'flash.height' : '高度',
'flash.upload' : '上传',
'flash.viewServer' : '文件空间',
'media.url' : 'URL',
'media.width' : '宽度',
'media.height' : '高度',
'media.autostart' : '自动播放',
'media.upload' : '上传',
'media.viewServer' : '文件空间',
'image.remoteImage' : '网络图片',
'image.localImage' : '本地上传',
'image.remoteUrl' : '图片地址',
'image.localUrl' : '上传文件',
'image.size' : '图片大小',
'image.width' : '宽',
'image.height' : '高',
'image.resetSize' : '重置大小',
'image.align' : '对齐方式',
'image.defaultAlign' : '默认方式',
'image.leftAlign' : '左对齐',
'image.rightAlign' : '右对齐',
'image.imgTitle' : '图片说明',
'image.upload' : '浏览...',
'image.viewServer' : '图片空间',
'multiimage.uploadDesc' : '允许用户同时上传<%=uploadLimit%>张图片,单张图片容量不超过<%=sizeLimit%>',
'multiimage.startUpload' : '开始上传',
'multiimage.clearAll' : '全部清空',
'multiimage.insertAll' : '全部插入',
'multiimage.queueLimitExceeded' : '文件数量超过限制。',
'multiimage.fileExceedsSizeLimit' : '文件大小超过限制。',
'multiimage.zeroByteFile' : '无法上传空文件。',
'multiimage.invalidFiletype' : '文件类型不正确。',
'multiimage.unknownError' : '发生异常,无法上传。',
'multiimage.pending' : '等待上传',
'multiimage.uploadError' : '上传失败',
'filemanager.emptyFolder' : '空文件夹',
'filemanager.moveup' : '移到上一级文件夹',
'filemanager.viewType' : '显示方式:',
'filemanager.viewImage' : '缩略图',
'filemanager.listImage' : '详细信息',
'filemanager.orderType' : '排序方式:',
'filemanager.fileName' : '名称',
'filemanager.fileSize' : '大小',
'filemanager.fileType' : '类型',
'insertfile.url' : 'URL',
'insertfile.title' : '文件说明',
'insertfile.upload' : '上传',
'insertfile.viewServer' : '文件空间',
'table.cells' : '单元格数',
'table.rows' : '行数',
'table.cols' : '列数',
'table.size' : '大小',
'table.width' : '宽度',
'table.height' : '高度',
'table.percent' : '%',
'table.px' : 'px',
'table.space' : '边距间距',
'table.padding' : '边距',
'table.spacing' : '间距',
'table.align' : '对齐方式',
'table.textAlign' : '水平对齐',
'table.verticalAlign' : '垂直对齐',
'table.alignDefault' : '默认',
'table.alignLeft' : '左对齐',
'table.alignCenter' : '居中',
'table.alignRight' : '右对齐',
'table.alignTop' : '顶部',
'table.alignMiddle' : '中部',
'table.alignBottom' : '底部',
'table.alignBaseline' : '基线',
'table.border' : '边框',
'table.borderWidth' : '边框',
'table.borderColor' : '颜色',
'table.backgroundColor' : '背景颜色',
'map.address' : '地址: ',
'map.search' : '搜索',
'baidumap.address' : '地址: ',
'baidumap.search' : '搜索',
'baidumap.insertDynamicMap' : '插入动态地图',
'anchor.name' : '锚点名称',
'formatblock.formatBlock' : {
h1 : '标题 1',
h2 : '标题 2',
h3 : '标题 3',
h4 : '标题 4',
p : '正 文'
},
'fontname.fontName' : {
'SimSun' : '宋体',
'NSimSun' : '新宋体',
'FangSong_GB2312' : '仿宋_GB2312',
'KaiTi_GB2312' : '楷体_GB2312',
'SimHei' : '黑体',
'Microsoft YaHei' : '微软雅黑',
'Arial' : 'Arial',
'Arial Black' : 'Arial Black',
'Times New Roman' : 'Times New Roman',
'Courier New' : 'Courier New',
'Tahoma' : 'Tahoma',
'Verdana' : 'Verdana'
},
'lineheight.lineHeight' : [
{'1' : '单倍行距'},
{'1.5' : '1.5倍行距'},
{'2' : '2倍行距'},
{'2.5' : '2.5倍行距'},
{'3' : '3倍行距'}
],
'template.selectTemplate' : '可选模板',
'template.replaceContent' : '替换当前内容',
'template.fileList' : {
'1.html' : '图片和文字',
'2.html' : '表格',
'3.html' : '项目编号'
}
}, 'zh_CN');

View File

@ -1,71 +0,0 @@
/*******************************************************************************
* KindEditor - WYSIWYG HTML Editor for Internet
* Copyright (C) 2006-2011 kindsoft.net
*
* @author Roddy <luolonghao@gmail.com>
* @site http://www.kindsoft.net/
* @licence http://www.kindsoft.net/license.php
*******************************************************************************/
KindEditor.plugin('autoheight', function(K) {
var self = this;
//self.autoHeightMode = true;
if (!self.autoHeightMode) {
return;
}
var minHeight;
function hideScroll() {
var edit = self.edit;
var body = edit.doc.body;
edit.iframe[0].scroll = 'no';
body.style.overflowY = 'hidden';
}
function resetHeight() {
var edit = self.edit;
var body = edit.doc.body;
edit.iframe.height(minHeight);
//最高418 超过用滚动条
self.resize(null, Math.max(
(K.IE ? body.scrollHeight : body.offsetHeight) > 385 ? 385+33 : (K.IE ? body.scrollHeight : body.offsetHeight)
+ 33, minHeight));
if( self.height == "418px" ){
edit.iframe[0].scroll = 'yes';
body.style.overflowY = 'auto';
}
else{
hideScroll();
}
}
function init() {
minHeight = K.removeUnit(self.height);
self.edit.afterChange(resetHeight);
hideScroll();
var edit = self.edit;
var body = edit.doc.body;
edit.iframe.height(minHeight);
self.resize(null, Math.max(
((K.IE ? body.scrollHeight : body.offsetHeight) > 250 ? 250 : (K.IE ? body.scrollHeight : body.offsetHeight)) //限制初始化太高的情况
+ 33, minHeight));
}
if (self.isCreated) {
init();
} else {
self.afterCreate(init);
}
});
/*
* 如何实现真正的自动高度
* 修改编辑器高度之后再次获取body内容高度时最小值只会是当前iframe的设置高度这样就导致高度只增不减
* 所以每次获取body内容高度之前先将iframe的高度重置为最小高度这样就能获取body的实际高度
* 由此就实现了真正的自动高度
* 测试chromefirefoxIE9IE8
* */

View File

@ -1,56 +0,0 @@
.ke-content {
font-family: "微软雅黑","宋体";
}
.ke-content pre {
font-size:9pt;
font-family:Courier New,Arial;
border:1px solid #ddd;
border-left:5px solid #6CE26C;
background:#f6f6f6;
padding:5px;
}
.ke-content code {
margin: 0 2px;
padding: 0 5px;
white-space: nowrap;
border: 1px solid #DDD;
background-color: #F6F6F6;
border-radius: 3px;
}
.ke-content pre>code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}
.ke-content pre code {
background-color: transparent;
border: none;
}
.ke-content p {
/*margin: 0 0 15px 0;*/
/*margin-bottom:15pt;*/
line-height:1.5;
/*letter-spacing: 1px;*/
}
.ke-content div.ref {border:1px solid #ddd;margin:0 0 10px 0;padding:2px;font-size:9pt;background:#ffe;}
.ke-content div.ref h4 {margin:0;padding:1px 3px;background:#CC9966;color:#fff;font-size:9pt;font-weight:normal;}
.ke-content div.ref .ref_body {margin:0;padding:2px;line-height:20px;color:#666;font-size:9pt;}
.ke-content blockquote{background: none;border: none;padding: 0px;margin: 0 0 0 40px;}
span.at {color:#269ac9;}
span.at a{color:#269ac9;text-decoration: none;}
/*yk*/
.ke-content ol li{list-style-type: decimal;}
.ke-content ul li{list-style-type: disc;}
.ke-content ol,.ke-content ul,.ke-content h1,.ke-content h2,.ke-content h3,.ke-content h4{margin-top:0;margin-bottom: 0;}
.ke-content a{color: #136ec2;}
.ke-content a:link,.ke-content a:visited{text-decoration:none;}

View File

@ -1,361 +0,0 @@
// Generated by CoffeeScript 1.9.0
/*
paste.js is an interface to read data ( text / image ) from clipboard in different browsers. It also contains several hacks.
https://github.com/layerssss/paste.js
*/
(function() {
var $, Paste, createHiddenEditable, dataURLtoBlob;
$ = window.jQuery;
$.paste = function(pasteContainer) {
var pm;
if (typeof console !== "undefined" && console !== null) {
console.log("DEPRECATED: This method is deprecated. Please use $.fn.pastableNonInputable() instead.");
}
pm = Paste.mountNonInputable(pasteContainer);
return pm._container;
};
$.fn.pastableNonInputable = function() {
var el, _i, _len;
for (_i = 0, _len = this.length; _i < _len; _i++) {
el = this[_i];
Paste.mountNonInputable(el);
}
return this;
};
$.fn.pastableTextarea = function() {
var el, _i, _len;
for (_i = 0, _len = this.length; _i < _len; _i++) {
el = this[_i];
Paste.mountTextarea(el);
}
return this;
};
$.fn.pastableContenteditable = function() {
var el, _i, _len;
for (_i = 0, _len = this.length; _i < _len; _i++) {
el = this[_i];
Paste.mountContenteditable(el);
}
return this;
};
dataURLtoBlob = function(dataURL, sliceSize) {
var b64Data, byteArray, byteArrays, byteCharacters, byteNumbers, contentType, i, m, offset, slice, _ref;
if (sliceSize == null) {
sliceSize = 512;
}
if (!(m = dataURL.match(/^data\:([^\;]+)\;base64\,(.+)$/))) {
return null;
}
_ref = m, m = _ref[0], contentType = _ref[1], b64Data = _ref[2];
byteCharacters = atob(b64Data);
byteArrays = [];
offset = 0;
while (offset < byteCharacters.length) {
slice = byteCharacters.slice(offset, offset + sliceSize);
byteNumbers = new Array(slice.length);
i = 0;
while (i < slice.length) {
byteNumbers[i] = slice.charCodeAt(i);
i++;
}
byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
offset += sliceSize;
}
return new Blob(byteArrays, {
type: contentType
});
};
createHiddenEditable = function() {
return $(document.createElement('div')).attr('contenteditable', true).css({
width: 1,
height: 1,
position: 'fixed',
left: -100,
overflow: 'hidden'
});
};
Paste = (function() {
Paste.prototype._target = null;
Paste.prototype._container = null;
Paste.mountNonInputable = function(nonInputable) {
var paste;
paste = new Paste(createHiddenEditable().appendTo(nonInputable), nonInputable);
$(nonInputable).on('click', (function(_this) {
return function() {
return paste._container.focus();
};
})(this));
paste._container.on('focus', (function(_this) {
return function() {
return $(nonInputable).addClass('pastable-focus');
};
})(this));
return paste._container.on('blur', (function(_this) {
return function() {
return $(nonInputable).removeClass('pastable-focus');
};
})(this));
};
Paste.mountTextarea = function(textarea) {
var ctlDown, paste;
if (-1 !== navigator.userAgent.toLowerCase().indexOf('chrome')) {
return this.mountContenteditable(textarea);
}
paste = new Paste(createHiddenEditable().insertBefore(textarea), textarea);
ctlDown = false;
$(textarea).on('keyup', function(ev) {
var _ref;
if ((_ref = ev.keyCode) === 17 || _ref === 224) {
return ctlDown = false;
}
});
$(textarea).on('keydown', function(ev) {
var _ref;
if ((_ref = ev.keyCode) === 17 || _ref === 224) {
ctlDown = true;
}
if (ctlDown && ev.keyCode === 86) {
return paste._container.focus();
}
});
$(paste._target).on('pasteImage', (function(_this) {
return function() {
return $(textarea).focus();
};
})(this));
$(paste._target).on('pasteText', (function(_this) {
return function() {
return $(textarea).focus();
};
})(this));
$(textarea).on('focus', (function(_this) {
return function() {
return $(textarea).addClass('pastable-focus');
};
})(this));
return $(textarea).on('blur', (function(_this) {
return function() {
return $(textarea).removeClass('pastable-focus');
};
})(this));
};
Paste.mountContenteditable = function(contenteditable) {
var paste;
paste = new Paste(contenteditable, contenteditable);
$(contenteditable).on('focus', (function(_this) {
return function() {
return $(contenteditable).addClass('pastable-focus');
};
})(this));
return $(contenteditable).on('blur', (function(_this) {
return function() {
return $(contenteditable).removeClass('pastable-focus');
};
})(this));
};
function Paste(_at__container, _at__target) {
this._container = _at__container;
this._target = _at__target;
this._container = $(this._container);
this._target = $(this._target).addClass('pastable');
this._container.on('paste', (function(_this) {
return function(ev) {
var clipboardData, file, item, reader, text, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3, _results;
if (((_ref = ev.originalEvent) != null ? _ref.clipboardData : void 0) != null) {
clipboardData = ev.originalEvent.clipboardData;
if (clipboardData.items) {
_ref1 = clipboardData.items;
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
item = _ref1[_i];
if (item.type.match(/^image\//)) {
reader = new FileReader();
reader.onload = function(event) {
return _this._handleImage(event.target.result);
};
reader.readAsDataURL(item.getAsFile());
}
if (item.type === 'text/plain') {
item.getAsString(function(string) {
return _this._target.trigger('pasteText', {
text: string
});
});
}
}
} else {
if (-1 !== Array.prototype.indexOf.call(clipboardData.types, 'text/plain')) {
text = clipboardData.getData('Text');
_this._target.trigger('pasteText', {
text: text
});
}
_this._checkImagesInContainer(function(src) {
return _this._handleImage(src);
});
}
}
if (clipboardData = window.clipboardData) {
if ((_ref2 = (text = clipboardData.getData('Text'))) != null ? _ref2.length : void 0) {
return _this._target.trigger('pasteText', {
text: text
});
} else {
_ref3 = clipboardData.files;
_results = [];
for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) {
file = _ref3[_j];
_this._handleImage(URL.createObjectURL(file));
_results.push(_this._checkImagesInContainer(function() {}));
}
return _results;
}
}
};
})(this));
}
Paste.prototype._handleImage = function(src) {
var loader;
loader = new Image();
loader.onload = (function(_this) {
return function() {
var blob, canvas, ctx, dataURL;
canvas = document.createElement('canvas');
canvas.width = loader.width;
canvas.height = loader.height;
ctx = canvas.getContext('2d');
ctx.drawImage(loader, 0, 0, canvas.width, canvas.height);
dataURL = null;
try {
dataURL = canvas.toDataURL('image/png');
blob = dataURLtoBlob(dataURL);
} catch (_error) {}
if (dataURL) {
return _this._target.trigger('pasteImage', {
blob: blob,
dataURL: dataURL,
width: loader.width,
height: loader.height
});
}
};
})(this);
return loader.src = src;
};
Paste.prototype._checkImagesInContainer = function(cb) {
var img, timespan, _i, _len, _ref;
timespan = Math.floor(1000 * Math.random());
_ref = this._container.find('img');
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
img = _ref[_i];
img["_paste_marked_" + timespan] = true;
}
return setTimeout((function(_this) {
return function() {
var _j, _len1, _ref1, _results;
_ref1 = _this._container.find('img');
_results = [];
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
img = _ref1[_j];
if (!img["_paste_marked_" + timespan]) {
cb(img.src);
}
//firefox问题处理因为采用了 pastableContenteditable 模式,故只需要删除默认的粘贴
if ($(img).attr('src').startsWith('data:image/')) {
_results.push($(img).remove());
};
}
return _results;
};
})(this), 1);
};
return Paste;
})();
}).call(this);
KindEditor.plugin('paste', function(K) {
var editor = this,
name = 'paste';
if(editor.edit == undefined || editor.edit.iframe == undefined){
return;
}
var contentWindow = editor.edit.iframe[0].contentWindow;
//var contentWindow = editor.iframeDoc;
//var contentWindow = document.getElementsByTagName('iframe')[0].contentWindow;
var nodeBody = contentWindow.document.getElementsByTagName('body')[0];
console.log(nodeBody);
$(nodeBody).pastableContenteditable();
dataURItoBlob = function(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataURI.split(',')[1]);
else
byteString = unescape(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
};
$(nodeBody).on('pasteImage', function(ev, data) {
console.log('pasteImage');
console.log("dataURL: " + data.dataURL);
console.log("width: " + data.width);
console.log("height: " + data.height);
var blob = dataURItoBlob(data.dataURL);
if (data.blob !== null) {
var data = new FormData();
// data.append("imgFile",blob, "imageFilename.png");
data.append("file",blob, "imageFilename.png");
console.log(data);
$.ajax({
url: '/kindeditor/upload?dir=image',
url: '/api/attachments.json',
contentType: false,
type: 'POST',
data: data,
processData: false,
success: function(data) {
console.log(data);
// editor.exec('insertimage', JSON.parse(data).url);
// /api/attachments/227362
editor.exec('insertimage', '/api/attachments/' + data.id);
}
});
}
});
return;
});

File diff suppressed because it is too large Load Diff

1907
public/css/css_min_all.css Executable file

File diff suppressed because one or more lines are too long

7576
public/css/demo_index.html Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

5
public/css/font-awesome.css vendored Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
public/css/iconfont.eot Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

992
public/css/iconfont.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

524
public/css/taskstyle.css Executable file
View File

@ -0,0 +1,524 @@
/************新版公共****************/
/************新版公共****************/
html{height:100%;}
/*.newContainer{ min-height:100%; height: auto !important; height: 100%; position: relative;}
.newMain{ margin: 0 auto; padding-bottom: 155px; }
.newFooter{ position: absolute; bottom: 0; width: 100%; height: 155px;background: #323232; clear:both; min-width: 1200px}
.newHeader{background: #46484c;width:100%; height: 50px; min-width: 1200px}*/
.w20_center{ width: 20px;text-align: center; }
.task-container{ min-width:1300px; margin:0 auto; background: #f5f9fc; position: relative;}
/*左侧导航*/
.leftbar{ height: 100%; background: #1f212d; width:80px;}
.user-info{ width:80px; height:100px; padding-top:15px;}
a.user-info-img{ display: block; width: 50px; height: 50px; margin:0 auto; }
a.user-info-img img{border-radius:100px;border:2px solid #666;}
a.user-info-img img:hover{border:2px solid #888;}
a.user-info-name{ display: block; font-size: 16px; color: #fff; max-width:100px; margin: 10px auto; text-align: center; overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}
.leftnav-box{ width: 80px; height: 60px; background:#292b3a; padding:10px 0; margin-bottom:2px;}
a.leftnav-box-inner{ display: block; width:77px; border-left:3px solid #292b3a; background:#292b3a; text-align: center; padding:10px 0; color:#575f6c;}
a:hover.leftnav-box-inner,a.leftnav-active{border-left:3px solid #3498db;color: #fff!important;}
a:hover.leftnav-box-inner .btn-cir{background:#fff;color:#333333}
a.leftnav-box-reset-inner{ display: block; width:77px; border-left:3px solid #292b3a; background:#292b3a; text-align: center; color:#575f6c;}
a:hover.leftnav-box-reset-inner{border-left:3px solid #3498db;color: #fff!important;}
/*右侧头部*/
.rightbar-header{width: 100%; background:#282c37; height:60px; min-width:1000px;}
.rightbar-score{ margin-top: 17px; font-size: 14px; margin-right:20px;}
.rightbar-score li{ float: left; color:#fff; margin-right: 20px;}
.rightbar-score li a{ color:#fff;}
a.rightbar-pause{ color:#29bd8b; font-size: 18px; margin-right:15px; margin-top: 12px;}
.rightbar-h2{ color:#fff; margin:12px 0 0 20px; font-weight: normal;}
.rightbar{background:#f5f9fc; color:#333; position: relative;}
/*右侧内容*/
.content{ min-width:1000px; }
.content-row{ padding:15px; }
.content-info{ width:49.5%; min-width:250px;}
.content-editor{ width:49.3%; min-width:250px; margin-left:15px; }
.panel-header{ border-bottom:1px solid #eee; padding:10px 15px; color:#898989;}
.panel-header-border{ border:1px solid #eee; padding:10px 15px; border-bottom:none; }
/* tab */
.tab_content{ width: 100%; margin: 0 auto; background:#fff; }
#tab_nav {height:42px;background: #fff; border-bottom: 1px solid #EEEEEE}
#tab_nav li {float:left; padding:0 30px;text-align:center;height: 40px;line-height: 40px; }
#tab_nav li a{font-size:14px; }
.tab_hover {border-bottom:2px solid #3498db; background: #fff;color: #3498db}
/*.tab_hover_setting{background:#FC7033;}*/
.tab_hover a{ color:#3498db!important;}
/*.tab_hover_setting a{color:#fff;}*/
.undis {display:none;}
.dis {display:block;}
.tab-info{ }
.content-editor-inner{ overflow:auto;}
.tab-info-inner{ overflow:auto; height:600px; margin:0 0 0px 15px;padding-top: 15px}
.content-history-inner{height:120px; overflow:auto; padding:15px;}
.content-history{width:48.7%; min-width:500px; }
.history-success{ width: 100%; height:40px; line-height: 40px; background:#eef1f2; color:#666; }
.history-fail{ width: 100%; height:40px; line-height: 40px; background:#fdebeb; color:#e53238; }
.icon-fail{ display:inline-block; padding:0 8px; background:#e53238; color:#fff;}
.icon-success{ display:inline-block; padding:0 8px; background:#252e38; color:#fff;}
.info-partly{display: block;box-flex:1;flex:1;-webkit-flex:1;position: relative;}
.content-output{width:37.5%; min-width:200px; }
.content-submit{width:10%; min-width:135px; }
.content-submitbox{ width:120px; margin: 15px auto; height:135px;}
.panel-inner{ background:#EFF2F7; margin:15px; padding:15px;}
.panel-inner-title{ font-size: 14px; color: #666; max-width:85%; line-height:30px;word-wrap: break-word; margin-bottom: 10px}
.panel-footer{ min-width:1000px; height: 210px!important;}
/* 弹框 */
.task-popup-text-center{ text-align: center; color: #333;}
.task-popup-title{ border-bottom: 1px solid #eee; padding:10px 15px; }
.task-popup-submit{ margin: 0 auto 15px; width: 120px;}
/* TPM */
.task-header{ width: 100%;min-width:1200px; background:url("/images/task/task-bg-header.png");height: 180px;background-size: cover;display: flex;align-items: center;}
.task-header-info{ width: 1200px; margin: 0 auto; color:#fff}
.task-header-info h2 a,.task-header-info h2{ font-weight: normal;color:#fff;}
a.task-header-name{ max-width:200px;}
.task-header-title{ display: block; max-width:750px;word-wrap: break-word;overflow:hidden; white-space: nowrap; text-overflow:ellipsis;}
.task-header-nav{ width: 100%;min-width:1200px; height:50px;}
.task-header-navs{ width: 1200px; margin: 0 auto;}
.task-header-navs li{ float: left;}
.task-header-navs li{ display: block; height: 50px; padding:0 50px; color:#666; text-align: center; font-size: 16px; line-height: 50px;}
.task-header-navs li:hover,.task-header-navs li:hover a{ color:#FC7033!important;}
.task-header-navs li.active{border-bottom: 2px solid #FC7033;color:#FC7033;}
.task-header-navs li.active a{color:#FC7033!important;}
.task-header-navs li.active .edu-cir-grey,.task-header-navs li:hover .edu-cir-grey,.edu-cir-grey.active{background: #FF7500;color: #FFFFff}
.task-pm-content{ width: 1200px; margin: 0 auto; min-height:566px}
.task-pm-box{ width: 100%; background: #fff; border: 1px solid #e8e8e8;}
.task-paner-con{ padding:15px; color:#666; line-height:2.0;}
.task-paner-con img{ max-width: 100%}
.panel-form{margin:0 30px 0px 20px; padding:30px 0; }.panel-form li{ margin-bottom:20px; font-size: 14px; color:#666;}
.panel-form-label{ display:inline-block; width:10%; min-width:90px; text-align:right; line-height:40px; color: #666;}
.panel-form-label1{ display:inline-block; width:20%; min-width:90px; text-align:right; line-height:40px; }
.panel-form input,.panel-form textarea{ border:1px solid #e2e2e2;color:#666;line-height: 1.9;}
.panel-box-sizing{-moz-box-sizing: border-box; /*Firefox3.5+*/-webkit-box-sizing: border-box; /*Safari3.2+*/-o-box-sizing: border-box; /*Opera9.6*/-ms-box-sizing: border-box; /*IE8*/box-sizing: border-box; }
input.panel-form-width-690{ padding:5px;width:90%; height:40px; min-width:700px;}
input.panel-form-width-200{ padding:5px; height:40px; width:200px;}
input.panel-form-width-100{ padding:5px;width:100%; height:40px; min-width:700px;}
textarea.panel-form-width-100{ padding:5px;width:100%; height:150px; min-width:700px;}
textarea.panel-form-width-40{ padding:5px;width:100%; height: 40px; min-width:700px;}
textarea.panel-form-width-690{ padding:5px;width:90%; height:150px; min-width:700px;}
textarea.panel-form-width2-100{ padding:5px;width:100%; height:40px; min-width:700px;}
textarea.panel-form-width2-690{ padding:5px;width:90%; height:40px; min-width:700px;}
textarea.panel-form-width2-695{ padding:5px;width:95%; height:40px; min-width:700px;}
.panel-form-width-670{ width: 670px; padding:5px;}
.panel-form-height-150{ height: 150px;}
.panel-form-height-30{height: 30px;}
.task-bg-grey{ background:#f3f3f3!important; width:90%; min-width:700px; padding:10px; border:1px solid #f3f3f3;}
.task-bg-grey-ligh{line-height: 1.9;padding:5px 10px;}
.task-bg-grey li{ margin-bottom: 0}
.task-bd-grey{width:680px; padding:10 0px;}
.panel-form-width-690{ padding:5px;width:90%; min-height:40px; min-width:700px;}
input.task-tag-input{ border:none; background: none; height:30px; padding:0 5px; color:#888; line-height: 30px;}
textarea.task-textarea-pd{ padding-bottom: 0; padding-top:0;}
.task-setting-tab{ min-height:600px;}
.task-pd15-box{ padding:15px;}
.mb20{margin-bottom: 20px;}
input.knowledge_frame{height:28px;line-height:28px;border:none;background:#f3f5f7;}
/* TPi全屏展示css */
.content-all-fix{ position: absolute; top:75px; left:15px; right:15px; z-index:99; height: 91%; width: 98.5%;}
.content-all-fix .big-tab-info-inner{ display: block; height:50%; overflow:auto; margin:15px 0 0px 15px; }
.content-half-fix{ min-width:450px; margin:0; position: absolute; top:75px; left:15px; z-index:99;}
.content-half-fix .content-history-inner{height:100%; overflow:auto; }
.content-half-fix02{margin:0; position: absolute; top:65px; z-index:99; right:45px;}
.content-history-extend{ height: 98%;overflow:auto;}
.task-bg-grey .prettyprint{font-size: 9pt;font-family: Courier New,Arial;border: 1px solid #ddd;border-left: 5px solid #6CE26C;background: #f6f6f6;padding: 5px;}
/* 左右版TPI 20170410byLB */
#game_task_pass img{cursor: pointer}
.-fit { position: absolute; top: 0; right: 0; bottom: 0; left: 0;}
.-layout-v { display: flex; flex-direction: column;box-flex-direction: column;-webkit-flex-direction: column;}
.page--header { position: fixed;top: 0; left:80px; right: 0; z-index: 7000;background:#33485F; height:44px; padding:10px 0; color:#fff;}
.page--leftnav{position: fixed;top:0; left:0; right: 0; z-index: 9001;width:80px; height:100%;background:#282c37;}
.page--body { position: relative;}
.-margin-t-64 { margin-top: 64px;}
.-flex { box-flex:1;flex:1;-webkit-flex:1;}
/*.-flex-auto{flex-basis:100%;}*/
.split-panel.-fit {position: absolute;}
.split-panel { position: relative; overflow: hidden; min-height: 200px; height: 100%;}
.-stretch { align-items: stretch;}
.-layout { display: flex;}
.split-panel--first { overflow: hidden;}
.-relative { position: relative;}
.-bg-white { background-color: #eee;}
.split-panel.-handle .split-panel--second { padding-left: 2px;}
/* .split-panel--second { overflow: hidden;} */
.task-answer-view { position: absolute; top: 0; right: 0; bottom: 0;left: 0; display: flex;
flex-direction: column; border-top: 1px solid #515151;}
.-vertical { flex-direction: column;box-flex-direction: column;-webkit-flex-direction: column;}
.-layout-h { display: flex;flex-direction: row;box-flex-direction: row;-webkit-flex-direction: row;}
.-horizontal {flex-direction: row-reverse;box-flex-direction: row-reverse;-webkit-flex-direction: row-reverse;}
.-scroll{ overflow:auto;}
.-flex-basic0{flex-basis: 0%!important;box-flex-basis: 0%!important;-webkit-flex-basis: 0%!important; display: none}
/*王昌------------拖拽增加样式---------------修改*/
.-flex-basic40{width:40%;box-flex:auto;flex:auto;-webkit-flex:auto;}
.-flex-basic50{width:60%;box-flex:auto;flex:auto;-webkit-flex:auto;}
.b-label{width:4px;cursor:ew-resize;background:#2b2b2b;}
.h-center{height:4px;cursor:ns-resize;background:#333;}
.-changebg{height:3px;}
.-brother{width:100%;height:100%;position:absolute;left:0;top:0;z-index:999;}
.-bg-weightblack{background:#000;}
.-flex-basic70{box-flex:4 9 auto;flex:4 9 auto;-webkit-flex:4 9 auto;height:70%;}
/*---------------------------------------------*/
.-flex-basic60{box-flex:2 1 auto;flex:2 1 auto;-webkit-flex:2 1 auto;height:30%;}
.-flex-basic100{flex-basis: 100%!important;box-flex-basis: 100%!important;-webkit-flex-basis: 100%!important;}
.-header-title{ max-width:500px; font-weight: normal;}
.-header-right{ background:#333;border-radius:25px; padding:5px 15px; height: 30px; position: absolute; right:10px;line-height: 30px;}
.-header-right-info{ padding:10px; background:#fff; border-radius:3px; top:50px; right:10px; position: relative;display:none;color:#666;}
.-header-right-info font { border: 1px solid #dddddd; display: block;border-width: 8px; position: absolute; top: -15px;right:20px;border-style: solid; border-color: transparent transparent #fff transparent; font-size: 0; line-height: 0;}
.-header-right-box:hover .-header-right-info{ display: block;}
.-task-bar-bg{ width: 160px; height:15px; border-radius:15px; background:#ff9932; color:#fff; font-size: 12px; line-height: 15px; text-align: right; position: relative; padding-right:10px;}
.-task-bar-inner{background:#ffc100; display: block; height: 15px;border-radius:15px; position: absolute; top:0; left:0;}
.-task-widht-10{ width: 10%;}
.-task-widht-20{ width: 20%;}
.-task-widht-30{ width: 30%;}
.-task-widht-40{ width: 40%;}
.-task-widht-50{ width: 50%;}
.-task-widht-60{ width: 60%;}
.-task-widht-70{ width: 70%;}
.-task-widht-80{ width: 80%;}
.-task-widht-90{ width: 90%;}
.-task-widht-100{ width: 100%;}
.-footer-left{min-height:48px;background:#f5f5f5;}
.-footer-left ul {width: 100%}
.-footer-left ul li{ cursor: pointer; color:#666;}
.-footer-left ul li:hover{ color:#888;}
.-bg-black{ background:#2b2b2b; color:#f4f1ed;}
.-bg-darkblack{background:#1D1D1D; color: #fff;}
.task-answer-view{ border-top:1px solid #515151; background:#333;}
#blacktab_nav {height:40px;background:#292929; }
#blacktab_nav li {float:left; padding:0px 50px;text-align:center;height: 40px;line-height: 40px; }
#blacktab_nav .add-webssh{position:relative;}
#blacktab_nav .add-webssh span{position:absolute;top:0;right:5px;color:#fff;cursor:pointer;}
#blacktab_nav li a{font-size:14px; }
#blacktab_nav li.code-file-tab{padding: 0px;width: 120px;box-sizing: border-box;padding: 0px 15px;}
.code-flie-list{display:none;position: absolute;z-index: 5;top:40px;background: #515151;width: 300px;left: 0px;color: #fff;}
.blue-line{border-left: 3px solid #199ED8!important;padding-left: 5px;}
.codefile-all{max-height: 122px;overflow-y: auto;overflow-x: hidden;}
.codefile-all p{text-align: left;cursor: pointer;height: 22px;line-height: 22px;margin-bottom: 3px;padding-left: 5px;border-left: 3px solid #515151;width: 273px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;}
.codefile-all p:hover{background: #CCCCCC;color: #333;}
.blacktab_hover { background: #333;}
.blacktab_hover a{ color:#fff; }
.-task-ces-top{ padding:5px 15px; background:#515151; color:#bfbfbf;}
.-task-ces-info-left{ display: inline-block; width:100px; text-align: right; }
.-position-a-r15{ position: absolute; top:5px; right:15px;}
.-task-ml80{ margin-left: 80px;}
.page--over { position: fixed;top: 0; left:80px; right: 0; z-index:8000; height:100%; color:#fff;}
.-task-list-header{ border-bottom:1px solid #eee; padding:5px 15px; color:#898989; font-size: 14px; font-weight: normal;}
.-task-list-header h3{ font-weight: normal; font-size:16px; color:#333;}
.-task-list-inner{ background:#EFF2F7; margin:10px; padding:5px;}
.-task-list-title{ font-size: 14px; color: #666;word-wrap: break-word; font-weight: normal; max-width: 80%;}
.greytab-inner{ background:#fff; }
.blacktab-inner{ background:#333;}
.task-padding16{ padding:16px;}
.task-padding10{ padding:10px;}
.task-padding-new{ padding-top: 16px}
/* TPM统计 20170321byLB */
.panel-warp-3{ width: 30%; background:#23b181; color:#fff; margin:2.5%; margin-right:0; position: relative; }
.panel-warp-3-over{ background:#fff;opacity:0.8; color:#29bd8b; width: 100%; height:135px; position: absolute; top:0; left:0; text-align: center; padding-top:130px;}
.panel-warp-3-over a{color:#29bd8b; font-size: 18px; text-align:center; font-weight: bold;}
.panel-warp-img{width: 30px; height: 30px; border-radius:100px;}
.panel-warp-name{ display:block; max-width:100px;}
.panel-warp-inner{ padding:15px;}
.panel-warp-dbg{ background:#29bd8b; padding:15px; height:120px;}
.panel-warp-dbg li{ margin-bottom:15px; }
.panel-warp-dbg li:last-child{ margin-bottom:0;}
.fa-icons-trophy{ position:relative; padding-top:10px;}
.fa-icons-trophy span{ position:absolute; top:12px; right:10px; color:#f04b27; font-size:14px; font-weight: bold;}
.fa-icons-flower{ display: inline-block; width: 14px; height: 14px; background:url("../images/task/icons-flower.png") 0 0 no-repeat;}
.fa-icons-flower:hover{display:inline-block; width: 14px; height: 14px;background:url("../images/task/icons-flower.png") -18px 0 no-repeat;}
/* 实训首页 20170330byLB */
.task-index{ width: 1200px; margin:0 auto;}
.task-index-head{ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15); }
/*.task-index-head-top{background-image: linear-gradient(to right, rgb(106, 177, 216) 0%, rgb(1, 74, 78) 100%);background-color: rgb(1, 70, 74); padding:30px;}*/
/*background: linear-gradient(to right, rgb(104, 177, 215) 0%, rgb(1,75,79) 100%);*/
/*background: linear-gradient(to right,#5DDAE4,#23ADC9);*/
.task-index-head-top{ padding:30px;background:#FCA24B;background: linear-gradient(to right, rgb(104, 177, 215) 0%, rgb(1,75,79) 100%);}
/*.task-index-head-top{ padding:30px;background:#FFA65E;}*/
.top-xz{position: absolute;border:14px solid #FFFFFF;border-radius: 50%;box-shadow: 0px 2px 10px rgba(142,142,142,0.6);
opacity: 0.4;}
.task-index-head-top-course{padding:30px;background:linear-gradient(to right, rgb(69, 191, 165) 0%, rgb(164, 175, 247) 100%);}
/*linear-gradient(to right, rgb(69, 191, 165) 0%, rgb(164, 175, 247) 100%);*/
.task-inde-head-title{ color:#fff; }
.task-index-head-info{ background:#fff; padding:10px 30px;}
.task-index-head-info li{ width:100px; float: left; text-align: center; color:#666;}
.task-index-list{ width: 1200px;}
.task-index-list-box{box-sizing:border-box; width:23.87%;margin: 0 1.5% 30px 0px; border-radius:2px;border:1px solid #eee; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15); color:#666; position:relative; }
.task-index-list-box:hover{-webkit-animation: bounce-down 1s linear 1;animation: bounce-down 1s linear 1; }
.task-index-list-box:hover .black-half{display: block;}
.task-index-list-box:nth-child(4n+0) {margin: 0 0 30px 0;}
.task-mg8{ margin:0 15px 15px 0px; border-radius:2px; border:1px solid #eee; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15); color:#666; position:relative; }
.task-index-list-box-top{padding:16px; padding-top:30px; background:#fff; text-align: center; position:relative; height: 160px;}
.task-index-list-title{ max-width:80%; display: block; margin:10px auto 0px; font-size:14px; font-weight: bold;}
.task-index-list-user{padding:5px 10px; border-radius:25px;background: #F5F6F7; margin:0px auto 20px; display: inline-block;}
.task-index-list-box-bottom{ background: #F5F6F7; color:#666; padding:10px 10%; text-align: center;}
.task-index-list-box-bottom li{ display: inline; margin: 0 5px;}
.task-index-list-box-bottom2{ background: #fff; color:#666; padding:10px 10%; text-align: center;}
.task-index-list-box-bottom2 li{ display: inline; margin: 0 5px;}
.task-vip{ position: absolute; right:15px; top:15px;}
@-webkit-keyframes bounce-down {
25% {-webkit-transform: translateY(-10px);}
50%, 100% {-webkit-transform: translateY(0);}
}
@keyframes bounce-down {
25% {transform: translateY(-10px);}
50%, 100% {transform: translateY(0);}
}
.task-index-list-hover{ position:absolute; top:0; left:0; color:#fff; width: 100%; height: 100%; border-radius:2px 2px 0 0; }
.task-index-list-hover p{ margin:15px;overflow:hidden; text-align: left; height: 85%;}
.task-index-list-hover{ display: none; }
.task-mg8:hover .task-index-list-hover{display: block;}
.task-mg8:hover{ box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.15);cursor: pointer;}
.task-dropdown{}
.task-dropdown-menu{ min-width: 100px; border: 1px solid rgba(0,0,0,.05);box-shadow: 0 6px 12px rgba(0,0,0,.15);}
.task-dropdown-menu li a{ color:#666; }
/* 伸展型搜索 20170330byLB */
.search-wrapper {position: absolute; font-size:14px; }
.search-wrapper .input-holder { overflow: hidden; height: 30px; position: relative; width:32px;background: none;}
.search-wrapper.active .input-holder { width:320px; border:none; border-bottom:2px solid #ccc; }
.search-wrapper .input-holder .search-input { width:100%; height: 30px; font-size:14px; position: absolute; top:0px; left:0; border:none; opacity: 0; }
.search-wrapper.active .input-holder .search-input { opacity: 1; outline:none; background: none;}
.search-wrapper .search-icon { width:20px; height:20px; border:none; padding:0px; outline:none; position: relative; z-index: 2; float:right; cursor: pointer; background: none; color: #666; top:2px;}
.search-wrapper .close { position: absolute; z-index: 1; top:2px; right:20px; width:25px; height:25px; cursor: pointer; opacity: 0;color: #666;}
.search-wrapper.active .close {right:-35px; opacity: 1;}
a.sortArrowActiveD {background:url(../images/post_image_list.png) -0px -20px no-repeat; width:7px; height:9px; float:left; margin-top: 10px;margin-left: 5px;}
a.sortArrowActiveU {background:url(../images/post_image_list.png) -17px -20px no-repeat; width:7px; height:9px; float:left; margin-left:5px; margin-top:10px;}
.postSort {width:75px; float:right}
.shixunPostSort {width:60px; float:right}
.remove_li li{ list-style-type: none!important;}
a.shixun-task-btn { display: inline-block;font-weight: bold;border: none;padding: 0 12px;color: #666;letter-spacing: 1px;text-align: center;font-size: 14px;height: 30px;line-height: 30px;border-radius: 3px; }
a.shixun-task-ban-btn{background-color: #c2c4c6;display: inline-block;font-weight: bold;border: none;padding: 0 12px;color: #666;letter-spacing: 1px;text-align: center;font-size: 14px;height: 30px;line-height: 30px;border-radius: 3px; cursor: default;}
.shixun-panel-list > div:nth-child(odd){ background:#f9f9f9; }
.shixun-panel-list > div:nth-child(even){ background:#fff; }
.shixun-panel-list {background: #fff; margin: 0 1px;}
.shixun-panel-inner { background: #EFF2F7; padding: 15px; height: 70px;}
.challange_operate{display: none}
.shixun-panel-inner:hover .challange_operate{display: block}
.shixun_title {color: #333;font-size: 16px;}
.g_frame{border: 1px solid #29bd8b;color: #29bd8b;padding:0 5px;border-radius: 3px;text-align:center;}
.loading-center{text-align: center; align-items: center;justify-content: center;}
.center{vertical-align: middle;text-align: center; }
.itoblock_w150{ display: block; float:left; width:150px }
.itoblock_w75{ display: block; float:left; width:75px }
/*实训--技能勋章*/
.modal-list li{float: left;padding: 0px 15px;background:#ff7500;color: #ffffff;border-radius: 4px;margin-right: 10px}
.modal-list li:before{content: '●';color: #FFFFFF;margin-right: 5px;font-size: 14px}
.modal-list span{width: 8px;height: 8px;border-radius: 50%;background: #ffffff;display: block;float: left;margin-right: 5px;margin-top:10px;}
/* 合作者 20170516byLB */
.task-partner-list{ padding:15px; border-bottom:1px solid #eee;}
.task-width33{ width:33.3%;}
.read_only{ -moz-user-select: none; -webkit-user-select: none; }
.task-form-28{width: 28%;padding:0px 10px}
/* 实训首页的搜索 */
.xy_box{padding:16px;height:180px}
.task_yx_bo{margin: 0px auto 13px;}
.course-nav-box{padding:0px 10px;margin:30px 0px}
.xy_level{width: 80%;margin: 0px auto;border-top: 1px solid #eee;margin-top: 5px;line-height: 35px;}
.course-nav-row{padding:7px 0px}
.course-nav-row_item li{width:auto;height: 30px;line-height: 30px;margin: 5px;padding:0px 15px;}
.course-nav-row_item label{cursor: pointer;}
.check_item{height:40px;line-height: 40px;padding: 0px 15px;}
.more_check{position: absolute;bottom: 5px;right: 10px;cursor: pointer;}
.bottomdashed1{border-bottom: 1px dashed #eeeeee;}
/*更多和收起*/
.two_line_lesson{height: 80px;overflow: hidden;}
.more_line_lesson{max-height: 200px;display: block;}
.scroll_lesson{overflow-x: hidden;overflow-y: scroll;}
.searchFor{width:auto;}
.searchFor .searchCon{width:250px;border-bottom:1px solid #cccccc;float: left;height: 30px;}
.searchFor .searchCon input{border: none;outline: none;height: 29px;width:91%;}
.searchFor .searchImg{margin:5px 10px 0px 0px;cursor: pointer;}
.searchFor .search_close{font-size: 18px;float: right;color: #666;height: 29px;line-height: 29px;cursor: pointer;}
.tab_color{color: #bfbfbf!important;}
/*_game_show.html.erb页面新增的一个tab*/
.comments_item_content img{border-radius: 50%;margin-right: 5px}
.comment_item_one{flex: 1;}
.comment_item_bottom{border-bottom: 1px solid #efefef;display: flex}
.comment_item_top{border-top: 1px solid #efefef}
.comment_item_left_green{border-left: 3px solid #29bd8b}
.return_item{height: 20px;line-height: 20px;margin-top: 5px;}
.comment-input{width: 100%;margin: 10px;margin-right: 17px;}
.comment-input textarea{border: none !important;width:100%; outline: none;height: 30px;border-radius: 4px;padding-left: 5px;float:left}
.comment_position{ position: absolute;bottom: 8px;right: 20px}
/*-------新建阶段添加选项部分----------*/
.option-item{border:1px solid #e2e2e2;}
.option-item,.add-option-item{display: block;width: 38px;height: 38px;text-align: center;line-height: 38px;border-radius: 4px}
.check-option-bg{background: #FF7500;color: #ffffff!important;border: 1px solid #FF7500}
.add-option-input{padding: 5px;width: 90%;height: 40px;min-width: 700px;}
.add-option-input a{display: block;width: 100%;height: 100%;cursor: pointer}
.position-delete{position: absolute;right: -22px;top: 12px;cursor: pointer}
/*--------TPI的答案选项卡------*/
.quiz-task-options:not(.-compact) {padding:10px;}
.card {position: relative;border-radius: 2px;overflow: hidden;}
/*.card:hover{background: #3f3f3f;}*/
.card-check{background: #3498db!important;}
.-justify {justify-content: space-between;}
.-center { align-items: center;min-height: 66px;}
.markdown {letter-spacing: 0;line-height: 1.6;word-wrap: break-word;word-break: break-word;}
.markdown code {padding:0;line-height: 23px;margin: 0;font-family: "微软雅黑","宋体";}
/*模拟实战---加载等待*/
.loading_all{background:#ffffff;z-index: 100000;width: 100%;height: 100%;position: fixed;left: 0px;top:0px;text-align: center;}
.loading_main img{border-radius: 4px;}
.loading_main span{font-size: 44px;font-weight: bold;color: #ff7500;letter-spacing: 5px;margin-left: 5px;}
.load{width: auto;top:50%;margin-top:-100px;position: relative;}
.loading_seconde{color: #ff7500;letter-spacing: 3px;font-size: 16px;}
#ajax-indicator-base {
position: absolute; /* fixed not supported by IE*//*
top: 50%;
left: 50%;
margin-left: -20px;
margin-top: -40px;
width: 20%;
height: 5%; */
width: 100%;
height: 100%;
font-weight:bold;
text-align:center;
/*padding:0.6em;*/
z-index:9999;
background: rgba(225,225,225,0);
}
html>body #ajax-indicator-base { position: fixed; }
#ajax-indicator-base embed{
position: relative;
top: 40%;
width: 40px;
height: 40px;
margin-left: -40px;
left: 50%;
}
/*#ajax-indicator-base span{
color:#fff;
background-position: 0% 40%;
background-repeat: no-repeat;
*//*background-image: url(/images/loading.gif);*//*
padding-left: 26px;
vertical-align: bottom;
z-index:999;
}*/
.save-tip{display:none;position: fixed;top:0px;left: 0px;width: 100%;height: 100%;}
.save-tip-content{position: absolute;top:50%;left: 50%;margin-left: -36px;margin-top:-19px;background: rgba(0,0,0,0.7);color:#fff;padding:5px 15px;border-radius: 4px}
.empty{background: #494A4C;display: inline; margin: 0 2px; padding: 0 3px;}
.tab-key{background: #494A4C;display: inline; margin: 0 2px; padding: 0 6px;}
/*二次回复的提示语的样式*/
.points-data-tip-top{position:absolute;left:100px;top:-30px;opacity:.7;width:150px;height:30px;z-index:9999;display:none;}
.data-tip-top1{position:relative;box-shadow:0px 0px 8px #000;background:#000;color:#fff;word-wrap: break-word;
text-align:center;border-radius:4px;padding:0 10px;border:1px solid #000;}
.data-tip-top1:after,.data-tip-top1:before{position: absolute;content:'';width:0;height:0;left: 45%;bottom:-10px;border-left: 5px solid transparent;
border-right: 5px solid transparent;border-top: 10px solid #000;}
.data-tip-top1:before{bottom:-11px;}
/*选择题tab切换*/
.nav_option li{overflow: hidden;width: 110px; text-align: center;cursor: pointer;height: 38px;line-height: 38px;border-top-right-radius: 5px;border-top-left-radius: 5px;border:1px solid #e2e2e2;border-bottom: 0px;color: #FF7500;border-right: none;}
.nav_option li:last-child{border-right: 1px solid #e2e2e2;}
.nav_option li a{width: 100%;height: 100%;display: block;}
/*---------------------试卷----------------------*/
.question_item_con{font-weight: normal!important;border:1px solid #EEEEEE!important;color: #333!important;background: #FFFFff!important;position: relative}
.exam_operator{cursor: pointer;position: absolute;right: 15px;top: 11px;}
.question_item_con .write_answer{border-top:1px solid #EEEEEE;background:#EFF9FD;padding: 10px 15px;text-align:justify;}
.add_item_part{width: auto;padding: 2px 20px;border: 1px solid #ff7500;border-radius: 3px;margin-left: 15px;cursor: pointer;color: #ff7500!important;}
.add_item_part:hover{color:#fff!important;background-color: #ff7500}
/*作业问答*/
.work_search_ul{border: 1px solid #EEEEEE;border-radius: 4px;}
.work_search_ul li span{display:block;float: left;height: 38px;line-height: 38px}
.work_search_ul li{border-bottom: 1px dashed #EEEEEE;}
.work_search_ul li:last-child{border-bottom: none}
.work_search_ul .magic-radio + label,.work_search_ul .magic-checkbox + label{top:5px}
/*更新提示*/
.update_back_main{display: none;position: fixed;left: 0px;top:0px;background: rgba(0,0,0,0.3);width: 100%;z-index: 7001;height: 100%;}
.tip-panel-animate-left{position: absolute;z-index: 9000;left: 80px;top:290px;background: #FFFFff;width: 430px;height: 140px;border-radius: 3px;}
.tip-panel-animate{position: absolute;z-index: 10001;right: 4px;top:40px;background: #FFFFff;width: 430px;height: 140px;border-radius: 3px;display: none}
.tip-panel-animate .tip-img,.tip-panel-animate-left .tip-img{width: 130px;text-align: center;background-color: #E8E9ED;height: 100%;}
.tip-panel-animate .tip-img img,.tip-panel-animate-left .tip-img img{width: 70px;height: 70px;margin: 35px 30px;}
.tip-right-con{width: 69.7%;height: 100%;}
.tip-operator-btn{width:100%;border-top: 1px solid #eee;height: 40px;position: absolute;right: 0px;bottom: 0px;text-align: center;}
.tip-operator-btn a,.tip-operator-btn span{height: 100%;text-align: center;line-height: 40px;width: 50%}
.tip-operator-btn a:hover,.tip-operator-btn span:hover{background-color:#f9f9f9}
.tip-operator-btn a:first-child,.tip-operator-btn span:first-child{border-right: 1px solid #eee;width: 49.5%}
.animate-tip{animation:rightToleft 1s;}
.animate-tip-hide{animation:leftToright 1s;}
@keyframes rightToleft
{
from {right: -400px;}
to {right: 4px;}
}
@keyframes leftToright
{
from {right: 4px;}
to {right: -420px;}
}
.animate-tip-l{animation:rightToleft-l 1s;}
.animate-tip-hide-l{animation:leftToright-l 1s;}
@keyframes rightToleft-l
{
from {left: -400px;}
to {left: 80px;}
}
@keyframes leftToright-l
{
from {left: 80px;}
to {left: -420px;}
}
/*----------实训TPI图片查看效果--------------*/
.photo_display{box-sizing: border-box;width: 100%;position: fixed;top: 0px;left: 0px;padding-top: 64px;padding-left: 80px;background: rgba(0,0,0,0);height: 100%;z-index: 100}
.photo_display .task-popup{width: 100%!important;height: 100%!important;}
#picture-content img{max-width: 100%;height: 400px;display: block; margin:0px auto;margin-bottom: 20px;}
#box-img{width:100%;height:100%;display:table;text-align:center;background:#fff;}
#box-img span{display:table-cell;vertical-align:middle;}
/*-------------学员统计 通关排行榜------------*/
.rankings_num{position: absolute;width: 100%;top: 3px;height: 15px;line-height: 15px;left: 0px;font-size: 12px;color: #F24B27;text-align: center}
.census_main{width: 1086px;overflow: hidden;position: relative;min-height: 350px;margin:0px 45px;}
.census_main ul{position: absolute;min-width: 1086px;}
.census_main ul>li{float:left;width: 260px;margin:6px 6px;min-height: 335px}
.census_main ul>li:nth-child(4n){margin-right: 0px;}
.part_main{border-radius: 5px;background: #FFFFff;border:1px solid #EEEEEE}
.part_main .part_top{background: #FF9E6A;color: #FFFFff;padding: 10px 15px;border-radius: 5px 5px 0px 0px;}
.wipe{display: none;cursor: pointer;line-height: 332px;color:#FFFFff!important;font-size:16px ;width: 100%;position: absolute;left: 0px;top:0px;background:rgba(0,0,0,0.3);height: 100%;z-index: 3;text-align: center;border-radius: 5px; }
.part_main:hover .wipe{display: block;}
#census_left,#census_right{display: none;position: absolute;cursor: pointer;background: #FCF2EC;padding: 10px 5px;width: 35px;box-sizing: border-box;top:122.5px;text-align: center}
#census_left i,#census_right i{color:#FBBD81;}
/*-----------实训配置、评测脚本-------------*/
.edit_script_text .test_script_text{word-break: break-all;background-color: #f7f7f7;}
.edit_script_text .CodeMirror-lines{padding: 0px!important;padding-bottom: 4px}

BIN
public/favicon.ico Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -1,30 +1,213 @@
<!DOCTYPE html>
<html lang="zh-CN" class="notranslate translated-ltr" translate="no">
<head>
<meta charset="utf-8">
<meta name=”Keywords” Content=”trustie,trustieforge,forge,确实让创建更美好,协同开发平台″>
<meta name=”Keywords” Content=”TrustieOpenSourceProject″>
<meta name=”Keywords” Content=”issue,bug,tracker,软件工程,课程实践″>
<meta name=”Description” Content=”持续构建协同、共享、可信的软件创建生态开源创作与软件生产相结合,支持大规模群体开展软件协同创新活动”>
<meta name="theme-color" content="#000000">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/iconfont.css">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/edu-purge.css">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/editormd.min.css">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/merge.css">
<%= htmlWebpackPlugin.tags.headTags %>
</head>
<body>
<!--用于markdown转html -->
<div id="md_div" style="display: none;"></div>
<div id="root" class="page -layout-v -fit widthunit"></div>
<div id="picture_display" style="display: none;"></div>
<script src="%PUBLIC_URL%js/jquery-1.8.3.min.js"></script>
<script src="%PUBLIC_URL%js/js_min_all.js"></script>
<script src="%PUBLIC_URL%js/codemirror/codemirror.js"></script>
<script src="%PUBLIC_URL%js/editormd/editormd.min.js"></script>
<script src="%PUBLIC_URL%js/codemirror/merge/merge.js"></script>
<%= htmlWebpackPlugin.tags.bodyTags %>
</body>
</html>
<html lang="en">
<head>
<meta charset="utf-8">
<!--<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">-->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- width=device-width, initial-scale=1 , shrink-to-fit=no -->
<!-- <meta name="viewport" content=""> -->
<meta name=”Keywords” Content=”EduCoder,信息技术实践教学,精品课程网,慕课MOOC″>
<meta name=”Keywords” Content=”实践课程,项目实战,java实训,python实战,人工智能技术,后端开发学习,移动开发入门″>
<meta name=”Keywords” Content=”翻转课堂,高效课堂创建,教学模式″>
<meta name=”Keywords” Content=”实训项目,python教程,C语言入门,java书,php后端开发,app前端开发,数据库技术″>
<meta name=”Keywords” Content=”在线竞赛,计算机应用大赛,编程大赛,大学生计算机设计大赛,全国高校绿色计算机大赛″>
<meta name=”Description” Content=”EduCoder是信息技术类实践教学平台。EduCoder涵盖了计算机、大数据、云计算、人工智能、软件工程、物联网等专业课程。超10000个实训案例及22000个技能评测点建立学、练、评、测一体化实验环境。”>
<meta name=”Description” Content=”EduCoder实践课程旨在于通过企业级实战实训案例帮助众多程序员提升各项业务能力。解决学生、学员、企业员工等程序设计能力、算法设计能力、问题求解能力、应用开发能力、系统运维能力等。”>
<meta name=”Description” Content=”EduCoder翻转课堂教学模式颠覆了传统教学模式让教师与学生的关系由“权威”变成了“伙伴”。将学习的主动权转交给学生使学生可个性化化学学生的学习主体得到了彰显。”>
<meta name=”Description” Content=”EduCoder实训项目为单个知识点关卡实践训练帮助学生巩固单一弱点强化学习。 >
<meta name=”Description” Content=”EduCoder实践教学平台各类大赛为进一步提高各类学生综合运用高级语言程序设计能力培养创新意识和实践探索精神发掘优秀软件人才。 >
<!-- <meta name="viewport" id="viewport" content="width=device-width, initial-scale=0.3, maximum-scale=0.3, user-scalable=no">-->
<meta name="viewport" id="viewport" content="width=device-width, initial-scale=0.3, maximum-scale=0.3">
<meta name="theme-color" content="#000000">
<!--<meta http-equiv="cache-control" content="no-cache,no-store, must-revalidate" />-->
<!--<meta http-equiv="pragma" content="no-cache" />-->
<!--<meta http-equiv="Expires" content="0" />-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<!-- <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">-->
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<!-- <title>EduCoder</title>-->
<!--react-ssr-head-->
<script type="text/javascript">
window.__isR = true;
// 不支持ie9 ie10
if (
( navigator.userAgent.indexOf('MSIE 9') != -1
|| navigator.userAgent.indexOf('MSIE 10') != -1 )
&&
location.pathname.indexOf("/compatibility") == -1) {
debugger;
// location.href = './compatibility'
location.href = '/compatibility.html'
}
// const isMobile = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()));
const isWeiXin = (/MicroMessenger/i.test(navigator.userAgent.toLowerCase()));
if (isWeiXin) {
document.write('<script type="text/javascript" src="/javascripts/wx/jweixin-1.3.0.js"><\/script>');
}
</script>
<!-- <link rel="stylesheet" type="text/css" href="/css/edu-common.css">
<link rel="stylesheet" type="text/css" href="/css/edu-public.css">
<link rel="stylesheet" type="text/css" href="/css/taskstyle.css">
<link rel="stylesheet" type="text/css" href="/css/font-awesome.css">
<link rel="stylesheet" type="text/css" href="/css/editormd.min.css">
<link rel="stylesheet" type="text/css" href="/css/merge.css"> -->
<link rel="stylesheet" type="text/css" href="/css/css_min_all.css">
<!--<link rel="stylesheet" type="text/css" href="/css/css_min_all.css">-->
<!--<link rel="stylesheet" type="text/css" href="//at.alicdn.com/t/font_653600_nm6lho7nxxq.css">-->
<!-- <link href="/react/build/css/iconfont.css" rel="stylesheet" type="text/css"> -->
<!--<link href="http://47.96.87.25:48080/stylesheets/educoder/edu-all.css" rel="stylesheet" type="text/css">-->
<!--<link href="https://pandao.github.io/editor.md/examples/css/style.css" rel="stylesheet" type="text/css">-->
<!--<link href="https://pandao.github.io/editor.md/css/editormd.preview.css" rel="stylesheet" type="text/css">-->
<!-- <link href="https://testeduplus2.educoder.net/stylesheets/css/edu-common.css" rel="stylesheet" type="text/css">
<link href="https://testeduplus2.educoder.net/stylesheets/educoder/edu-main.css" rel="stylesheet" type="text/css">
<link href="https://testeduplus2.educoder.net/stylesheets/educoder/antd.min.css" rel="stylesheet" type="text/css"> -->
<!-- <link rel="stylesheet" type="text/css" href="https://www.educoder.net/stylesheets/css/font-awesome.css?1510652321"> -->
<!--<link rel="stylesheet" type="text/css" href="http://47.96.87.25:48080/stylesheets/educoder/iconfont/iconfont.css">-->
<!--需要去build js配置-->
<link rel="stylesheet" type="text/css" href="/css/iconfont.css">
<link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/quill/1.3.7/quill.core.min.css">
<style>
/*<!--去除浏览器点击操作后有蓝色的底块-->*/
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
</style>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<!--用于markdown转html -->
<div id="md_div" style="display: none;"></div>
<div id="root" class="page -layout-v -fit widthunit">
<!--<div class="d2-home">-->
<!--<div class="d2-home__main">-->
<!--&lt;!&ndash;<img class="d2-home__loading"&ndash;&gt;-->
<!--&lt;!&ndash;src="loading-spin.svg"&ndash;&gt;-->
<!--&lt;!&ndash;alt="loading">&ndash;&gt;-->
<!--<div class="lds-ripple"><div></div><div></div></div>-->
<!--<div class="d2-home__title">-->
<!--正在加载资源-->
<!--</div>-->
<!--<div class="d2-home__sub-title">-->
<!--加载资源可能需要较多时间 请耐心等待-->
<!--</div>-->
<!--</div>-->
<!--<div class="d2-home__footer">-->
<!--&lt;!&ndash;<a href="www.educoder.net"&ndash;&gt;-->
<!--&lt;!&ndash;target="_blank">&ndash;&gt;-->
<!--&lt;!&ndash;&ndash;&gt;-->
<!--&lt;!&ndash;</a>&ndash;&gt;-->
<!--EduCoder-->
<!--</div>-->
<!--</div>-->
</div>
<div id="picture_display" style="display: none;"></div>
<!-- js css合并 文件优先级的问题 -->
<script type="text/javascript" src="/js/js_min_all.js"></script>
<!-- <script type="text/javascript" src="/js/js_min_all_2.js"></script> -->
<!--
<script type="text/javascript" src="/js/jquery-1.8.3.min.js"></script>
<script type="text/javascript" src="/js/editormd/underscore.min.js"></script>
<script type="text/javascript" src="/js/editormd/marked.min.js"></script>
<script type="text/javascript" src="/js/editormd/prettify.min.js"></script>
<script type="text/javascript" src="/js/editormd/raphael.min.js"></script>
<script type="text/javascript" src="/js/editormd/sequence-diagram.min.js"></script>
<script type="text/javascript" src="/js/editormd/flowchart.min.js"></script>
<script type="text/javascript" src="/js/editormd/jquery.flowchart.min.js"></script>
<script type="text/javascript" src="/js/editormd/editormd.min.js"></script>
-->
<!-- codemirror addon -->
<!--
<script type="text/javascript" src="/js/codemirror/codemirror.js"></script>
<script type="text/javascript" src="/js/codemirror/mode/javascript.js"></script>
<script type="text/javascript" src="/js/diff_match_patch.js"></script>
<script type="text/javascript" src="/js/merge.js"></script>
<script type="text/javascript" src="/js/edu_tpi.js"></script>
-->
<!-- testbdweb.trustie.net testbdweb.educoder.net -->
<!-- 在tpi js里加载这3个脚本 -->
<script>
(function() { // Scoping function to avoid globals
var href = location.href;
if(window.location.port === "3007"){
if (href.indexOf('/tasks/') != -1) {
document.write('<script type="text/javascript" src="https://newweb.educoder.net/assets/kindeditor/kindeditor.js"><\/script>');
// build.js中会将这个url附加一个前缀 react/build
document.write('<script type="text/javascript" src="/js/create_kindeditor.js"><\/script>');
document.write('<script type="text/javascript" src="https://newweb.educoder.net/javascripts/educoder/edu_application.js"><\/script>');
} else if (href.indexOf('/paths/') != -1) {
document.write('<script type="text/javascript" src="https://newweb.educoder.net/javascripts/educoder/edu_application.js"><\/script>');
}
}else{
if (href.indexOf('/tasks/') != -1) {
document.write('<script type="text/javascript" src="/assets/kindeditor/kindeditor.js"><\/script>');
// build.js中会将这个url附加一个前缀 react/build
document.write('<script type="text/javascript" src="/js/create_kindeditor.js"><\/script>');
document.write('<script type="text/javascript" src="/javascripts/educoder/edu_application.js"><\/script>');
} else if (href.indexOf('/paths/') != -1) {
document.write('<script type="text/javascript" src="/javascripts/educoder/edu_application.js"><\/script>');
}
}
})();
</script>
<!-- <script type="text/javascript" src="https://testeduplus2.educoder.net/assets/kindeditor/kindeditor.js"></script>
<script type="text/javascript" src="/js/create_kindeditor.js"></script>
<script type="text/javascript" src="https://testeduplus2.educoder.net/javascripts/educoder/edu_application.js"></script> -->
<script type="text/javascript" src="https://cdn.bootcss.com/quill/1.3.7/quill.core.min.js"></script>
<!-- <script>-->
<!-- document.body.addEventListener('touchmove', function (e) {-->
<!-- e.preventDefault(); //阻止默认的处理方式(阻止下拉滑动的效果)-->
<!-- }, {passive: false});-->
<!-- </script>-->
</body>
</html>

View File

@ -11267,7 +11267,7 @@ Copyright © 2018 Basecamp, LLC
type: "GET",
dataType: "script",
cache: true,
async: true,
async: false,
global: false,
// Only evaluate the response if it is successful (gh-4126)
@ -17573,7 +17573,7 @@ Copyright © 2018 Basecamp, LLC
var actualPadding = document.body.style.paddingRight;
var calculatedPadding = $(document.body).css('padding-right');
// $(document.body).data('padding-right', actualPadding).css('padding-right', parseFloat(calculatedPadding) + this._scrollbarWidth + "px");
$(document.body).data('padding-right', actualPadding).css('padding-right', parseFloat(calculatedPadding) + this._scrollbarWidth + "px");
}
$(document.body).addClass(ClassName.OPEN);
@ -17597,9 +17597,9 @@ Copyright © 2018 Basecamp, LLC
}
}); // Restore body padding
// var padding = $(document.body).data('padding-right');
// $(document.body).removeData('padding-right');
// document.body.style.paddingRight = padding ? padding : '';
var padding = $(document.body).data('padding-right');
$(document.body).removeData('padding-right');
document.body.style.paddingRight = padding ? padding : '';
};
_proto._getScrollbarWidth = function _getScrollbarWidth() {

376
public/js/create_kindeditor.js Normal file → Executable file
View File

@ -1,5 +1,5 @@
//需求:表情栏可以隐藏显示,高度只要一点高
function sd_create_editor(params) {
function sd_create_editor(params){
// var minHeight; //最小高度
var paramsHeight = params.height; //设定的高度
var id = arguments[1] ? arguments[1] : undefined;
@ -7,40 +7,40 @@ function sd_create_editor(params) {
var paramsWidth = params.width == undefined ? "100%" : params.width;
var editor = params.kindutil.create(params.textarea, {
resizeType: 1, minWidth: "1px", width: "94%",
height: "33px",// == undefined ? "30px":paramsHeight+"px",
minHeight: "33px",// == undefined ? "30px":paramsHeight+"px",
width: params.width,
resizeType : 1,minWidth:"1px",width:"94%",
height:"33px",// == undefined ? "30px":paramsHeight+"px",
minHeight:"33px",// == undefined ? "30px":paramsHeight+"px",
width:params.width,
/*
items:['emoticons','fontname',
'forecolor', 'hilitecolor', 'bold', '|', 'justifyleft', 'justifycenter', 'insertorderedlist','insertunorderedlist', '|',
'formatblock', 'fontsize', '|','indent', 'outdent',
'|','imagedirectupload','more'],*/
items: ['code', 'emoticons', 'fontname',
'forecolor', 'hilitecolor', 'bold', '|', 'justifyleft', 'justifycenter', 'insertorderedlist', 'insertunorderedlist', '|',
'formatblock', 'fontsize', '|', 'indent', 'outdent',
'|', 'imagedirectupload', 'table', 'media', 'preview', "more"
items : ['code','emoticons','fontname',
'forecolor', 'hilitecolor', 'bold', '|', 'justifyleft', 'justifycenter', 'insertorderedlist','insertunorderedlist', '|',
'formatblock', 'fontsize', '|','indent', 'outdent',
'|','imagedirectupload','table', 'media', 'preview',"more"
],
afterChange: function () {//按键事件
afterChange:function(){//按键事件
var edit = this.edit;
var body = edit.doc.body;
edit.iframe.height(paramsHeight);
this.resize(null, Math.max((params.kindutil.IE ? body.scrollHeight : (params.kindutil.GECKO ? body.offsetHeight + 26 : body.offsetHeight + 7)), paramsHeight));
this.resize(null, Math.max((params.kindutil.IE ? body.scrollHeight : (params.kindutil.GECKO ? body.offsetHeight+26:body.offsetHeight+7)) , paramsHeight));
},
afterBlur: function () {
afterBlur:function(){
//params.toolbar_container.hide();
params.textarea.blur();
sd_check_editor_form_field({ content: this, contentmsg: params.contentmsg, textarea: params.textarea });
if (this.isEmpty()) {
sd_check_editor_form_field({content:this,contentmsg:params.contentmsg,textarea:params.textarea});
if(this.isEmpty()) {
this.edit.html("<span id='hint' style='color:#999999;font-size:12px;'>我要回复</span>");
}
//params.toolbar_container.hide();
$('#reply_image_' + id).addClass('imageFuzzy');
if (/^\s*<\w*\s*\w*\=\"\w*\"\s*\w*\=\"\w*\:\s*\#\d*\;\s*\w*\-\w*\:\s*\w*\;\"\>[\u4e00-\u9fa5]*<\/\w*\>\s*$/.test(this.edit.html())) {
if(/^\s*<\w*\s*\w*\=\"\w*\"\s*\w*\=\"\w*\:\s*\#\d*\;\s*\w*\-\w*\:\s*\w*\;\"\>[\u4e00-\u9fa5]*<\/\w*\>\s*$/.test(this.edit.html())){
params.submit_btn.hide();
params.toolbar_container.hide();
this.resize("100%", null);
} else if (this.edit.html().trim() != "") {
}else if(this.edit.html().trim() != ""){
params.submit_btn.show();
params.toolbar_container.show();
}
@ -48,10 +48,10 @@ function sd_create_editor(params) {
//params.submit_btn.css("display","none");
},
afterFocus: function () {
afterFocus: function(){
var edit = this.edit;
var body = edit.doc.body;
if (/^\s*<\w*\s*\w*\=\"\w*\"\s*\w*\=\"\w*\:\s*\#\d*\;\s*\w*\-\w*\:\s*\w*\;\"\>[\u4e00-\u9fa5]*<\/\w*\>\s*$/.test(edit.html())) {
if(/^\s*<\w*\s*\w*\=\"\w*\"\s*\w*\=\"\w*\:\s*\#\d*\;\s*\w*\-\w*\:\s*\w*\;\"\>[\u4e00-\u9fa5]*<\/\w*\>\s*$/.test(edit.html())){
edit.html('');
}
params.submit_btn.show();
@ -67,12 +67,12 @@ function sd_create_editor(params) {
},
afterCreate: function () {
afterCreate:function(){
//params.submit_btn.hide();
var toolbar = $("div[class='ke-toolbar']", params.div_form);
toolbar.css('display', 'inline');
toolbar.css('padding', 0);
$(".ke-outline>.ke-toolbar-icon", toolbar).append('表情');
var toolbar = $("div[class='ke-toolbar']",params.div_form);
toolbar.css('display','inline');
toolbar.css('padding',0);
$(".ke-outline>.ke-toolbar-icon",toolbar).append('表情');
params.toolbar_container.append(toolbar);
params.toolbar_container.hide();
params.submit_btn.hide();
@ -85,9 +85,9 @@ function sd_create_editor(params) {
paramsHeight = paramsHeight == undefined ? params.kindutil.removeUnit(this.height) : paramsHeight;
edit.iframe.height(paramsHeight);
edit.html("<span id='hint' style='color:#999999;font-size:12px;'>我要回复</span>");
this.resize(null, paramsHeight);// Math.max((params.kindutil.IE ? body.scrollHeight : body.offsetHeight)+ paramsHeight , paramsHeight)
this.resize(null,paramsHeight);// Math.max((params.kindutil.IE ? body.scrollHeight : body.offsetHeight)+ paramsHeight , paramsHeight)
// params.toolbar_container.hide();
if (typeof enableAt === 'function') {
if(typeof enableAt === 'function'){
enableAt(this, id, type);
}
}
@ -95,7 +95,7 @@ function sd_create_editor(params) {
return editor;
}
function sd_create_shixun_editor(params) {
function sd_create_shixun_editor(params){
// var minHeight; //最小高度
var paramsHeight = params.height; //设定的高度
var id = arguments[1] ? arguments[1] : undefined;
@ -103,20 +103,20 @@ function sd_create_shixun_editor(params) {
var paramsWidth = params.width == undefined ? "100%" : params.width;
var editor = params.kindutil.create(params.textarea, {
resizeType: 1, minWidth: "1px", width: "94%",
height: "33px",// == undefined ? "30px":paramsHeight+"px",
minHeight: "33px",// == undefined ? "30px":paramsHeight+"px",
width: params.width,
resizeType : 1,minWidth:"1px",width:"94%",
height:"33px",// == undefined ? "30px":paramsHeight+"px",
minHeight:"33px",// == undefined ? "30px":paramsHeight+"px",
width:params.width,
/*
items:['emoticons','fontname',
'forecolor', 'hilitecolor', 'bold', '|', 'justifyleft', 'justifycenter', 'insertorderedlist','insertunorderedlist', '|',
'formatblock', 'fontsize', '|','indent', 'outdent',
'|','imagedirectupload','more'],*/
items: ['imagedirectupload'],
afterChange: function () {//按键事件
if (this.isEmpty() || this.edit.doc.body.innerText == '说点什么') {
$('#mini_comment_section').height('auto')
} else {
items : ['imagedirectupload'],
afterChange:function(){//按键事件
if(this.isEmpty() || this.edit.doc.body.innerText == '说点什么') {
$('#mini_comment_section').height('auto')
} else {
var edit = this.edit;
var body = edit.doc.body;
var newHeight = 0;
@ -125,14 +125,14 @@ function sd_create_shixun_editor(params) {
if (FF) { // 火狐下处理方式不一样
newHeight = $(body).height()
} else {
$(body).children().each(function () { newHeight += $(this).height() });
$(body).children().each(function(){newHeight+=$(this).height()});
}
// var newHeight = $(body).height()
var maxHeight = 357 // $(window).height() - 150 - 57; // 150 上部距离 57 下部距离
newHeight = newHeight <= maxHeight ? newHeight : maxHeight
if (newHeight > 150) {
if (FF) { // 火狐下处理方式不一样
@ -140,9 +140,9 @@ function sd_create_shixun_editor(params) {
} else {
this.resize("100%", newHeight + 'px');
}
$('#mini_comment_section').height(newHeight + 57)
$('#mini_comment_section').height(newHeight+57)
} else {
this.resize("100%", '150px');
this.resize("100%", '150px');
$('#mini_comment_section').height('auto')
}
}
@ -150,19 +150,19 @@ function sd_create_shixun_editor(params) {
//edit.iframe.height(paramsHeight);
//this.resize(null, Math.max((params.kindutil.IE ? body.scrollHeight : (params.kindutil.GECKO ? body.offsetHeight+26:body.offsetHeight+7)) , 15));
},
afterBlur: function () {
afterBlur:function(){
//params.toolbar_container.hide();
params.textarea.blur();
sd_check_editor_form_field({ content: this, contentmsg: params.contentmsg, textarea: params.textarea });
if (this.isEmpty()) {
sd_check_editor_form_field({content:this,contentmsg:params.contentmsg,textarea:params.textarea});
if(this.isEmpty()) {
$('#mini_comment_section').height('auto')
this.edit.html("<span id='hint' style='color:#999999;font-size:14px;'>说点什么</span>");
params.submit_btn.hide();
params.toolbar_container.hide();
this.resize("100%", "30px");
$("#dis_reply_id").val("");
if ($("#editor_panel").length > 0) {
$("#editor_panel").attr("style", "margin-top:9px;flex: 1;");
if($("#editor_panel").length>0){
$("#editor_panel").attr("style","margin-top:9px;flex: 1;");
$("#editor_panel").parents("form").addClass("df")
}
}
@ -180,10 +180,10 @@ function sd_create_shixun_editor(params) {
// $('#mini_comment_section').height('auto')
},
afterFocus: function () {
afterFocus: function(){
var edit = this.edit;
var body = edit.doc.body;
if (/^\s*<\w*\s*\w*\=\"\w*\"\s*\w*\=\"\w*\:\s*\#\d*\;\s*\w*\-\w*\:\s*\w*\;\"\>[\u4e00-\u9fa5]*<\/\w*\>\s*$/.test(edit.html())) {
if(/^\s*<\w*\s*\w*\=\"\w*\"\s*\w*\=\"\w*\:\s*\#\d*\;\s*\w*\-\w*\:\s*\w*\;\"\>[\u4e00-\u9fa5]*<\/\w*\>\s*$/.test(edit.html())){
edit.html("");
}
params.submit_btn.show();
@ -192,14 +192,14 @@ function sd_create_shixun_editor(params) {
// params.toolbar_container.show();
$('#reply_image_' + id).removeClass('imageFuzzy');
//edit.iframe.width(paramsWidth);
var newHeight = $(body).height()
if (newHeight < 150) {
this.resize("100%", "150px");
this.resize(paramsWidth, "150px");
}
if ($("#editor_panel").length > 0) {
$("#editor_panel").attr("style", "width:100%;margin-top:9px;");
if($("#editor_panel").length>0){
$("#editor_panel").attr("style","width:100%;margin-top:9px;");
$("#editor_panel").parents("form").removeClass("df")
}
//params.submit_btn.show();
@ -207,12 +207,12 @@ function sd_create_shixun_editor(params) {
// $('#mini_comment_section').height('244px')
},
afterCreate: function () {
afterCreate:function(){
//params.submit_btn.hide();
var toolbar = $("div[class='ke-toolbar']", params.div_form);
toolbar.css('display', 'inline');
toolbar.css('padding', 0);
$(".ke-outline>.ke-toolbar-icon", toolbar).append('表情');
var toolbar = $("div[class='ke-toolbar']",params.div_form);
toolbar.css('display','inline');
toolbar.css('padding',0);
$(".ke-outline>.ke-toolbar-icon",toolbar).append('表情');
params.toolbar_container.append(toolbar);
params.toolbar_container.hide();
params.submit_btn.hide();
@ -221,16 +221,16 @@ function sd_create_shixun_editor(params) {
var body = edit.doc.body;
edit.iframe[0].scroll = 'no';
// body.style.overflowY = 'hidden';
body.style['padding-top'] = '2px';
body.style['padding-left'] = '5px';
body.style['padding-top']= '2px';
body.style['padding-left']= '5px';
// <style type='text/css'>body{padding-top: 2px;padding-left: 5px;}</style>
//reset height
paramsHeight = paramsHeight == undefined ? params.kindutil.removeUnit(this.height) : paramsHeight;
edit.iframe.height(paramsHeight);
edit.html("<span id='hint' style='color:#999999;font-size:14px;'>说点什么</span>");
this.resize(null, paramsHeight);// Math.max((params.kindutil.IE ? body.scrollHeight : body.offsetHeight)+ paramsHeight , paramsHeight)
this.resize(null,paramsHeight);// Math.max((params.kindutil.IE ? body.scrollHeight : body.offsetHeight)+ paramsHeight , paramsHeight)
// params.toolbar_container.hide();
if (typeof enableAt === 'function') {
if(typeof enableAt === 'function'){
enableAt(this, id, type);
}
@ -242,43 +242,43 @@ function sd_create_shixun_editor(params) {
return editor;
}
function sd_check_editor_form_field(params) {
var result = true;
if (params.content != undefined) {
if (params.content.isEmpty()) {
result = false;
function sd_check_editor_form_field(params){
var result=true;
if(params.content!=undefined){
if(params.content.isEmpty()){
result=false;
}
if (params.content.html() != params.textarea.html() || params.issubmit == true) {
if(params.content.html()!=params.textarea.html() || params.issubmit==true){
params.textarea.html(params.content.html());
params.content.sync();
if (params.content.isEmpty() || /^\s*<\w*\s*\w*\=\"\w*\"\s*\w*\=\"\w*\:\s*\#\d*\;\s*\w*\-\w*\:\s*\w*\;\"\>[\u4e00-\u9fa5]*<\/\w*\>\s*$/.test(params.textarea.html())) {
if(params.content.isEmpty() || /^\s*<\w*\s*\w*\=\"\w*\"\s*\w*\=\"\w*\:\s*\#\d*\;\s*\w*\-\w*\:\s*\w*\;\"\>[\u4e00-\u9fa5]*<\/\w*\>\s*$/.test(params.textarea.html())){
params.contentmsg.html('内容不能为空');
params.contentmsg.css({ color: '#ff0000' });
} else {
params.contentmsg.css({color:'#ff0000'});
}else{
params.contentmsg.html('填写正确');
params.contentmsg.css({ color: '#008000' });
params.contentmsg.css({color:'#008000'});
}
params.contentmsg.show();
}
}
return result;
}
function sd_create_form(params) {
params.form.submit(function () {
function sd_create_form(params){
params.form.submit(function(){
var flag = false;
if (params.form.attr('data-remote') != undefined) {
if(params.form.attr('data-remote') != undefined ){
flag = true
}
var is_checked = sd_check_editor_form_field({
issubmit: true,
content: params.editor,
contentmsg: params.contentmsg,
textarea: params.textarea
issubmit:true,
content:params.editor,
contentmsg:params.contentmsg,
textarea:params.textarea
});
if (is_checked) {
if (flag) {
if(is_checked){
if(flag){
return true;
} else {
}else{
$(this)[0].submit();
return false;
}
@ -286,142 +286,144 @@ function sd_create_form(params) {
return false;
});
}
function sd_reset_editor_form(params) {
function sd_reset_editor_form(params){
params.form[0].reset();
params.textarea.empty();
if (params.editor != undefined) {
if(params.editor != undefined){
params.editor.html(params.textarea.html());
}
params.contentmsg.hide();
}
//第二个参数是高度,可以传,可以不传
function sd_create_editor_from_data(id) {
function sd_create_editor_from_data(id){
var height = arguments[1] ? arguments[1] : undefined;
var width = arguments[2] ? arguments[2] : undefined;
var type = arguments[3] ? arguments[3] : undefined;
var K = KindEditor;
$("div[nhname='new_message_" + id + "']").each(function () {
var params = {};
params.kindutil = K;
params.div_form = $(this);
params.form = $("form", params.div_form);
if (params.form == undefined || params.form.length == 0) {
return;
}
params.textarea = $("textarea[nhname='new_message_textarea_" + id + "']", params.div_form);
params.contentmsg = $("span[nhname='contentmsg_" + id + "']", params.div_form);
params.toolbar_container = $("div[nhname='toolbar_container_" + id + "']", params.div_form);
params.cancel_btn = $("#new_message_cancel_btn_" + id);
params.submit_btn = $("#new_message_submit_btn_" + id);
params.height = height;
params.width = width;
if (params.textarea.data('init') == undefined) {
params.editor = sd_create_editor(params, id, type);
sd_create_form(params);
params.cancel_btn.click(function () {
sd_reset_editor_form(params);
});
params.submit_btn.click(function () {
var tContents = $("#comment_news_" + id).val();
if (tContents != undefined) {
var beforeImage = tContents.split("<img");
var afterImage = tContents.split("/>");
if (beforeImage[0] == "" && afterImage[1] == "") {
notice_box('不支持纯图片评论<br/>请在评论中增加文字信息');
return;
}
// KindEditor.ready(function (K) {
// react 环境不需要ready方法页面已经加载完了才执行sd_create_editor_from_data
var K = KindEditor;
$("div[nhname='new_message_" + id + "']").each(function () {
var params = {};
params.kindutil = K;
params.div_form = $(this);
params.form = $("form", params.div_form);
if (params.form == undefined || params.form.length == 0) {
return;
}
params.textarea = $("textarea[nhname='new_message_textarea_" + id + "']", params.div_form);
params.contentmsg = $("span[nhname='contentmsg_" + id + "']", params.div_form);
params.toolbar_container = $("div[nhname='toolbar_container_" + id + "']", params.div_form);
params.cancel_btn = $("#new_message_cancel_btn_" + id);
params.submit_btn = $("#new_message_submit_btn_" + id);
params.height = height;
params.width = width;
if (params.textarea.data('init') == undefined) {
params.editor = sd_create_editor(params,id, type);
sd_create_form(params);
params.cancel_btn.click(function () {
sd_reset_editor_form(params);
});
params.submit_btn.click(function () {
var tContents = $("#comment_news_" + id).val();
if(tContents != undefined){
var beforeImage = tContents.split("<img");
var afterImage = tContents.split("/>");
if(beforeImage[0] == "" && afterImage[1] == ""){
notice_box('不支持纯图片评论<br/>请在评论中增加文字信息');
return;
}
if (tContents.startsWith('<') && tContents.endsWith('>')
&& (tContents.indexOf('<link') != -1 || tContents.indexOf('<script') != -1)) {
notice_box('不支持包含link或script标签的html内容');
return;
if (tContents.startsWith('<') && tContents.endsWith('>')
&& (tContents.indexOf('<link') != -1 || tContents.indexOf('<script') != -1 )) {
notice_box('不支持包含link或script标签的html内容');
return;
}
}
}
// react环境下发消息给react组件
if (window['__isR'] === true) {
$(document).trigger("onReply", { commentContent: tContents, id: id, editor: params.editor });
} else {
params.form.submit();
}
});
params.textarea.focus(function () {
params.editor.focus();
});
params.textarea.data('init', 1);
$(this).show();
// react环境下发消息给react组件
if (window['__isR'] === true) {
$(document).trigger("onReply", { commentContent:tContents, id:id, editor:params.editor } );
} else {
params.form.submit();
}
});
params.textarea.focus(function (){
params.editor.focus();
});
params.textarea.data('init', 1);
$(this).show();
__editor = params.editor
}
});
__editor = params.editor
}
});
// });
div_form = $("div[nhname='new_message_" + id + "']");
$(".ke-edit", div_form).css("height", "33px");
$(".ke-edit-iframe", div_form).css("height", "33px");
$(".ke-edit", div_form).css("height","33px");
$(".ke-edit-iframe",div_form).css("height","33px");
return __editor;
}
//第二个参数是高度,可以传,可以不传
function sd_create_editor_from_shixun_data(id) {
function sd_create_editor_from_shixun_data(id){
var height = arguments[1] ? arguments[1] : undefined;
var width = arguments[2] ? arguments[2] : undefined;
var type = arguments[3] ? arguments[3] : undefined;
// KindEditor.ready(function (K) {
// react 环境不需要ready方法页面已经加载完了才执行sd_create_editor_from_data
var K = KindEditor;
$("div[nhname='new_message_" + id + "']").each(function () {
var params = {};
params.kindutil = K;
params.div_form = $(this);
params.form = $("form", params.div_form);
if (params.form == undefined || params.form.length == 0) {
return;
}
params.textarea = $("textarea[nhname='new_message_textarea_" + id + "']", params.div_form);
params.contentmsg = $("span[nhname='contentmsg_" + id + "']", params.div_form);
params.toolbar_container = $("div[nhname='toolbar_container_" + id + "']", params.div_form);
params.cancel_btn = $("#new_message_cancel_btn_" + id);
params.submit_btn = $("#new_message_submit_btn_" + id);
params.height = height;
params.width = width;
if (params.textarea.data('init') == undefined) {
params.editor = sd_create_shixun_editor(params, id, type);
window._commentInput = params.editor;
sd_create_form(params);
params.cancel_btn.click(function () {
sd_reset_editor_form(params);
});
// 在react组件中hide
// params.submit_btn.click(function () {
// $(this).hide()
// });
// 非react环境才监听这个click
!window['__isR'] && params.submit_btn.click(function () {
var tContents = $("#comment_news_" + id).val();
if (tContents != undefined) {
var beforeImage = tContents.split("<img");
var afterImage = tContents.split("/>");
if (beforeImage[0] == "" && afterImage[1] == "") {
notice_box('不支持纯图片评论<br/>请在评论中增加文字信息');
return;
// react 环境不需要ready方法页面已经加载完了才执行sd_create_editor_from_data
var K = KindEditor;
$("div[nhname='new_message_" + id + "']").each(function () {
var params = {};
params.kindutil = K;
params.div_form = $(this);
params.form = $("form", params.div_form);
if (params.form == undefined || params.form.length == 0) {
return;
}
params.textarea = $("textarea[nhname='new_message_textarea_" + id + "']", params.div_form);
params.contentmsg = $("span[nhname='contentmsg_" + id + "']", params.div_form);
params.toolbar_container = $("div[nhname='toolbar_container_" + id + "']", params.div_form);
params.cancel_btn = $("#new_message_cancel_btn_" + id);
params.submit_btn = $("#new_message_submit_btn_" + id);
params.height = height;
params.width = width;
if (params.textarea.data('init') == undefined) {
params.editor = sd_create_shixun_editor(params,id, type);
window._commentInput = params.editor;
sd_create_form(params);
params.cancel_btn.click(function () {
sd_reset_editor_form(params);
});
// 在react组件中hide
// params.submit_btn.click(function () {
// $(this).hide()
// });
// 非react环境才监听这个click
!window['__isR'] && params.submit_btn.click(function () {
var tContents = $("#comment_news_" + id).val();
if(tContents != undefined){
var beforeImage = tContents.split("<img");
var afterImage = tContents.split("/>");
if(beforeImage[0] == "" && afterImage[1] == ""){
notice_box('不支持纯图片评论<br/>请在评论中增加文字信息');
return;
}
}
}
params.form.submit();
});
params.textarea.focus(function () {
params.editor.focus();
});
params.textarea.data('init', 1);
$(this).show();
}
});
params.form.submit();
});
params.textarea.focus(function (){
params.editor.focus();
});
params.textarea.data('init', 1);
$(this).show();
}
});
// });
div_form = $("div[nhname='new_message_" + id + "']");
$(".ke-edit", div_form).css("height", "33px");
$(".ke-edit-iframe", div_form).css("height", "33px");
$(".ke-edit", div_form).css("height","33px");
$(".ke-edit-iframe",div_form).css("height","33px");
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -13,7 +13,7 @@
*/
function openTerminal(options) {
// 为了多个实例能同时存在
(function () {
(function () {
var heartBeatInterval;
var force_close_socket = false;
//var CONNECT_TIME = 0; // 请求连接次数
@ -21,21 +21,20 @@ function openTerminal(options) {
var parentDomId = options.parentDomId || ''
var client = new WSSHClient();
var base64 = new Base64();
var term = new Terminal({
cols: options.columns, rows: Rows, screenKeys: true, useStyle: true
// TODO 默认是canvas可能被其他样式影响了 canvas用不了
var term = new Terminal({cols: options.columns, rows: Rows, screenKeys: true, useStyle: true
// TODO 默认是canvas可能被其他样式影响了 canvas用不了
, rendererType: 'dom'
, fontSize: 16
});
});
term.on('data', function (data) {
console.log("xterm data: ");
console.log(data);
client.sendClientData(data);
window.parent.postMessage({ tp: 'sshWorking' }, "*");
window.parent.postMessage({tp: 'sshWorking'}, "*");
});
term.open();
$('body>.terminal').detach().appendTo(parentDomId + ' #term');
$('body>.terminal').detach().appendTo( parentDomId + ' #term' );
$(parentDomId + " #term").show();
term.write("Connecting...");
console.log(options)
@ -44,7 +43,7 @@ function openTerminal(options) {
//var interTime = setInterval(client_connect, 1000)
setTimeout(client_connect, 3000);
heartBeatInterval = setInterval(function () {
heartBeatInterval = setInterval(function(){
client.sendHeartBeat()
}, 30 * 1000)
/**
@ -68,13 +67,13 @@ function openTerminal(options) {
window.addEventListener("message", function (event) {
console.log("post message: ");
console.log(event.data);
if (event.data.tp === 'resize') {
if(event.data.tp === 'resize'){
resizeTerminal(event.data);
} else if (event.data.tp === 'reload') {
window.location.reload()
} else if (event.data.tp === 'close_ssh_cocket') {
force_close_socket = true; // 强制关闭socket用于不开启自动重连
client && client.close();
client && client.close();
}
}, false);
@ -95,9 +94,10 @@ function openTerminal(options) {
term.focus();
},
onClose: function () {
debugger;
clearInterval(heartBeatInterval);
console.log("连接关闭");
term.write("\r\nconnection closed");
if (CONNECTED) {
@ -106,7 +106,7 @@ function openTerminal(options) {
}
if (force_close_socket === false) {
// $(window).trigger('setSSHConnectStatus');
window.parent.postMessage({ tp: 'setSSHConnectStatus', tab: options.tab }, "*");
window.parent.postMessage({tp: 'setSSHConnectStatus', tab: options.tab}, "*");
} else {
// 主动关闭连接时,不自动重连
force_close_socket = false;
@ -116,10 +116,10 @@ function openTerminal(options) {
if (!CONNECTED) {
console.log("first connected.");
// 问题重现的实训 带代码tab的 命令行实训 https://www.educoder.net/tasks/83hflni9es7tl
setTimeout(function () {
setTimeout(function() {
// TODO canvas模式下没有body
if (term && term.body && term.body.innerText
&& term.body.innerText.indexOf('Connecting') != -1) {
if ( term && term.body && term.body.innerText
&& term.body.innerText.indexOf('Connecting') != -1 ) {
term.clear(); // 有的连上后还出现了“Connecting。。。”
}
}, 1000)
@ -179,10 +179,10 @@ function connect() {
username: $("#username").val(),
secret: $("#password").val(),
gameid: $("#gameid").val(),
rows: parseInt($("#terminalRow").val()),
columns: parseInt($("#terminalColumn").val()),
width: parseInt($("#terminalWidth").val()),
height: parseInt($("#terminalHeight").val()),
rows: parseInt( $("#terminalRow").val() ),
columns: parseInt( $("#terminalColumn").val() ),
width: parseInt( $("#terminalWidth").val() ),
height: parseInt( $("#terminalHeight").val() ),
tab: $("#terminalTab").val(),
}
if (remember) {

File diff suppressed because one or more lines are too long

View File

@ -2,6 +2,7 @@
// 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
@ -45,10 +46,10 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
function removeExceptGitDir(dir) {
// readdirSync
const list = fs2.readdirSync(dir)
// if (err) return done(err);
// if (err) return done(err);
var pending = list.length;
// if (!pending) return done(null, results);
list.forEach(function (file) {
list.forEach(function(file) {
if (file.indexOf('.git') == -1) {
file = path.resolve(dir, file);
fs.removeSync(file)
@ -79,13 +80,13 @@ measureFileSizesBeforeBuild(paths.appBuild)
console.log(warnings.join('\n\n'));
console.log(
'\nSearch for the ' +
chalk.underline(chalk.yellow('keywords')) +
' to learn more about each warning.'
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'
chalk.cyan('// eslint-disable-next-line') +
' to the line before.\n'
);
} else {
console.log(chalk.green('Compiled successfully.\n'));
@ -148,12 +149,14 @@ function build(previousFileSizes) {
console.log(
chalk.yellow(
'\nTreating warnings as errors because process.env.CI = true.\n' +
'Most CI servers set it automatically.\n'
'Most CI servers set it automatically.\n'
)
);
return reject(new Error(messages.warnings.join('\n\n')));
}
generateNewIndexJsp();
return resolve({
stats,
previousFileSizes,
@ -167,5 +170,82 @@ 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);
}

View File

@ -1,31 +1,31 @@
var fs = require('fs');
var uglify = require("uglify-js");
var path = require('path');
var concat = require('concat')
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);
}
});
});
});
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
@ -34,51 +34,65 @@ var cssDir = './public/css'
// true &&
false &&
walk(cssDir, function () {
console.log('results', results.length, results)
})
walk(cssDir, function() {
console.log('results', results.length, results)
})
// return;
// ----------------------------------------------------------------------------- CSS
var cssResults = [
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\\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')
'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 = [
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\\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\\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');
// }
// });
'D:\\Code\\trustieplus\\public\\react\\public\\js\\merge.js',
// var uglified = uglify.minify(['file1.js', 'file2.js', 'file3.js']);
'D:\\Code\\trustieplus\\public\\react\\public\\js\\edu_tpi.js',
]
concat(_results, './public/js/js_min_all.js')

View File

@ -1,21 +0,0 @@
const { PurgeCSS } = require('purgecss')
var fs = require('fs');
async function purgeBuild() {
const purgeCSSResult = await new PurgeCSS().purge({
content: ['../src/**/*.js', '../src/**/*.jsx',],
css: ['../public/css/edu-public.css', '../public/css/taskstyle.css', '../public/css/edu-main.css', '../public/css/edu-all.css']
})
let result = []
for (let i = 0; i < purgeCSSResult.length; i++) {
let item = purgeCSSResult[i]
result.push(item.css)
}
fs.writeFile('../public/css/edu-purge.css', result.join(''), 'utf8', function (err) {
if (err) return console.log(err)
})
}
purgeBuild()

View File

@ -36,7 +36,7 @@ const useYarn = fs.existsSync(paths.yarnLockFile);
const isInteractive = process.stdout.isTTY;
const portSetting = require(paths.appPackageJson).port
if (portSetting) {
if ( portSetting ) {
process.env.port = portSetting
}
@ -76,9 +76,10 @@ choosePort(HOST, DEFAULT_PORT)
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 });
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(
@ -98,8 +99,8 @@ choosePort(HOST, DEFAULT_PORT)
openBrowser(urls.localUrlForBrowser);
});
['SIGINT', 'SIGTERM'].forEach(function (sig) {
process.on(sig, function () {
['SIGINT', 'SIGTERM'].forEach(function(sig) {
process.on(sig, function() {
devServer.close();
process.exit();
});

View File

@ -23,13 +23,8 @@
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@ -40,85 +35,63 @@
.editormd .CodeMirror-linenumbers {
padding: 0;
}
.editormd-html-preview hr,
.editormd-preview-container hr {
.editormd-html-preview hr, .editormd-preview-container hr {
/* 颜色加深 */
border-top: 1px solid #ccc;
}
/* 重置掉antd的一些样式 */
html,
body {
html, body {
-webkit-font-smoothing: auto !important;
}
.ant-progress-textyes {
color: #52c41a;
}
.ant-progress-textno {
.ant-progress-textno{
color: #f5222d;
}
.CodeMirror pre.CodeMirror-line{
font-size: 16px!important;
}
/* md多空格 */
.markdown-body p {
margin:10px 0px!important;
font-size: 16px !important;
white-space: pre-wrap;
font-size: 16px!important
}
.markdown-body>p {
.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;
padding:8px 8px 50px;
}
.editormd-preview .markdown-body{
padding:0px !important;
}
/* 图片点击放大的场景,隐藏图片链接 */
.editormd-image-click-expand .editormd-image-dialog {
height: 234px !important;
}
.editormd-image-click-expand .editormd-image-dialog .image-link {
display: none;
}
.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;
.CodeMirror .CodeMirror-lines pre.CodeMirror-line, .CodeMirror .CodeMirror-lines pre.CodeMirror-line-like {
padding: 0 12px ;
}
/* antd扩展 */
.formItemInline.ant-form-item {
.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;

File diff suppressed because it is too large Load Diff

View File

@ -1,141 +1,420 @@
import React from "react";
import axios from 'axios';
import md5 from 'md5';
import { requestProxy } from "./indexEduplus2RequestProxy";
import { broadcastChannelOnmessage, isDev, queryString } from 'educoder';
import { notification } from 'antd';
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;
let message501 = false;
broadcastChannelOnmessage('refreshPage', () => {
window.location.reload();
window.location.reload()
})
function locationurl(list) {
if (window.location.port !== "3007") {
window.location.href = list
function locationurl(list){
if (window.location.port === "3007") {
} else {
window.location.href=list
}
}
// TODO 开发期多个身份切换
let debugType = ""
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 = 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();
initOnlineOfflineListener()
var proxy = "https://testforgeplus.trustie.net";
//响应前的设置
axios.interceptors.request.use(
config => {
if(config.url.indexOf("http") !== -1) {
return config
}
requestProxy(config);
let url = `/api${config.url}`;
// TODO 避免重复的请求 https://github.com/axios/axios#cancellation
// https://github.com/axios/axios/issues/1497
if (`${config[0]}` !== `true`) {
if (window.location.port === "3007") {
config.url = `${proxy}${url}`;
if (config.url.indexOf('?') === -1) {
config.url = `${config.url}?debug=${debugType}`;
} else {
config.url = `${config.url}&debug=${debugType}`;
}
} else {
config.url = url;
}
}
return config;
},
err => {
return Promise.reject(err);
});
// 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"
axios.interceptors.response.use(function (response) {
if (response === undefined) {
return
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;
}
const config = response.config;
if (response.data.status === -1) {
if (window.location.pathname.startsWith('/tasks/')) {
props.showSnackbar(response.data.message || '服务器异常,请联系管理员。')
} else {
notification.open({
message: "提示",
description: response.data.message || '服务器异常,请联系管理员。',
style: {
zIndex: 99999999
},
//响应前的设置
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);
});
}
throw new axios.Cancel('Operation canceled by the user.');
}
if (response.data.status === 403 || response.data.status === "403") {
locationurl('/403');
}
axios.interceptors.response.use(function (response) {
if (response.data.status === 404) {
let responseURL = response.request ? response.request.responseURL:'';
if (responseURL.indexOf('/api/users/') === -1 && responseURL.indexOf('/api/organizations/') === -1 ) {
locationurl('/nopage');
}
}
// console.log(".............")
if(response===undefined){
return
}
const config = response.config
if (response.data.status === -1) {
// console.error('error:', response.data.message)
// throw new Error()
if (response.data.status === 500) {
locationurl('/500');
}
// 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 || '服务器异常,请联系管理员。',
// });
}
if (response.data.status === 501) {
if (message501 === false) {
message501 = true
notification.open({
message: "提示",
description: response.data.message || '访问异常,请求不合理',
style: {
zIndex: 99999999
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);
}
return response;
}, function (error) {
return Promise.reject(error);
});
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()
const $ = window.$
$(window).bind("online", () => {
notification.destroy()
notification.success({
duration: 2,
message: '网络恢复正常',
description:
'网络恢复正常,感谢使用。',
})
});
$(window).bind("offline", () => {
notification.destroy()
notification.warning({
duration: null,
message: '网络异常',
description:
'网络异常,请检测网络后重试。',
})
});
notification.warning({
duration: null,
message: '网络异常',
description:
'网络异常,请检测网络后重试。',
})
});
}

View File

@ -1,16 +1,34 @@
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)
}
}
render() {
return ""
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;

View File

@ -1,9 +0,0 @@
import React from 'react'
import MiniPagination from './components/mini-pagination'
export default () => {
function onPageChange(page) {
console.log(page, '-----------')
}
return <MiniPagination onChange={onPageChange} current={1} total={100} pageSize={16} />
}

View File

@ -29,7 +29,7 @@ class College extends Component {
align: 'center',
className: "edu-txt-center font-14 maxnamewidth247",
render: (text, record) => (
<a href={`/classrooms/${record.id}/students`} title={record.name} target="_blank" className="d-inline-block text-truncate maxnamewidth247"
<a href={`/courses/${record.id}/students`} title={record.name} target="_blank" className="d-inline-block text-truncate maxnamewidth247"
style={{
maxWidth:'220px',
color:'#007bff',
@ -195,7 +195,7 @@ class College extends Component {
align: 'center',
className: "edu-txt-center font-14 maxnamewidth105",
render: (text, record) => (
<a href={`/${record.login}`} title={record.name} target="_blank" className="task-hide maxnamewidth105" style={{
<a href={`/users/${record.login}`} title={record.name} target="_blank" className="task-hide maxnamewidth105" style={{
color:'#007bff',
}}> {
@ -895,7 +895,7 @@ class College extends Component {
共建实训
</p>
<p className="yslstatistic-base-item-label">
报告
报告
</p>
<p className="yslstatistic-base-item-label">
学员实战时间
@ -907,7 +907,7 @@ class College extends Component {
<div className="jibenshiyong100 sortinxdirection yinyin">
<div className="yslstatistic-base-item-labels">
{
teachers_count || teachers_count===0?
teachers_count?
<div className="yslstatistic-base-item-labelsp"> {teachers_count}<span className="yslstatistic-base-item-labelsspan"></span></div>
:
<Spin/>
@ -915,7 +915,7 @@ class College extends Component {
</div>
<div className="yslstatistic-base-item-labels">
{
students_count || students_count===0?
students_count?
<div className="yslstatistic-base-item-labelsp">{students_count}<span className="yslstatistic-base-item-labelsspan"></span></div>
:
<Spin/>
@ -923,7 +923,7 @@ class College extends Component {
</div>
<div className="yslstatistic-base-item-labels">
{
courses_count || courses_count === 0?
courses_count?
<div className="yslstatistic-base-item-labelsp">{courses_count}<span className="yslstatistic-base-item-labelsspan"></span></div>
:
<Spin/>
@ -931,7 +931,7 @@ class College extends Component {
</div>
<div className="yslstatistic-base-item-labels">
{
shixuns_count || shixuns_count === 0?
shixuns_count?
<div className="yslstatistic-base-item-labelsp">{shixuns_count}<span className="yslstatistic-base-item-labelsspan"></span></div>
:
<Spin/>
@ -939,7 +939,7 @@ class College extends Component {
</div>
<div className="yslstatistic-base-item-labels">
{
shixun_report_count || shixun_report_count===0?
shixun_report_count?
<div className="yslstatistic-base-item-labelsp">{shixun_report_count}<span className="yslstatistic-base-item-labelsspan"></span></div>
:
<Spin/>
@ -947,8 +947,8 @@ class College extends Component {
</div>
<div className="yslstatistic-base-item-labels">
{
shixun_time || shixun_time === 0 ?
<div className="yslstatistic-base-item-labelsp">{shixun_time}<span className="yslstatistic-base-item-labelsspan">分钟</span></div>
shixun_time?
<div className="yslstatistic-base-item-labelsp">{shixun_time}<span className="yslstatistic-base-item-labelsspan"></span></div>
:
<Spin/>
}

View File

@ -1,129 +1,112 @@
.yslstatistic-header {
width: 100%;
height: 240px;
background-image: url('/images/educoder/statistics.jpg');
background-size: 100% 100%;
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;
}
.yslborder {
border: 1px solid;
.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-title {
flex: 1;
display: flex;
align-items: center;
color: #4CACFF;
font-size: 32px;
.yslstatistic-header-item-content{
font-size: 24px;
}
.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;
.intermediatecenter{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
/* 简单居中 */
.intermediatecenterysls {
display: flex;
align-items: center;
.intermediatecenterysls{
display: flex;
align-items: center;
}
.spacearound {
display: flex;
justify-content: space-around;
.spacearound{
display: flex;
justify-content: space-around;
}
.spacebetween {
display: flex;
justify-content: space-between;
.spacebetween{
display: flex;
justify-content: space-between;
}
/* 头顶部居中 */
.topcenter {
display: -webkit-flex;
flex-direction: column;
align-items: center;
.topcenter{
display: -webkit-flex;
flex-direction: column;
align-items: center;
}
/* x轴正方向排序 */
/* 一 二 三 四 五 六 七 八 */
.sortinxdirection {
display: flex;
flex-direction: row;
align-items: center;
.sortinxdirection{
display: flex;
flex-direction:row;
}
/* x轴反方向排序 */
/* 八 七 六 五 四 三 二 一 */
.xaxisreverseorder {
display: flex;
flex-direction: row-reverse;
.xaxisreverseorder{
display: flex;
flex-direction:row-reverse;
}
/* 垂直布局 正方向*/
/*
@ -133,118 +116,98 @@
*/
.verticallayout {
display: flex;
flex-direction: column;
.verticallayout{
display: flex;
flex-direction:column;
}
/* 垂直布局 反方向*/
.reversedirection {
display: flex;
flex-direction: column-reverse;
.reversedirection{
display: flex;
flex-direction:column-reverse;
}
.h4 {
font-size: 1.5rem;
font-weight: 500 !important;
.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;
}
.ysllinjibenshiyong {
font-weight: 500;
line-height: 1.2;
padding: 2rem 1.25rem;
border-bottom: unset;
background: #fff;
.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;
}
.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;
.color-huang{
color:#ffc107 !important
}
.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;
.maxnamewidth105{
max-width: 105px;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
cursor: default;
}
.yinyin {
background: #fff;
box-shadow: 0px 6px 12px 0px rgba(0, 0, 0, 0.1);
border-radius: 2px;
.maxnamewidth247{
max-width: 247px;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
cursor: default;
}
.edu-back-eeee {
background: #EEEEEE !important;
.maxnamewidth340{
max-width: 340px;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
cursor: default;
}
.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: #2A61FF;
}
.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;
}

View File

@ -1,5 +1,4 @@
import moment from "moment";
import { number } from "prop-types";
// 处理整点 半点
// 取传入时间往后的第一个半点
@ -39,36 +38,6 @@ export function getNextHalfHourOfMoment(moment) {
return moment
}
 export function formatSeconds(value) {
        var theTime = parseInt(value);// 秒
        var middle= 0;// 分
        var hour= 0;// 小时
    
        if(theTime > 60) {
            middle= parseInt(theTime/60);
            theTime = parseInt(theTime%60);
            if(middle> 60) {
                hour= parseInt(middle/60);
                middle= parseInt(middle%60);
            }
        }
        var result = ""+parseInt(theTime)+"秒";
        if(middle > 0) {
if(hour>0){
result = ""+parseInt(middle)+"分";
}else{
result = ""+parseInt(middle)+"分"+result;
}
            
        }
        if(hour> 0) {
            result = ""+parseInt(hour)+"小时"+result;
        }
        return result;
    }
export function formatDuring(mss){
var days = parseInt(mss / (1000 * 60 * 60 * 24));
var hours = parseInt((mss % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
@ -97,42 +66,4 @@ export function formatDuring(mss){
}
return days + "天" + hours + "小时" + minutes + "分";
}
/*
返回多久以前
backDate以前的某个日期
*/
export function timeAgo(backDate) {
try {
moment(backDate);
} catch (e) {
return '刚刚';
}
if(typeof backDate ==='number'){
backDate=backDate*1000
}else{
backDate= moment(backDate);
}
let time = new Date() - backDate;
var days = Math.floor(time / (1000 * 60 * 60 * 24));
var hours = Math.floor((time % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
var minutes = Math.floor((time % (1000 * 60 * 60)) / (1000 * 60));
var seconds = Math.floor((time % (1000 * 60 * 60)) / 1000);
if (time <= 0) {
return "刚刚";
}
if (days) {
return days + "天前";
}
if (hours) {
return hours + "小时前";
}
if (minutes) {
return minutes + "分前";
}
if (seconds) {
return seconds + "秒前";
}
return "刚刚";
}

View File

@ -21,9 +21,9 @@ class LoadingSpin extends Component{
}
`}
</style>
<Spin/>
<Spin tip="正在获取相关数据..."/>
</div>
)
}
}
export default LoadingSpin;
export default LoadingSpin;

View File

@ -17,14 +17,14 @@ const timeStamp = () => {
/*
带trace的默认折叠起来的控制台输出
第一个参数最好传入string类型的标识接着可以跟任意类型任意个数的参数各个参数都会打印到控制台
*/
*/
export function trace_collapse(content) {
if (console.groupCollapsed) {
// console.groupCollapsed(typeof content == 'string' ? content : 'trace_collapse');
// log.trace(arguments);
// console.groupEnd();
console.groupCollapsed(typeof content == 'string' ? content : 'trace_collapse');
log.trace(arguments);
console.groupEnd();
} else {
// trace(content)
trace(content)
}
}

View File

@ -2,23 +2,19 @@ import React, { Component } from 'react';
import Snackbar from 'material-ui/Snackbar';
import Fade from 'material-ui/transitions/Fade';
import { notification } from 'antd'
const snackbarProp = { 'aria-describedby': 'message-id' }
export function SnackbarHOC(options = {}) {
return function wrap(WrappedComponent) {
return class Wrapper extends Component {
constructor(props) {
super(props);
this.showSnackbar = this.showSnackbar.bind(this)
this.handleSnackbarClose = this.handleSnackbarClose.bind(this)
this.state = {
snackbarText: '',
snackbarOpen: false,
}
}
return class Wrapper extends Component {
constructor(props) {
super(props);
this.showSnackbar = this.showSnackbar.bind(this)
this.state = {
snackbarText: '',
snackbarOpen: false,
}
}
handleSnackbarClose() {
handleSnackbarClose() {
this.setState({
snackbarOpen: false,
snackbarVertical: '',
@ -26,50 +22,70 @@ export function SnackbarHOC(options = {}) {
})
}
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() {
// 全局的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);
// }
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={snackbarProp}
resumeHideDuration={2000}
message={<span id="message-id">{this.state.snackbarText}</span>}
/>
<WrappedComponent {...this.props} showSnackbar={this.showSnackbar} showNotification={this.showNotification} >
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;
</WrappedComponent>
</React.Fragment>
)
}
}
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>
)
}
}
}
}

View File

@ -1,167 +1,79 @@
import { bytesToSize, getUrl, getUrl2 } from 'educoder'
import showdown from 'showdown'
const $ = window.$
export function isImageExtension(fileName) {
return fileName ? !!(fileName.match(/.(jpg|jpeg|png|gif)$/i)) : false
}
export function markdownToHTML(oldContent, selector) {
try {
window.$('#md_div').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
}
} catch (e) {
}
}
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",]
export function validateLength(str = '', length = 100) {
let len = 0
if (str) {
len = str.length
for (let i = 0; i < len; i++) {
let charCode = str.charCodeAt(i)
if (charCode >= 0xD800 && charCode <= 0xDBFF) {
len--
i++
}
}
}
return len <= length
}
export function mdJSONParse(str) {
let val = "";
try {
return val = JSON.parse(str)
} catch (error) {
if (str) {
return val = str;
} else {
return "";
}
}
}
showdown.extension('prettify', function () {
return [{
type: 'output',
filter: function (source) {
return source.replace(/(<pre[^>]*>)?[\n\s]?<code([^>]*)>/gi, function (match, pre, codeClass) {
if (pre) {
return '<pre class="prettyprint linenums"><code' + codeClass + '>'
} else {
return ' <code class="prettyprint">'
}
})
}
}]
})
const showdownKatex = require('showdown-katex')
const CONVERTER = new showdown.Converter({
tables: true,
simpleLineBreaks: false,
underline: true,
extensions: [
'prettify',
showdownKatex({
throwOnError: false,
displayMode: false,
errorColor: '#1500ff',
}),
]
})
const oldKatexMode = /(?:`?\$\$)([^`\$]+)(?:\$\$`?)/g
export function exportMdtoHtml(md = '') {
if (typeof md === 'string') {
let rs = md.replace(oldKatexMode, (_, p1) => {
return "$$" + p1 + "$$"
})
return CONVERTER.makeHtml(rs)
}
return md + ''
}
function escapeText(text) {
return text.replace(/[&<>"']/g, s => {
const entityMap = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '',
"'": '',
};
return entityMap[s]
})
}
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", ]

View File

@ -5,112 +5,39 @@ const { Search } = Input;
const $ = window.$;
const isDev = window.location.port == 3007;
const isdev2= window.location.hostname ==='www.educoder.net'
export const TEST_HOST = "https://testforgeplus.trustie.net/"
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://testforgeplus.trustie.net';
const local = 'https://test-newweb.educoder.net'
if (isDev) {
return `${local}/${path}`
}
return `${path}`;
return `/${path}`;
}
export function numFormat(num, digits){
let d = digits || 1;
var si = [
{ value: 1, symbol: "" },
{ value: 1E3, symbol: "k" },
{ value: 1E4, symbol: "W" }
];
var rx = /\.0+$|(\.[0-9]*[1-9])0+$/;
var i;
for (i = si.length - 1; i > 0; i--) {
if (num >= si[i].value) {
break;
}
}
return (num / si[i].value).toFixed(d).replace(rx, "$1") + si[i].symbol;
}
export function getImage(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
const local = 'https://testforgeplus.trustie.net/';
if(path.indexOf("http://")===-1){
if (isDev) {
return `${local}/images/${path}`
}
return `/${path}`;
}else{
return path;
}
}
export function getcdnImageUrl(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
const testlocal = 'https://testali-cdn.educoder.net'
const local='https://ali-cdn.educoder.net'
let firstStr=path.substr(0,1);
if (isdev2) {
return `${local}/${path}`;
}else{
if(firstStr=="/"){
return `${path}`
}else{
return `/${path}`
}
}
// return `${local}/${path}`;
}
export function setcndImagesUrl(path){
const testlocal = 'https://testali-cdn.educoder.net'
const local='https://ali-cdn.educoder.net'
let firstStr=path.substr(0,1);
if(firstStr=="/"){
if (!isdev2) {
return `${path}`
}
return `${local}${path}`;
//return isDev?`${testlocal}${path}`:`${local}${path}`;
}else{
if (!isdev2) {
return `/${path}`
}
return `${local}/${path}`;
// return isDev?`${testlocal}/${path}`:`${local}/${path}`;
}
}
export function setImagesUrl(path){
const testlocal = 'https://testali-cdn.educoder.net'
const local='https://ali-cdn.educoder.net'
const local = 'https://test-newweb.educoder.net'
let firstStr=path.substr(0,1);
// console.log(firstStr);
if(firstStr=="/"){
if (!isdev2) {
return `${path}`
}
return `${local}${path}`;
// return isDev?`${testlocal}${path}`:`${path}`;
return isDev?`${local}${path}`:`${path}`;
}else{
if (!isdev2) {
return `/${path}`
}
return `${local}/${path}`;
//return isDev?`${testlocal}/${path}`:`/${path}`;
return isDev?`${local}/${path}`:`/${path}`;
}
}
export function getUrl(path, goTest) {
const local = 'https://testforgeplus.trustie.net'
// 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:''}`
}
@ -170,7 +97,7 @@ function railsgettimess(proxy) {
export function Railsgettimes() {
// railsgettimess(`${getUrl()}/api/main/first_stamp.json`);
railsgettimess(`${getUrl()}/api/main/first_stamp.json`);
// railsgettimess(`https://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp`);
}
export function getmyUrl(geturl) {
@ -179,31 +106,31 @@ export function getmyUrl(geturl) {
}
export function getUploadActionUrl(path, goTest) {
return `${getUrl()}/api/attachments.json${isDev ?`${isDev ?`?debug=${window._debugType || 'admin'}` : ""}` : ""}`;
}
export function getUploadLogoActionUrl() {
return `${getUrl()}/api/resumes/logo.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`;
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) {
return `${getUrlmys()}/api/shixuns/${id}/upload_data_sets.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`
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() {
return `${getUrlmys()}/api/jupyters/import_with_tpm.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`
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 getupload_git_file(id) {
return `${getUrlmys()}/api/shixuns/${id}/upload_git_file.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`
}
export function getUploadActionUrlOfAuth(id) {
return `${getUrl()}/api/users/accounts/${id}/auth_attachment.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`
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}`
}
@ -222,7 +149,15 @@ export function getTaskUrlById(id) {
}
export function getRandomcode(url) {
return 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) {

View File

@ -1,11 +1,52 @@
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
@ -13,50 +54,64 @@ let _url_origin = getUrl2()
imageSrc 源图片src
width 数字格式
height 数字格式
*/
*/
class Cropper extends Component {
state = {
};
componentDidMount() {
this.options = {
aspectRatio: 1,
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)
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 (
@ -64,8 +119,8 @@ class Cropper extends Component {
{/* This rule is very important, please do not ignore this! */}
<style>{`
.wrapper {
width: ${ width ? (width + 'px') : '500px'};
height: ${ height ? (height + 'px') : '500px'};
width: ${ width ? (width+'px') : '500px'};
height: ${ height ? (height+'px') : '500px'};
border: 1px solid #eee;
}
img {
@ -79,13 +134,155 @@ class Cropper extends Component {
}
`}</style>
<div className="wrapper">
<img id={this.props.imageId || "__image"} src={`${imageSrc}`}></img>
{/* http://localhost:3007/images/footNavLogo.png 图片转了后不对
|| "/images/testPicture.jpg"
|| "/images/shixun0.jpg"
*/}
<img id={this.props.imageId || "__image"} src={`${imageSrc }`}></img>
</div>
{!previewId && <div id="img-preview" className="img-preview preview-lg" style={{ width: '128px', height: '128px', }}>
{/* 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;
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);
// }
// });
// }
// }

View File

@ -0,0 +1,116 @@
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 );

View File

@ -6,23 +6,23 @@ 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()
}
}
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 (
return(
<a {...this.props} onClick={this.checkAuth}>{this.props.children}</a>
)
}

View File

@ -1,7 +1,7 @@
/*
* @Description: 评论表单
* @Author: tangjiang
* @Github:
* @Github:
* @Date: 2019-12-17 17:32:55
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-06 18:42:09
@ -10,13 +10,15 @@ 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) {
function CommentForm (props) {
const {
onCancel,
onSubmit,
onSubmit,
form,
type
} = props;
@ -26,6 +28,9 @@ function CommentForm(props) {
const [focus, setFocus] = useState(false);
const options = [
// ['bold', 'italic', 'underline'],
// [{header: [1,2,3,false]}],
'code-block',
'link',
'image',
'formula'
@ -42,30 +47,20 @@ function CommentForm(props) {
setShowQuill(false);
setCtx('');
props.form.resetFields();
onCancel && onCancel();
onCancel && onCancel();
}
// 编辑器内容变化时
const handleContentChange=(quill,html,mydata)=>{
const reg = /^[\s\S]*.*[^\s][\s\S]*$/;
if (!reg.test(html)) {
setCtx(html);
try {
// const _html = new QuillDeltaToHtmlConverter(content.ops, {}).convert();
props.form.setFieldsValue({ 'comment': html });
} catch (error) {
console.log(error);
}
}else{
setCtx(html);
try {
// const _html = new QuillDeltaToHtmlConverter(content.ops, {}).convert();
props.form.setFieldsValue({ 'comment': html });
} catch (error) {
console.log(error);
}
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) => {
@ -74,7 +69,7 @@ function CommentForm(props) {
if (!err) {
setShowQuill(false);
const content = ctx;
props.form.setFieldsValue({ 'comment': '' });
props.form.setFieldsValue({'comment': ''});
setCtx('');
// const _html = formatDelta(content.ops);
// console.log('保存的内容=====》》》》', content);
@ -100,10 +95,10 @@ function CommentForm(props) {
{
getFieldDecorator('comment', {
rules: [
{ required: true, message: '评论内容不能为空' }
{ required: true, message: '评论内容不能为空'}
],
})(
<Input
<Input
onClick={() => handleInputClick(type)}
placeholder="说点儿什么~"
className={showQuill ? '' : 'show_input'}
@ -115,13 +110,13 @@ function CommentForm(props) {
/>
)
}
<QuillForEditor
imgAttrs={{ width: '60px', height: '30px' }}
imgAttrs={{width: '60px', height: '30px'}}
wrapStyle={{
height: showQuill ? 'auto' : '0px',
opacity: showQuill ? 1 : 0,
overflow: showQuill ? 'none' : 'none',
overflow: showQuill ? 'none' : 'hidden',
transition: 'all 0.3s'
}}
autoFocus={focus}
@ -135,7 +130,7 @@ function CommentForm(props) {
</FormItem>
<FormItem style={{ textAlign: 'right', display: showQuill ? 'block' : 'none' }}>
<Button onClick={handleCancle}>取消</Button>
<Button onClick={handleSubmit} type="primary" style={{ marginLeft: '10px' }}>发送</Button>
<Button onClick={handleSubmit} type="primary" style={{ marginLeft: '10px'}}>发送</Button>
</FormItem>
</Form>
);

View File

@ -1,21 +1,24 @@
/*
* @Description: 评论单列
* @Author: tangjiang
* @Github:
* @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 {markdownToHTML} from 'educoder'
// import QuillForEditor from '../../quillForEditor';
import QuillForEditor from '../../quillForEditor';
function CommentItem({
function CommentItem ({
isAdmin,
options,
confirm,
@ -36,7 +39,7 @@ function CommentItem({
// 上传图片的ulr
const [url, setUrl] = useState('');
const {
const {
author = {}, // 作者
id, // 评论id
content, // 回复内容
@ -54,21 +57,22 @@ function CommentItem({
confirm({
title: '提示',
content: ('确定要删除该条回复吗?'),
onOk() {
onOk () {
console.log('点击了删除', id);
submitDeleteComment && submitDeleteComment(id);
}
});
}
// 评论头像
const commentAvatar = (author) => (
<img
className="item-flex flex-image"
src={author.image_url ? getImageUrl(`/${author.image_url}`) : 'https://b-ssl.duitang.com/uploads/item/201511/13/20151113110434_kyReJ.jpeg'}
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';
@ -80,10 +84,11 @@ function CommentItem({
<span className="iconfont icon-shanchu icon_font_size_14" onClick={() => deleteComment(id)}></span>
</span>
</div>
);
);
};
const handleShowUploadImage = (url) => {
// console.log('==============>>>>>>>>>>>>',url);
setUrl(url);
}
// 评论内容
@ -94,15 +99,13 @@ function CommentItem({
} catch (e) {
_ctx = ctx;
}
// <QuillForEditor
// readOnly={true}
// value={_ctx}
// showUploadImage={handleShowUploadImage}
// />
return (
<div className={"mt15 ml15"} style={{wordBreak: "break-word",fontWeight:"bold"}} dangerouslySetInnerHTML={{__html: markdownToHTML(_ctx).replace(/▁/g, "▁▁▁")}}></div>
)
};
<QuillForEditor
readOnly={true}
value={_ctx}
showUploadImage={handleShowUploadImage}
/>
)};
// 加载更多
const handleOnLoadMore = (len) => {
@ -151,7 +154,7 @@ function CommentItem({
<li className={_moreClass} onClick={() => handleOnLoadMore(len)}>
<p className="loadmore-txt">展开其余{lastTxt}条评论</p>
<p className="loadmore-icon">
<Icon type={!arrow ? 'down' : 'up'} />
<Icon type={!arrow ? 'down' : 'up'}/>
</p>
</li>
</ul>
@ -166,7 +169,7 @@ function CommentItem({
const handleClickLick = (id) => {
likeComment && likeComment(id);
}
// 点击评论icon
const handleClickMessage = () => {
setShowQuill(true);
@ -195,33 +198,33 @@ function CommentItem({
<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'}
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'}
style={{ display: can_delete ? 'inline-block' : 'none'}}
className='comment-icon-margin'
type={'shanchu'}
iconClick={() => deleteComment(id)}
/>
{/* 回复 */}
<CommentIcon
className='comment-icon-margin'
<CommentIcon
className='comment-icon-margin'
type="huifu1"
count={children.length}
count={children.length}
iconClick={handleClickMessage}
/>
{/* 点赞 */}
<CommentIcon
iconColor={user_praise ? '#5091FF' : ''}
<CommentIcon
iconColor={ user_praise ? '#5091FF' : '' }
className='comment-icon-margin'
theme={user_praise ? 'filled' : ''}
type="dianzan"
@ -231,7 +234,7 @@ function CommentItem({
</div>
<div
style={{ display: showQuill ? 'block' : 'none' }}
style={{ display: showQuill ? 'block' : 'none'}}
className="comment_item_quill">
<CommentForm
onCancel={handleClickCancel}
@ -239,10 +242,11 @@ function CommentItem({
/>
</div>
<div className="show_upload_image" style={{ display: url ? 'block' : 'none' }}>
<Icon type="close" className="image_close" onClick={handleClose} />
{/* 显示上传的图片信息 */}
<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="" />
<img className="image" src={url} alt=""/>
</div>
</div>
</div>
@ -250,4 +254,4 @@ function CommentItem({
);
}
export default CNotificationHOC()(CommentItem);
export default CNotificationHOC() (CommentItem);

View File

@ -7,28 +7,39 @@
* @LastEditTime : 2019-12-24 18:03:21
*/
import React from 'react';
// import CommentForm from './CommentForm';
import CommentList from './CommentList';
function Comment(props) {
function Comment (props) {
const {
const {
commentLists,
// addComment,
// cancelComment,
isAdmin,
addChildComment,
likeComment,
showOrHideComment,
submitDeleteComment
} = props;
// const handleCancelComment = () => {
// cancelComment && cancelComment();
// };
return (
<CommentList
isAdmin={isAdmin}
likeComment={likeComment}
showOrHideComment={showOrHideComment}
commentLists={commentLists}
submitChildComment={addChildComment}
submitDeleteComment={submitDeleteComment}
/>
<React.Fragment>
{/* <CommentForm
onCancel={handleCancelComment}
onSubmit={addComment}
/> */}
<CommentList
isAdmin={isAdmin}
likeComment={likeComment}
showOrHideComment={showOrHideComment}
commentLists={commentLists}
submitChildComment={addChildComment}
submitDeleteComment={submitDeleteComment}
/>
</React.Fragment>
);
}

View File

@ -1,7 +1,7 @@
/*
* @Description: quill delta -> html
* @Author: tangjiang
* @Github:
* @Github:
* @Date: 2019-12-24 08:51:25
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-26 09:30:11
@ -38,11 +38,13 @@ export const formatDelta = (deltas) => {
alt="${alt}"
/>
`;
// text = "<img src="+url+" width='60px' height='30px' onclick='' alt="+alt+"/>";
}
}
formatted.push(text);
});
console.log(formatted);
return formatted.join('');
}
@ -53,7 +55,7 @@ export const formatDelta = (deltas) => {
*/
export const operate = (text, key, value) => {
let operatedText = null;
debugger;
switch (key) {
case 'bold':
operatedText = `<strong>${text}</strong>`;
@ -70,7 +72,7 @@ export const operate = (text, key, value) => {
case 'link':
operatedText = `<a href="${value}" style="color: #5091ff; text-decoration: underline;" target="bland">${text}</a>`;
break;
default:
default:
operatedText = text;
}

View File

@ -1,64 +0,0 @@
import React, { useMemo, useState, useEffect } from 'react'
import TPMMDEditor from '../../../modules/tpm/challengesnew/tpm-md-editor'
import { markdownToHTML } from 'educoder'
import './index.scss'
function noop() { }
export const defaultQuillOpt = [
'bold', 'italic', 'underline',
{ list: 'ordered' }, { list: 'bullet' },
{ script: 'sub' }, { script: 'super' },
{ 'color': [] },
{ 'background': [] },
'image', 'code-block', 'formula', 'clean'
]
export default ({ is_md, value, onChange, placeholder, autoFocus = true, isError, isShowEditor, height = 160, options = defaultQuillOpt, index, onActiveOption = noop, md_id, showResizeBar = false }) => {
let html = useMemo(() => {
if (value) {
return is_md ? markdownToHTML(value) : value
}
return placeholder
}, [is_md, placeholder, value])
const [isFocus, setIsFocus] = useState(false)
useEffect(() => {
if (isShowEditor) {
setIsFocus(autoFocus)
} else {
setIsFocus(false)
}
}, [autoFocus, isShowEditor])
function onMdContentChangeHandler(v) {
onChange(v, index)
}
function onActiveHandler(e) {
e.preventDefault()
e.stopPropagation()
onActiveOption(index)
}
return (
<div className='editor-content-panel-wrapper'>
<div className={`editor-content-panel markdown-body ${isShowEditor ? 'hide' : null} ${isError ? 'error' : null} ${value ? 'hasValue' : null} `}
dangerouslySetInnerHTML={{ __html: html }} onClick={onActiveHandler} />
{is_md ?
<TPMMDEditor
initValue={value}
height={height}
mdID={md_id}
noStorage={true}
className={`${isShowEditor ? null : 'hideMd'} ${isError ? 'md-error' : null} `}
onChange={onMdContentChangeHandler}
showResizeBar={showResizeBar}
placeholder={placeholder}
/> : null
}
</div>
)
}

View File

@ -1,46 +0,0 @@
.editor-content-panel-wrapper {
margin-bottom: 12px;
.editor-content-panel {
display: flex;
align-items: center;
word-break: break-all;
border-radius: 2px;
max-width: 1056px;
text-align: justify;
height: auto;
min-height: 38px;
border: 1px solid rgba(234, 234, 234, 1);
padding: 4px;
width: 100%;
p {
width: 100%;
}
}
.hide {
display: none;
}
.hasValue {
color: rgb(153, 153, 153);
}
}
.md-error {
.edu-back-greyf5 {
border: 1px solid red;
}
}
.hideMd {
display: none;
}
.error {
border: 1px solid red;
}

View File

@ -524,8 +524,8 @@ const options = [{
value: '天水',
label: '天水'
},{
value: '陇南',
label: '陇南'
value: '武都',
label: '武都'
},{
value: '武威',
label: '武威'
@ -1746,6 +1746,7 @@ class City extends Component{
}
}
onChange = (r1, r2) => {
// debugger
this.props.onChange(r1, r2)
this.setState({ value: r1 })
}

View File

@ -0,0 +1,12 @@
.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%;
}

View File

@ -0,0 +1,83 @@
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;

View File

@ -8,12 +8,12 @@ let _path = isDev() ? 'public' : 'build'
let uploader
let _testHost = ''; '192.168.2.63:3001/api'
let _testHost = '' ; '192.168.2.63:3001/api'
const login = 'innov'
function createUploader() {
function createUploader () {
uploader = new window.AliyunUpload.Vod({
timeout: $('#timeout').val() || 60000,
partSize: $('#partSize').val() || 1048576,
@ -35,12 +35,12 @@ function createUploader() {
},
// 开始上传
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)
// 如果是 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) {
@ -49,9 +49,9 @@ function createUploader() {
// 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'
@ -65,7 +65,7 @@ function createUploader() {
}).catch((error) => {
console.log(error)
})
$('#status').text('文件开始上传...')
console.log("onUploadStarted:" + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object)
@ -81,7 +81,7 @@ function createUploader() {
// }, 'json')
var refreshUrl = `${_testHost}/users/${login}/video_auths.json?debug=true`
axios.put(refreshUrl, {
video_id: uploadInfo.videoId,
}).then((response) => {
@ -132,7 +132,7 @@ function createUploader() {
// }, 'json')
var refreshUrl = `${_testHost}/users/${login}/video_auths.json?debug=true`
axios.put(refreshUrl, {
video_id: uploadInfo.videoId,
}).then((response) => {
@ -152,24 +152,24 @@ function createUploader() {
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`,
`${_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/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) => {
});
});
`${_url_origin}/react/${_path}/js/aliyun-upload/aliyun-upload-sdk-1.5.0.min.js`,
(data, textStatus, jqxhr) => {
});
});
});
}
$('#fileUpload').on('change', function (e) {
@ -201,7 +201,7 @@ function AliyunUploader(props) {
}, [])
let { source, id, className, type } = props;
function onStop() {
$('#resumeUpload').attr('disabled', false)
// $('#pauseUpload').attr('disabled', true)
@ -212,26 +212,26 @@ function AliyunUploader(props) {
// $('#pauseUpload').attr('disabled', false)
uploader.startUpload()
}
return (
return(
<React.Fragment>
<div>
<input type="file" id="fileUpload"></input>
<label className="status">上传状态: <span id="status"></span></label>
<label class="status">上传状态: <span id="status"></span></label>
</div>
<div className="upload-type">
<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 className="progress">上传进度: <i id="auth-progress">0</i> %</span>
<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

View File

@ -8,7 +8,7 @@ let _path = _isDev ? 'public' : 'build'
let uploader
function createUploader() {
function createUploader () {
uploader = new AliyunUpload.Vod({
timeout: $('#timeout').val() || 60000,
partSize: $('#partSize').val() || 1048576,
@ -30,19 +30,19 @@ function createUploader() {
},
// 开始上传
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)
// 如果是 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)
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)
@ -51,10 +51,10 @@ function createUploader() {
// 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)
var uploadAuth = data.UploadAuth
var uploadAddress = data.UploadAddress
var videoId = data.VideoId
uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress,videoId)
}, 'json')
}
},
@ -103,24 +103,24 @@ function createUploader() {
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`,
`${_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/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) => {
});
});
`${_url_origin}/react/${_path}/js/aliyun-upload/aliyun-upload-sdk-1.5.0.min.js`,
(data, textStatus, jqxhr) => {
});
});
});
}
$('#fileUpload').on('change', function (e) {
@ -152,7 +152,7 @@ function AliyunUploaderDemo(props) {
}, [])
let { source, id, className, type } = props;
function onStop() {
$('#resumeUpload').attr('disabled', false)
// $('#pauseUpload').attr('disabled', true)
@ -163,26 +163,26 @@ function AliyunUploaderDemo(props) {
// $('#pauseUpload').attr('disabled', false)
uploader.startUpload()
}
return (
return(
<React.Fragment>
<div>
<input type="file" id="fileUpload"></input>
<label className="status">上传状态: <span id="status"></span></label>
<label class="status">上传状态: <span id="status"></span></label>
</div>
<div className="upload-type">
<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 className="progress">上传进度: <i id="auth-progress">0</i> %</span>
<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

View File

@ -1,23 +1,26 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom'
import {Link} from 'react-router-dom'
const map = {
"blue": "blueFull", "greyBack": "greyBack", "grey": "greyWidthFixed", "green": "greenBack", 'greyLine': "greyLine", 'orangeLine': "orangeLine",
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 (
let{to, children, style, className, ...others}=this.props
return(
<React.Fragment>
{
to === undefined ?
<a 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>
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>
)

View File

@ -1,28 +1,28 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom'
import {Link} from 'react-router-dom'
const map = { "blue": "colorblue", "white": "colorwhite", "grey": "colorgrey", 'orange': "color-orange", 'colorgrey9': 'color-grey-9' }
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 (
let{to, href,targets, style2, style, className, ...others }=this.props
return(
<React.Fragment>
{
to == undefined && targets == undefined ?
<a onClick={this.props.onClick} className={"btn " + `${map[this.props.style]} ${this.props.className || ""}`}
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}`}
>{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>
:
<Link to={to} className={"btn "+`${map[this.props.style]} ${this.props.className}`}
style={style2} {...others}
>{this.props.children}</Link>
}
</React.Fragment>
)

View File

@ -2,72 +2,64 @@
// export { default as OrderStateUtil } from '../routes/Order/components/OrderStateUtil';
export {
getUploadLogoActionUrl as getUploadLogoActionUrl,numFormat as numFormat,
getImageUrl as getImageUrl,getImage as getImage, 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, getcdnImageUrl as getcdnImageUrl
} from './UrlTool';
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 {setmiyah as setmiyah} from './Component';
export { default as queryString } from './UrlTool2';
export { SnackbarHOC as SnackbarHOC } from './SnackbarHOC';
export { SnackbarHOC as SnackbarHOC } from './SnackbarHOC';
export {
trigger as trigger, on as on, off as off
, broadcastChannelPostMessage, broadcastChannelOnmessage
} from './EventUtil';
export { trigger as trigger, on as on, off as off
, broadcastChannelPostMessage, broadcastChannelOnmessage } from './EventUtil';
export { updatePageParams as updatePageParams } from './RouterUtil';
export { updatePageParams as updatePageParams } from './RouterUtil';
export { bytesToSize as bytesToSize } from './UnitUtil';
export { bytesToSize as bytesToSize } from './UnitUtil';
export {
markdownToHTML, uploadNameSizeSeperator, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll, isImageExtension,
downloadFile, sortDirections, validateLength, mdJSONParse, exportMdtoHtml
} from './TextUtil'
export { handleDateString, getNextHalfHourOfMoment, formatDuring, formatSeconds ,timeAgo} from './DateUtil'
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 { configShareForIndex, configShareForPaths, configShareForShixuns, configShareForCourses, configShareForCustom } from './util/ShareUtil'
export { isDev as isDev, isMobile } from './Env'
export { isDev as isDev, isMobile } from './Env'
export { toStore as toStore, fromStore as fromStore } from './Store'
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 { 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 { 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 { themes, ThemeContext } from './context/ThemeContext'
export { ModalHOC } from './components/ModalHOC'
export { ModalHOC } from './components/ModalHOC'
export { SetAppModel } from './components/SetAppModel'
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 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 PopInstruction } from './components/instruction/PopInstruction'
export { default as City } from './components/form/City'
export { default as City } from './components/form/City'
// course
export { default as WordsBtn } from './course/WordsBtn'
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 QuillForEditor } from './quillForEditor'
export { default as DMDEditor } from './components/markdown/DMDEditor'
export { default as Clappr } from './components/media/Clappr'
export { default as AliyunUploader } from './components/media/AliyunUploader'
@ -76,4 +68,10 @@ 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'

View File

@ -12,29 +12,36 @@ function ImageLayer2(props) {
setShowImage(false)
setImageSrc('')
}
useEffect(() => {
$(parentSel).delegate(childSel, "click", (event) => {
const imageSrc = event.target.src || event.target.getAttribute('src') || event.target.getAttribute('href')
// 判断imageSrc是否是图片
const fileName = event.target.innerHTML.trim();
if (isImageExtension((imageSrc && imageSrc.trim())) || isImageExtension(fileName) || event.target.tagName == 'IMG' || (imageSrc && 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()
return false;
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 (
showImage?<ImageLayer showImage={showImage} imageSrc={imageSrc} onImageLayerClose={onImageLayerClose}></ImageLayer>:""
<ImageLayer showImage={showImage} imageSrc={imageSrc} onImageLayerClose={onImageLayerClose}></ImageLayer>
)
}

View File

@ -1,169 +0,0 @@
import marked from 'marked'
import { escape } from 'marked/src/helpers'
function indentCodeCompensation(raw, text) {
const matchIndentToCode = raw.match(/^(\s+)(?:```)/);
if (matchIndentToCode === null) {
return text;
}
const indentToCode = matchIndentToCode[1];
return text
.split('\n')
.map(node => {
const matchIndentInNode = node.match(/^\s+/);
if (matchIndentInNode === null) {
return node;
}
const [indentInNode] = matchIndentInNode;
if (indentInNode.length >= indentToCode.length) {
return node.slice(indentToCode.length);
}
return node;
})
.join('\n');
}
//兼容之前的 ##标题式写法
let toc = []
let ctx = ["<ul>"]
const renderer = new marked.Renderer()
const headingRegex = /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/
export function cleanToc() {
toc = []
ctx = ["<ul>"]
}
function buildToc(coll, k, level, ctx) {
if (k >= coll.length || coll[k].level <= level) { return k }
var node = coll[k]
ctx.push("<li><a href='#" + node.anchor + "'>" + node.text + "</a>")
k++
var childCtx = []
k = buildToc(coll, k, node.level, childCtx)
if (childCtx.length > 0) {
ctx.push("<ul>")
childCtx.forEach(function (idm) {
ctx.push(idm)
});
ctx.push("</ul>")
}
ctx.push("</li>");
k = buildToc(coll, k, level, ctx)
return k
}
export function getTocContent() {
buildToc(toc, 0, 0, ctx);
ctx.push("</ul>");
return ctx.join("");
}
const tokenizer = {
heading(src) {
const cap = headingRegex.exec(src)
if (cap) {
return {
type: 'heading',
raw: cap[0],
depth: cap[1].length,
text: cap[2]
}
}
},
fences(src) {
const cap = this.rules.block.fences.exec(src)
if (cap) {
const raw = cap[0]
let text = indentCodeCompensation(raw, cap[3] || '')
const lang = cap[2] ? cap[2].trim() : cap[2]
if (['latex', 'katex', 'math'].indexOf(lang) >= 0) {
const id = next_id()
const expression = text
text = id
math_expressions[id] = { type: 'block', expression }
}
return {
type: 'code',
raw,
lang,
text
}
}
}
}
const latexRegex = /(?:\${2})([^\n`]+?)(?:\${2})/gi
let katex_count = 0
const next_id = () => `__special_katext_id_${katex_count++}__`
let math_expressions = {}
export function getMathExpressions() {
return math_expressions
}
export function resetMathExpressions() {
katex_count = 0
math_expressions = {}
}
function replace_math_with_ids(text) {
let rs = text.replace(latexRegex, (_match, expression) => {
const id = next_id()
math_expressions[id] = { type: 'inline', expression }
return id
})
return rs
}
const original_listitem = renderer.listitem
renderer.listitem = function (text, task, checked) {
return original_listitem(replace_math_with_ids(text), task, checked)
}
const original_paragraph = renderer.paragraph
renderer.paragraph = function (text) {
return original_paragraph(replace_math_with_ids(text))
}
const original_tablecell = renderer.tablecell
renderer.tablecell = function (content, flags) {
return original_tablecell(replace_math_with_ids(content), flags)
}
renderer.code = function (code, infostring, escaped) {
const lang = (infostring || '').match(/\S*/)[0];
if (!lang) {
return '<pre class="prettyprint linenums"><code>'
+ (escaped ? code : escape(code, true))
+ '</code></pre>';
}
if (['latex', 'katex', 'math'].indexOf(lang) >= 0) {
return `<p class='editormd-tex'>${code}</p>`
} else {
return `<pre class="prettyprint linenums"><code class="language-${infostring}">${escaped ? code : escape(code, true)}</code></pre>\n`
}
}
renderer.heading = function (text, level, raw) {
let anchor = this.options.headerPrefix + raw.toLowerCase().replace(/[^\w\\u4e00-\\u9fa5]]+/g, '-');
toc.push({
anchor: anchor,
level: level,
text: text
})
return '<h' + level + ' id="' + anchor + '">' + text + '</h' + level + '>'
}
marked.setOptions({
silent: true,
smartypants: true,
gfm: true,
pedantic: false
})
marked.use({ tokenizer, renderer });
export default marked

View File

@ -1,46 +0,0 @@
function Mediator(obj) {
const channels = {}
const mediator = {
subscribe: function (channel, cb) {
if (!channels[channel]) {
channels[channel] = []
}
channels[channel].push(cb)
return this.unsubscribe.bind(null, channel, cb)
},
unsubscribe: function (channel, cb) {
let rs = channels[channel]
let index = -1
if (rs) {
for (let i = 0; i < rs.length; i++) {
if (rs[i].name === cb.name) {
index = i
break
}
}
if (index >= 0) {
channels[channel].splice(index, 1)
return true
}
}
return false
},
publish: function (channel) {
if (!channels[channel]) {
return false
}
const args = Array.prototype.slice.call(arguments, 1)
channels[channel].forEach(subscription => {
subscription.apply(null, args)
})
return this
},
}
return obj ? Object.assign(obj, mediator) : mediator
}
const mediator = new Mediator()
export default mediator

View File

@ -6,28 +6,29 @@
* @LastEditors : tangjiang
* @LastEditTime : 2020-02-05 10:44:01
*/
import Quill from "quill"
const InlineBlot = Quill.import('blots/inline')
const Prop = 'data-index'
class FillBlot extends InlineBlot {
static create(value) {
const node = super.create()
node.setAttribute(Prop, value.data_index);
node.innerText = value.text;
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) {
static value (node) {
return {
fill_index: node.getAttribute(Prop)
// dataSet: node.getAttribute('data-fill'),
data_index: node.getAttribute('data_index')
}
}
}
FillBlot.blotName = "fill";
FillBlot.tagName = "span";

View File

@ -1,54 +1,70 @@
/*
* @Description: 重写图片
* @Author: tangjiang
* @Github:
* @Github:
* @Date: 2019-12-16 15:50:45
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-31 13:59:02
*/
import Quill from "quill";
import mediator from "../mediator";
const BlockEmbed = Quill.import('blots/block/embed');
//stringify 序列化其中的onclick函数会丢失。
export default class ImageBlot extends BlockEmbed {
static create(value) {
const node = super.create()
const { alt, url, width, height, id } = value
node.setAttribute('alt', alt)
node.setAttribute('src', url)
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);
}
node.setAttribute('style', 'max-width: 100%, cursor:pointer');
node.onload = function () {
node.setAttribute('width', width ? width : this.width)
node.setAttribute('height', height ? height : this.height)
// 宽度和高度都不存在时,
if (!value.width && !value.height) {
// node.setAttribute('display', 'block');
node.setAttribute('width', '100%');
}
node.addEventListener('click', () => {
mediator.publish('on-preview-image', value.url)
})
// 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) {
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';
ImageBlot.tagName = 'img';

View File

@ -1,66 +0,0 @@
@charset "utf-8";
.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 !important;
}
.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 !important;
}
.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 !important;
}
.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 !important;
}
.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 !important;
}
.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 !important;
}
.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 !important;
}
.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 !important;
}
.ql-font-SimSun {
font-family:SimSun !important;
}
.ql-font-SimHei {
font-family:SimHei !important;
}
.ql-font-Microsoft-YaHei {
font-family:Microsoft YaHei !important;
}
.ql-font-KaiTi {
font-family:KaiTi !important;
}
.ql-font-FangSong {
font-family:FangSong !important;
}
.ql-font-Arial {
font-family:Arial !important;
}
.ql-font-Times-New-Roman {
font-family:Times New Roman !important;
}
.ql-font-sans-serif {
font-family:sans-serif !important;
}

View File

@ -1,100 +1,43 @@
import './index.scss'
import 'quill/dist/quill.core.css' // 核心样式
import 'quill/dist/quill.snow.css' // 有工具栏
import 'quill/dist/quill.bubble.css' // 无工具栏
import './font.css'
import React, { useState, useRef, useEffect } from 'react'
import Quill from 'quill'
import katex from 'katex'
import { fetchUploadImage } from '../../services/ojService.js'
/*
* @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'
import LinkBlot from './link-blot'
import mediator from '../mediator'
import { defaultQuillOpt } from '../components/custom-editor'
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'];
let Size = Quill.import('attributors/style/size')
Size.whitelist = ['14px', '16px', '18px', '20px', false]
let fonts = ['Microsoft-YaHei', 'SimSun', 'SimHei', 'KaiTi', 'FangSong']
let Font = Quill.import('formats/font')
Font.whitelist = fonts; //将字体加入到白名单
window.Quill = Quill;
window.katex = katex;
Quill.register(ImageBlot);
Quill.register(Size);
Quill.register(LinkBlot);
Quill.register(Font, true);
// Quill.register({'modules/toolbar': Toolbar});
Quill.register({
'formats/fill': FillBlot
});
// Quill.register(Color);
Quill.register(FillBlot)
const FillStr = '▁'
const FillReg = new RegExp(FillStr, 'g')
const BRSTR = '<p><br></p>'
//hack
function quillSetValue(el, value) {
if (value && value.hasOwnProperty('ops')) {
el.setContents(value)
} else {
if (value) {
let rs = value
if (rs.endsWith(BRSTR)) {
rs += BRSTR
}
el.clipboard.dangerouslyPasteHTML(rs);
}
}
}
const BUTTONTIP = {
'bold': '加粗',
'strike': '删除线',
'italic': '斜体',
'underline': '下划线',
'ordered': '有序列表',
'bullet': '无序列表',
'color': '字体颜色',
'background': '背景色',
'sub': '下标',
'super': '上标',
'image': '上传图片',
'code-block': '代码块',
'formula': '公式',
'clean': '清除格式',
'fill': '插入填空项',
'align': '对齐',
'header1': '标题一',
'header2': '标题二',
'header3': '标题三',
'header4': '标题四',
'header5': '标题五',
'header6': '标题六'
}
function insertAfter(newNode, referenceNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
function initButtonTip(el) {
const container = el.getModule('toolbar').container
const btns = container.querySelectorAll('.ql-formats > button,.ql-formats>span')
Array.from(btns).forEach(btn => {
let value = btn.getAttribute('value')
let cls = btn.classList[0].replace('ql-', '')
btn.setAttribute('title', BUTTONTIP[value || cls] || BUTTONTIP[`${cls}${value}`])
if (cls === 'fill') {
let span = document.createElement('span')
span.setAttribute('class', 'fill-tip')
span.innerText = '点击插入填空项'
span.addEventListener('click', () => { btn.click() })
insertAfter(span, btn)
}
})
}
export default ({
defaultValue,
function QuillForEditor ({
placeholder,
readOnly,
autoFocus = false,
@ -105,16 +48,46 @@ export default ({
wrapStyle = {},
showUploadImage,
onContentChange,
className,
onAddFill,
onDeleteFill
}) => {
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)
const editorRef = useRef(null);
// quill 实例
const [quill, setQuill] = useState(null)
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',
/**
@ -125,150 +98,183 @@ export default ({
* @param {*} context 上下文
*/
handler: function (range, context) {
const [index, delArrs] = getFillInfo(range, this.quill)
/**
* 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) {
if (window.confirm('确定要删除这个填空吗?')) {
onDeleteFill && onDeleteFill(index, delArrs.length) // 调用删除回调, 返回删除的元素下标[]
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 false;
}
}
return true
return true;
}
}
}
};
// quill 配置信息
const quillOption = {
modules: {
toolbar: options || defaultQuillOpt,
'syntax': false,
toolbar: renderOptions,
keyboard: {
bindings: bindings
}
// toolbar: {
// container: renderOptions
// }
},
readOnly,
placeholder,
theme: readOnly ? 'bubble' : 'snow',
}
};
//返回当前选中文件之前填空的索引,和选中的文本内有多少个填空
function getFillInfo(range, quill) {
const { index, length } = range
const start = length === 0 ? index - 1 : index
const selectionText = quill.getText(start, Math.max(1, length))
const beforeSelectionText = quill.getText(0, start)
return [(beforeSelectionText.match(FillReg) || []).length, selectionText.match(FillReg)]
}
useEffect(() => {
const quillInstance = new Quill(editorRef.current, quillOption)
setQuill(quillInstance)
const quillNode = document.createElement('div');
editorRef.current.appendChild(quillNode);
const _quill = new Quill(editorRef.current, quillOption);
setQuill(_quill);
// 处理图片上传功能
quillInstance.getModule('toolbar').addHandler('image', (e) => {
const input = document.createElement('input')
input.setAttribute('type', 'file')
input.setAttribute('accept', 'image/*')
input.click()
_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 file = input.files[0]; // 获取文件信息
const formData = new FormData();
formData.append('file', file);
const range = quillInstance.getSelection(true)
let fileUrl = '' // 保存上传成功后图片的url
const result = await fetchUploadImage(formData)
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}`)
fileUrl = getImageUrl(`api/attachments/${result.data.id}`);
}
const { width, height } = imgAttrs
// 根据id获取文件路径
const { width, height } = imgAttrs;
// console.log('上传图片的url:', fileUrl);
if (fileUrl) {
let imgOption = {
_quill.insertEmbed(range.index, 'image', {
url: fileUrl,
alt: '图片信息',
onClick: showUploadImage,
width,
height
}
quillInstance.insertEmbed(range.index, 'image', imgOption)
});
}
}
})
quillInstance.getModule('toolbar').addHandler('fill', (e) => {
const range = quillInstance.getSelection(true)
const [fillIndex, _] = getFillInfo(range, quillInstance)
quillInstance.insertEmbed(range.index, 'fill', { "data-index": fillIndex, text: FillStr })
quillInstance.setSelection(quillInstance.getLength(), 0, 'api')
onAddFill && onAddFill(fillIndex) // 调用添加回调
})
initButtonTip(quillInstance)
quillSetValue(quillInstance, value)
if (autoFocus) {
quillInstance.focus()
}
function onPreview(url) {
if (showUploadImage) {
showUploadImage(url)
}
}
let unsub = mediator.subscribe('on-preview-image', onPreview)
return () => {
unsub()
}
}, [])
});
// 处理填空
_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) {
if (readOnly === true) {
quill.enable(false)
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.enable(true)
quill.setContents(value)
if (autoFocus) quill.focus();
}
}
}, [quill, readOnly])
}, [quill, value, setQuill, autoFocus]);
// 清除选择区域
useEffect(() => {
quillSetValue(quill, defaultValue)
}, [quill, defaultValue])
useEffect(() => {
if (quill) {
quill.root.dataset.placeholder = placeholder;
if (quill && selection) {
quill.setSelection(selection)
setSelection(null)
}
}, [quill, placeholder])
}, [quill, selection, setSelection]);
// 设置placeholder值
useEffect(() => {
if (quill) {
autoFocus ? quill.focus() : quill.blur()
}
}, [quill, autoFocus])
if (!quill || !quill.root) return;
quill.root.dataset.placeholder = placeholder;
}, [quill, placeholder]);
// 处理内容变化
useEffect(() => {
if (quill && onContentChange) {
function onChangeHandler(delta, oldDelta, source) {
let html = editorRef.current.children[0].innerHTML
let text = quill.getText()
if (html === '<p><br></p>') html = ''
if (onContentChange) {
onContentChange(quill.getContents(), html, { quill, text, delta, oldDelta, source })
}
}
quill.on('text-change', onChangeHandler)
return () => {
quill.off('text-change', onChangeHandler)
}
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, onContentChange])
}, [quill, handleOnChange]);
// 返回结果
return (
<div className={`quill_editor_for_react_area ${className} `} style={wrapStyle}>
<div ref={editorRef} style={style}></div>
</div>
)
}
<div className='quill_editor_for_react_area' style={wrapStyle}>
<div ref={editorRef} style={style}></div>
</div>
);
}
export default QuillForEditor;

View File

@ -1,200 +1,132 @@
.quill_editor_for_react_area {
.quill_editor_for_react_area{
// background: #fff;
// margin: 0 15px;
.ql-editing {
.ql-editing{
left: 0 !important;
}
.ql-editor {
img {
.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;
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;
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;
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;
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;
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;
}
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";
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";
font-family: "SimSun";
}
.ql-font-SimHei {
font-family: "SimHei";
font-family: "SimHei";
}
.ql-font-Microsoft-YaHei {
font-family: "Microsoft YaHei";
font-family: "Microsoft YaHei";
}
.ql-font-KaiTi {
font-family: "KaiTi";
font-family: "KaiTi";
}
.ql-font-FangSong {
font-family: "FangSong";
font-family: "FangSong";
}
.ql-font-Arial {
font-family: "Arial";
font-family: "Arial";
}
.ql-font-Times-New-Roman {
font-family: "Times New Roman";
font-family: "Times New Roman";
}
.ql-font-sans-serif {
font-family: "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 {
.ql-snow .ql-fill{
display: inline-block;
position: relative;
color: #05101A;
// font-size: 18px;
vertical-align: top;
&::before {
&::before{
position: absolute;
left: 50%;
top: -1px;
content: '\e691';
content: '\e709';
font-family: 'iconfont';
margin-left: -7px;
}
}
//块点不动
.ql-toolbar .ql-formats .ql-code-block svg {
pointer-events: none;
}
}
.fill-tip {
cursor: pointer;
font-size: 12px;
display: inline-block;
padding: 5px;
border: 1px solid #eaeaea;
border-radius: 5px;
box-sizing: border-box;
line-height: 16px;
color: #666;
position: relative;
justify-content: center;
margin-left: 10px;
&::before {
content: ' ';
width: 0;
height: 0;
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
border-right: 6px solid #fff;
position: absolute;
left: -6px;
top: 6px;
z-index: 10;
}
&::after {
content: ' ';
width: 0;
height: 0;
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
border-right: 6px solid #cccbcb;
position: absolute;
left: -7px;
top: 6px;
}
}

View File

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

View File

@ -1,18 +0,0 @@
.ql-editor .ql-font-Microsoft-YaHei {
font-family: "Microsoft YaHei";
}
.ql-editor .ql-font-SimSun {
font-family: "SimSun";
}
.ql-editor .ql-font-SimHei {
font-family: "SimHei";
}
.ql-editor .ql-font-KaiTi {
font-family: "KaiTi";
}
.ql-editor .ql-font-Arial {
font-family: "Arial";
}
.ql-editor .Times-New-Roman {
font-family: "Times New Roman";
}

View File

@ -1,28 +0,0 @@
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
content: '微软雅黑';
}
.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: "微软雅黑";
}
.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: "宋体";
}
.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: "黑体";
}
.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: "楷体";
}
.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";
}
.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";
}

View File

@ -0,0 +1,54 @@
/*
* @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';

View File

@ -0,0 +1,45 @@
/*
* @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;

View File

@ -0,0 +1,47 @@
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;

View File

@ -0,0 +1,26 @@
/*
* @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;

View File

@ -0,0 +1,108 @@
/*
* @Description: 入口文件
* @Author: tangjiang
* @Github:
* @Date: 2019-12-17 10:41:48
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-17 20:34:40
*/
import React, { useState, useCallback, useEffect } from 'react';
import ReactQuill from './lib';
function Wrapper (props) {
// 默认工具栏配置项
const toolbarConfig = [
['bold', 'italic', 'underline'],
[{align: []}, {list: 'ordered'}, {list: 'bullet'}], // 列表
[{script: 'sub'}, {script: 'super'}],
[{header: [1,2,3,4,5,false]}],
['blockquote', 'code-block'],
['link', 'image', 'video'],
['formula'],
['clean']
];
const [placeholder] = useState(props.placeholder || 'placeholder');
const [disableBold] = useState(false);
const [value, setValue] = useState(props.value || '');
const [toolbar, setToolbar] = useState(toolbarConfig);
const [theme, setTheme] = useState(props.theme || 'snow');
const [readOnly] = useState(props.readOnly || false);
const {
onContentChagne, // 当编辑器内容变化时调用该函数
showUploadImage, // 显示上传图片, 返回url主要用于点击图片放大
} = props;
// 配置信息
const options = {
modules: {
toolbar: toolbar,
clipboard: {
matchVisual: false
}
},
readOnly: readOnly,
theme: theme
}
// 配置信息
useEffect (() => {
if (props.options) {
setToolbar(props.options);
}
setTheme(props.theme || 'snow');
setValue(props.value);
}, [props]);
// 当内容变化时
const handleOnChange = useCallback(
contents => {
if (disableBold) {
setValue({
ops: contents.ops.map(x => {
x = {...x};
if (x && x.attributes && x.attributes.bold) {
x.attributes = { ...x.attributes };
delete x.attributes.bold;
if (!Object.keys(x.attributes).length) {
delete x.attributes;
}
}
return x;
})
});
} else {
setValue(contents);
}
onContentChagne && onContentChagne(contents);
}, [disableBold]
);
// 图片上传
const handleUploadImage = (files) => {
console.log('选择的图片信息', files);
}
// 显示图片
const handleShowUploadImage = (url) => {
// console.log('上传的图片url:', url);
showUploadImage && showUploadImage(url);
}
return (
<React.Fragment>
<ReactQuill
value={value}
style={props.style}
onChange={handleOnChange}
placeholder={`${placeholder}`}
options={options}
uploadImage={handleUploadImage}
showUploadImage={(url) => handleShowUploadImage(url)}
/>
</React.Fragment>
);
}
export default Wrapper;
// ReactDOM.render(<Wrapper />, document.querySelector('#root'));

View File

@ -0,0 +1,32 @@
#quill-toolbar{
.quill-btn{
vertical-align: middle;
}
.quill_image{
display: inline-block;
position: relative;
vertical-align: middle;
width: 28px;
height: 24px;
overflow: hidden;
.image_input{
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
opacity: 0;
}
.ql-image{
position: relative;
left: 0;
top: 0;
}
}
}
.react_quill_area{
.ql-toolbar:not(:last-child) {
display: none;
}
}

View File

@ -0,0 +1,13 @@
/*
* @Description: 导出 ReactQuill
* @Author: tangjiang
* @Github:
* @Date: 2019-12-09 09:08:24
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-16 11:37:13
*/
import ReactQuill from './ReactQuill';
import useQuill from './useQuill';
export default ReactQuill;
export { useQuill };

View File

@ -0,0 +1,27 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2019-12-12 19:48:55
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-16 11:38:16
*/
import { useState, useEffect } from 'react';
import deepEqual from './deepEqual';
function useDeepEqual (input) {
const [value, setValue] = useState(input);
useEffect(() => {
if (!deepEqual(input, value)) {
setValue(input)
}
}, [input, value]);
return value;
}
export default useDeepEqual;

View File

@ -0,0 +1,148 @@
/*
* @Description: 创建 reactQuill实例
* @Author: tangjiang
* @Github:
* @Date: 2019-12-09 09:31:42
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-17 20:42:05
*/
import Quill from 'quill'; // 导入quill
import { useState, useEffect, useMemo } from 'react';
import flatten from './flatten.js';
import useDeepEqualMemo from './useDeepEqualMemo';
import Katex from 'katex';
import ImageBlot from './ImageBlot';
import { fetchUploadImage } from '../../services/ojService.js';
import { getImageUrl } from 'educoder'
window.katex = Katex;
Quill.register(ImageBlot);
function useMountQuill ({
element,
options: passedOptions,
uploadImage,
showUploadImage,
imgAttrs = {} // 指定图片的宽高属性
}) {
// 是否引入 katex
const [katexLoaded, setKatexLoaded] = useState(Boolean(window.katex))
const [quill, setQuill] = useState(null);
const options = useDeepEqualMemo(passedOptions);
console.log('use mount quill: ', passedOptions);
// 判断options中是否包含公式
const requireKatex = useMemo(() => {
return flatten(options.modules.toolbar).includes('formula');
}, [options]);
// 加载katex
useEffect(() => {
if (!requireKatex) return;
if (katexLoaded) return;
const interval = setInterval(() => {
if (window.katex) {
setKatexLoaded(true);
clearInterval(interval);
}
});
return () => { // 定义回调清除定时器
clearInterval(interval);
}
}, [
setKatexLoaded,
katexLoaded,
requireKatex
]);
// 加载 quill
useEffect(() => {
if (!element) return;
if (requireKatex && !katexLoaded) {
element.innerHTML = `
<div style="color: #ddd">
Loading Katex...
</div>
`
}
// 清空内容
element.innerHTML = '';
console.log(element);
// 创建 quill 节点
const quillNode = document.createElement('div');
element.appendChild(quillNode); // 将quill节点追回到 element 元素中
const quill = new Quill(element, options);
setQuill(quill);
// 加载上传图片功能
if (typeof uploadImage === 'function') {
quill.getModule('toolbar').addHandler('image', (e) => {
// 创建type类型输入框加载本地图片
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 reader = new FileReader();
// reader.readAsDataURL(file);
// console.log('文件信息===>>', reader);
// reader.onload = function (e) {
// debugger;
// console.log('文件信息===>>', e.target.result);
// const image = new Image();
// image.src = e.target.result;
// image.onload = function () {
// // file.width =
// console.log(image.width, image.height);
// }
// }
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
});
}
}
});
}
return () => {
element.innerHTML = '';
}
}, [
element,
options,
requireKatex,
katexLoaded,
]);
return quill;
}
export default useMountQuill;

View File

@ -0,0 +1,60 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2019-12-09 09:09:50
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-17 15:46:50
*/
import useQuillPlaceholder from './useQuillPlaceholder';
import useQuillValueSync from './useQuillValueSync';
import useQuillOnChange from './useQuillOnChange';
import useMountQuill from './useMountQuill';
import { useEffect } from 'react';
function useQuill ({
disallowColors,
placeholder,
uploadImage,
onChange,
options,
value,
element,
showUploadImage
}) {
// 获取 quill 实例
const quill = useMountQuill({
element,
options,
uploadImage,
showUploadImage
});
useEffect(() => {
if (disallowColors && quill) {
quill.clipboard.addMatcher(Node.ELEMENT_NODE, (node, delta) => {
delta.ops = delta.ops.map(op => {
if (op.attributes && op.attributes.color) {
const { color, ...attributes } = op.attributes;
return {
...op,
attributes
}
}
return op;
});
return delta;
});
}
}, [
disallowColors,
quill
]);
useQuillPlaceholder(quill, placeholder);
useQuillValueSync(quill, value);
useQuillOnChange(quill, onChange);
}
export default useQuill;

View File

@ -0,0 +1,33 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2019-12-12 19:49:11
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-16 11:39:27
*/
import { useEffect } from 'react';
function useQuillOnChange (quill, onChange) {
useEffect(() => {
if (!quill) return;
if (typeof onChange !== 'function') return;
let handler;
quill.on(
'text-change',
(handler = () => {
onChange(quill.getContents()); // getContents: 检索编辑器内容
})
);
return () => {
quill.off('text-change', handler);
}
}, [quill, onChange]);
}
export default useQuillOnChange;

View File

@ -0,0 +1,22 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2019-12-09 09:28:34
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-16 11:39:48
*/
import { useEffect } from 'react'
function useQuillPlaceholder (
quill,
placeholder
) {
useEffect(() => {
if (!quill || !quill.root) return;
quill.root.dataset.placeholder = placeholder;
}, [quill, placeholder]);
}
export default useQuillPlaceholder;

Some files were not shown because too many files have changed in this diff Show More