Commit 046d1e40 by guomingpang

style:将全局表格switch尺寸改为32px

parents aab45bf5 e10bc4c5
'use strict' const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const resolve = require('resolve');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const safePostCssParser = require('postcss-safe-parser');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const paths = require('./paths');
const modules = require('./modules');
const getClientEnvironment = require('./env');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin');
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
const fs = require('fs') const postcssNormalize = require('postcss-normalize');
const path = require('path')
const webpack = require('webpack')
const resolve = require('resolve')
const PnpWebpackPlugin = require('pnp-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin')
const TerserPlugin = require('terser-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const safePostCssParser = require('postcss-safe-parser')
const ManifestPlugin = require('webpack-manifest-plugin')
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin')
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin')
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin')
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent')
const paths = require('./paths')
const modules = require('./modules')
const vConsolePlugin = require('vconsole-webpack-plugin')
const getClientEnvironment = require('./env')
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin')
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin')
const typescriptFormatter = require('react-dev-utils/typescriptFormatter')
const postcssNormalize = require('postcss-normalize') const appPackageJson = require(paths.appPackageJson);
const appPackageJson = require(paths.appPackageJson)
// Source maps are resource heavy and can cause out of memory issue for large source files. // Source maps are resource heavy and can cause out of memory issue for large source files.
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false' const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
// Some apps do not need the benefits of saving a web request, so not inlining the chunk // Some apps do not need the benefits of saving a web request, so not inlining the chunk
// makes for a smoother build process. // makes for a smoother build process.
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false' const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
const isExtendingEslintConfig = process.env.EXTEND_ESLINT === 'true' const isExtendingEslintConfig = process.env.EXTEND_ESLINT === 'true';
const imageInlineSizeLimit = parseInt(process.env.IMAGE_INLINE_SIZE_LIMIT || '10000') const imageInlineSizeLimit = parseInt(process.env.IMAGE_INLINE_SIZE_LIMIT || '10000');
// Check if TypeScript is setup // Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig) const useTypeScript = fs.existsSync(paths.appTsConfig);
// style files regexes // style files regexes
const cssRegex = /\.css$/ const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/ const cssModuleRegex = /\.module\.css$/;
const lessRegex = /\.less$/ const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/ const lessModuleRegex = /\.module\.less$/;
// This is the production and development configuration. // This is the production and development configuration.
// It is focused on developer experience, fast rebuilds, and a minimal bundle. // It is focused on developer experience, fast rebuilds, and a minimal bundle.
module.exports = function (webpackEnv) { module.exports = function (webpackEnv) {
const isEnvDevelopment = webpackEnv === 'development' const isEnvDevelopment = webpackEnv === 'development';
const isEnvProduction = webpackEnv === 'production' const isEnvProduction = webpackEnv === 'production';
// Variable used for enabling profiling in Production // Variable used for enabling profiling in Production
// passed into alias object. Uses a flag if passed into the build command // passed into alias object. Uses a flag if passed into the build command
const isEnvProductionProfile = isEnvProduction && process.argv.includes('--profile') const isEnvProductionProfile = isEnvProduction && process.argv.includes('--profile');
// We will provide `paths.publicUrlOrPath` to our app // We will provide `paths.publicUrlOrPath` to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript. // 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. // Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
// Get environment variables to inject into our app. // Get environment variables to inject into our app.
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1)) const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
var PREFIX = 'b-'; //static 文件夹前缀
// common function to get style loaders // common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => { const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [ const loaders = [
...@@ -73,11 +70,11 @@ module.exports = function (webpackEnv) { ...@@ -73,11 +70,11 @@ module.exports = function (webpackEnv) {
loader: MiniCssExtractPlugin.loader, loader: MiniCssExtractPlugin.loader,
// css is located in `static/css`, use '../../' to locate index.html folder // css is located in `static/css`, use '../../' to locate index.html folder
// in production `paths.publicUrlOrPath` can be a relative path // in production `paths.publicUrlOrPath` can be a relative path
options: paths.publicUrlOrPath.startsWith('.') ? { publicPath: '../../' } : {} options: paths.publicUrlOrPath.startsWith('.') ? { publicPath: '../../' } : {},
}, },
{ {
loader: require.resolve('css-loader'), loader: require.resolve('css-loader'),
options: cssOptions options: cssOptions,
}, },
{ {
// Options for PostCSS as we reference these options twice // Options for PostCSS as we reference these options twice
...@@ -92,32 +89,32 @@ module.exports = function (webpackEnv) { ...@@ -92,32 +89,32 @@ module.exports = function (webpackEnv) {
require('postcss-flexbugs-fixes'), require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({ require('postcss-preset-env')({
autoprefixer: { autoprefixer: {
flexbox: 'no-2009' flexbox: 'no-2009',
}, },
stage: 3 stage: 3,
}), }),
// Adds PostCSS Normalize as the reset css with default options, // Adds PostCSS Normalize as the reset css with default options,
// so that it honors browserslist config in package.json // so that it honors browserslist config in package.json
// which in turn let's users customize the target behavior as per their needs. // which in turn let's users customize the target behavior as per their needs.
postcssNormalize() postcssNormalize(),
], ],
sourceMap: isEnvProduction && shouldUseSourceMap sourceMap: isEnvProduction && shouldUseSourceMap,
} },
} },
].filter(Boolean) ].filter(Boolean);
if (preProcessor) { if (preProcessor) {
loaders.push( loaders.push(
{ {
loader: require.resolve('resolve-url-loader'), loader: require.resolve('resolve-url-loader'),
options: { options: {
sourceMap: isEnvProduction && shouldUseSourceMap sourceMap: isEnvProduction && shouldUseSourceMap,
} },
}, },
{ {
loader: require.resolve(preProcessor), loader: require.resolve(preProcessor),
options: { options: {
sourceMap: true sourceMap: true,
} },
}, },
{ {
loader: require.resolve('less-loader'), loader: require.resolve('less-loader'),
...@@ -127,14 +124,14 @@ module.exports = function (webpackEnv) { ...@@ -127,14 +124,14 @@ module.exports = function (webpackEnv) {
'primary-color': '#2966FF', 'primary-color': '#2966FF',
'border-radius-base': '2px', 'border-radius-base': '2px',
}, },
javascriptEnabled: true javascriptEnabled: true,
} },
} },
} }
) );
} }
return loaders return loaders;
} };
return { return {
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development', mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
...@@ -156,7 +153,7 @@ module.exports = function (webpackEnv) { ...@@ -156,7 +153,7 @@ module.exports = function (webpackEnv) {
isEnvDevelopment && require.resolve('webpack/hot/dev-server'), isEnvDevelopment && require.resolve('webpack/hot/dev-server'),
isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient'), isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient'),
// Finally, this is your app's code: // Finally, this is your app's code:
paths.appIndexJs paths.appIndexJs,
// We include the app code last so that if there is a runtime error during // We include the app code last so that if there is a runtime error during
// initialization, it doesn't blow up the WebpackDevServer client, and // initialization, it doesn't blow up the WebpackDevServer client, and
// changing JS code would still trigger a refresh. // changing JS code would still trigger a refresh.
...@@ -168,11 +165,11 @@ module.exports = function (webpackEnv) { ...@@ -168,11 +165,11 @@ module.exports = function (webpackEnv) {
pathinfo: isEnvDevelopment, pathinfo: isEnvDevelopment,
// There will be one main bundle, and one file per asynchronous chunk. // There will be one main bundle, and one file per asynchronous chunk.
// In development, it does not produce real files. // In development, it does not produce real files.
filename: isEnvProduction ? 'static/js/[name].[contenthash:8].js' : isEnvDevelopment && 'static/js/bundle.js', filename: isEnvProduction ? PREFIX + 'static/js/[name].[contenthash:8].js' : isEnvDevelopment && PREFIX + 'static/js/bundle.js',
// TODO: remove this when upgrading to webpack 5 // TODO: remove this when upgrading to webpack 5
futureEmitAssets: true, futureEmitAssets: true,
// There are also additional JS chunk files if you use code splitting. // There are also additional JS chunk files if you use code splitting.
chunkFilename: isEnvProduction ? 'static/js/[name].[contenthash:8].chunk.js' : isEnvDevelopment && 'static/js/[name].chunk.js', chunkFilename: isEnvProduction ? PREFIX + 'static/js/[name].[contenthash:8].chunk.js' : isEnvDevelopment && PREFIX + 'static/js/[name].chunk.js',
// webpack uses `publicPath` to determine where the app is being served from. // 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. // It requires a trailing slash, or the file assets will get an incorrect path.
// We inferred the "public path" (such as / or /my-project) from homepage. // We inferred the "public path" (such as / or /my-project) from homepage.
...@@ -189,7 +186,7 @@ module.exports = function (webpackEnv) { ...@@ -189,7 +186,7 @@ module.exports = function (webpackEnv) {
globalObject: 'this', globalObject: 'this',
library: appPackageJson.name, library: appPackageJson.name,
libraryTarget: 'umd', libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${appPackageJson.name}` jsonpFunction: `webpackJsonp_${appPackageJson.name}`,
}, },
optimization: { optimization: {
minimize: isEnvProduction, minimize: isEnvProduction,
...@@ -203,7 +200,7 @@ module.exports = function (webpackEnv) { ...@@ -203,7 +200,7 @@ module.exports = function (webpackEnv) {
// into invalid ecma 5 code. This is why the 'compress' and 'output' // into invalid ecma 5 code. This is why the 'compress' and 'output'
// sections only apply transformations that are ecma 5 safe // sections only apply transformations that are ecma 5 safe
// https://github.com/facebook/create-react-app/pull/4234 // https://github.com/facebook/create-react-app/pull/4234
ecma: 8 ecma: 8,
}, },
compress: { compress: {
ecma: 5, ecma: 5,
...@@ -217,10 +214,10 @@ module.exports = function (webpackEnv) { ...@@ -217,10 +214,10 @@ module.exports = function (webpackEnv) {
// https://github.com/facebook/create-react-app/issues/5250 // https://github.com/facebook/create-react-app/issues/5250
// Pending further investigation: // Pending further investigation:
// https://github.com/terser-js/terser/issues/120 // https://github.com/terser-js/terser/issues/120
inline: 2 inline: 2,
}, },
mangle: { mangle: {
safari10: true safari10: true,
}, },
// Added for profiling in devtools // Added for profiling in devtools
keep_classnames: isEnvProductionProfile, keep_classnames: isEnvProductionProfile,
...@@ -230,10 +227,10 @@ module.exports = function (webpackEnv) { ...@@ -230,10 +227,10 @@ module.exports = function (webpackEnv) {
comments: false, comments: false,
// Turned on because emoji and regex is not minified properly using default // Turned on because emoji and regex is not minified properly using default
// https://github.com/facebook/create-react-app/issues/2488 // https://github.com/facebook/create-react-app/issues/2488
ascii_only: true ascii_only: true,
} },
}, },
sourceMap: shouldUseSourceMap sourceMap: shouldUseSourceMap,
}), }),
// This is only used in production mode // This is only used in production mode
new OptimizeCSSAssetsPlugin({ new OptimizeCSSAssetsPlugin({
...@@ -246,28 +243,28 @@ module.exports = function (webpackEnv) { ...@@ -246,28 +243,28 @@ module.exports = function (webpackEnv) {
inline: false, inline: false,
// `annotation: true` appends the sourceMappingURL to the end of // `annotation: true` appends the sourceMappingURL to the end of
// the css file, helping the browser find the sourcemap // the css file, helping the browser find the sourcemap
annotation: true annotation: true,
} }
: false : false,
}, },
cssProcessorPluginOptions: { cssProcessorPluginOptions: {
preset: ['default', { minifyFontValues: { removeQuotes: false } }] preset: ['default', { minifyFontValues: { removeQuotes: false } }],
} },
}) }),
], ],
// Automatically split vendor and commons // Automatically split vendor and commons
// https://twitter.com/wSokra/status/969633336732905474 // https://twitter.com/wSokra/status/969633336732905474
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366 // https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
splitChunks: { splitChunks: {
chunks: 'all', chunks: 'all',
name: false name: false,
}, },
// Keep the runtime chunk separated to enable long term caching // Keep the runtime chunk separated to enable long term caching
// https://twitter.com/wSokra/status/969679223278505985 // https://twitter.com/wSokra/status/969679223278505985
// https://github.com/facebook/create-react-app/issues/5358 // https://github.com/facebook/create-react-app/issues/5358
runtimeChunk: { runtimeChunk: {
name: (entrypoint) => `runtime-${entrypoint.name}` name: (entrypoint) => `runtime-${entrypoint.name}`,
} },
}, },
resolve: { resolve: {
// This allows you to set a fallback for where webpack should look for modules. // This allows you to set a fallback for where webpack should look for modules.
...@@ -290,9 +287,9 @@ module.exports = function (webpackEnv) { ...@@ -290,9 +287,9 @@ module.exports = function (webpackEnv) {
// Allows for better profiling with ReactDevTools // Allows for better profiling with ReactDevTools
...(isEnvProductionProfile && { ...(isEnvProductionProfile && {
'react-dom$': 'react-dom/profiling', 'react-dom$': 'react-dom/profiling',
'scheduler/tracing': 'scheduler/tracing-profiling' 'scheduler/tracing': 'scheduler/tracing-profiling',
}), }),
...(modules.webpackAliases || {}) ...(modules.webpackAliases || {}),
}, },
plugins: [ plugins: [
// Adds support for installing with Plug'n'Play, leading to faster installs and adding // Adds support for installing with Plug'n'Play, leading to faster installs and adding
...@@ -303,15 +300,15 @@ module.exports = function (webpackEnv) { ...@@ -303,15 +300,15 @@ module.exports = function (webpackEnv) {
// To fix this, we prevent you from importing files out of src/ -- if you'd like to, // 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. // 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. // Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]) new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
] ],
}, },
resolveLoader: { resolveLoader: {
plugins: [ plugins: [
// Also related to Plug'n'Play, but this time it tells webpack to load its loaders // Also related to Plug'n'Play, but this time it tells webpack to load its loaders
// from the current package. // from the current package.
PnpWebpackPlugin.moduleLoader(module) PnpWebpackPlugin.moduleLoader(module),
] ],
}, },
module: { module: {
strictExportPresence: true, strictExportPresence: true,
...@@ -330,12 +327,12 @@ module.exports = function (webpackEnv) { ...@@ -330,12 +327,12 @@ module.exports = function (webpackEnv) {
cache: true, cache: true,
formatter: require.resolve('react-dev-utils/eslintFormatter'), formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'), eslintPath: require.resolve('eslint'),
resolvePluginsRelativeTo: __dirname resolvePluginsRelativeTo: __dirname,
}, },
loader: require.resolve('eslint-loader') loader: require.resolve('eslint-loader'),
} },
], ],
include: paths.appSrc include: paths.appSrc,
}, },
{ {
// "oneOf" will traverse all following loaders until one will // "oneOf" will traverse all following loaders until one will
...@@ -350,8 +347,8 @@ module.exports = function (webpackEnv) { ...@@ -350,8 +347,8 @@ module.exports = function (webpackEnv) {
loader: require.resolve('url-loader'), loader: require.resolve('url-loader'),
options: { options: {
limit: imageInlineSizeLimit, limit: imageInlineSizeLimit,
name: 'static/media/[name].[hash:8].[ext]' name: PREFIX + 'static/media/[name].[hash:8].[ext]',
} },
}, },
// Process application JS with Babel. // Process application JS with Babel.
// The preset includes JSX, Flow, TypeScript, and some ESnext features. // The preset includes JSX, Flow, TypeScript, and some ESnext features.
...@@ -368,11 +365,11 @@ module.exports = function (webpackEnv) { ...@@ -368,11 +365,11 @@ module.exports = function (webpackEnv) {
{ {
loaderMap: { loaderMap: {
svg: { svg: {
ReactComponent: '@svgr/webpack?-svgo,+titleProp,+ref![path]' ReactComponent: '@svgr/webpack?-svgo,+titleProp,+ref![path]',
} },
} },
} },
] ],
], ],
// 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/ // It enables caching results in ./node_modules/.cache/babel-loader/
...@@ -380,8 +377,8 @@ module.exports = function (webpackEnv) { ...@@ -380,8 +377,8 @@ module.exports = function (webpackEnv) {
cacheDirectory: true, cacheDirectory: true,
// See #6846 for context on why cacheCompression is disabled // See #6846 for context on why cacheCompression is disabled
cacheCompression: false, cacheCompression: false,
compact: isEnvProduction compact: isEnvProduction,
} },
}, },
// Process any JS outside of the app with Babel. // Process any JS outside of the app with Babel.
// Unlike the application JS, we only compile the standard ES features. // Unlike the application JS, we only compile the standard ES features.
...@@ -402,8 +399,8 @@ module.exports = function (webpackEnv) { ...@@ -402,8 +399,8 @@ module.exports = function (webpackEnv) {
// code. Without the options below, debuggers like VSCode // code. Without the options below, debuggers like VSCode
// show incorrect code and set breakpoints on the wrong lines. // show incorrect code and set breakpoints on the wrong lines.
sourceMaps: shouldUseSourceMap, sourceMaps: shouldUseSourceMap,
inputSourceMap: shouldUseSourceMap inputSourceMap: shouldUseSourceMap,
} },
}, },
// "postcss" loader applies autoprefixer to our CSS. // "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies. // "css" loader resolves paths in CSS and adds assets as dependencies.
...@@ -417,13 +414,13 @@ module.exports = function (webpackEnv) { ...@@ -417,13 +414,13 @@ module.exports = function (webpackEnv) {
exclude: cssModuleRegex, exclude: cssModuleRegex,
use: getStyleLoaders({ use: getStyleLoaders({
importLoaders: 1, importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap sourceMap: isEnvProduction && shouldUseSourceMap,
}), }),
// Don't consider CSS imports dead code even if the // Don't consider CSS imports dead code even if the
// containing package claims to have no side effects. // containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this. // Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571 // See https://github.com/webpack/webpack/issues/6571
sideEffects: true sideEffects: true,
}, },
// Adds support for CSS Modules (https://github.com/css-modules/css-modules) // Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css // using the extension .module.css
...@@ -433,9 +430,9 @@ module.exports = function (webpackEnv) { ...@@ -433,9 +430,9 @@ module.exports = function (webpackEnv) {
importLoaders: 1, importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap, sourceMap: isEnvProduction && shouldUseSourceMap,
modules: { modules: {
getLocalIdent: getCSSModuleLocalIdent getLocalIdent: getCSSModuleLocalIdent,
} },
}) }),
}, },
// Opt-in support for SASS (using .scss or .sass extensions). // Opt-in support for SASS (using .scss or .sass extensions).
// By default we support SASS Modules with the // By default we support SASS Modules with the
...@@ -446,11 +443,11 @@ module.exports = function (webpackEnv) { ...@@ -446,11 +443,11 @@ module.exports = function (webpackEnv) {
use: getStyleLoaders( use: getStyleLoaders(
{ {
importLoaders: 2, importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap sourceMap: isEnvProduction && shouldUseSourceMap,
}, },
'less-loader' 'less-loader'
), ),
sideEffects: true sideEffects: true,
}, },
{ {
test: lessModuleRegex, test: lessModuleRegex,
...@@ -459,10 +456,10 @@ module.exports = function (webpackEnv) { ...@@ -459,10 +456,10 @@ module.exports = function (webpackEnv) {
importLoaders: 2, importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap, sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true, modules: true,
getLocalIdent: getCSSModuleLocalIdent getLocalIdent: getCSSModuleLocalIdent,
}, },
'less-loader' 'less-loader'
) ),
}, },
// "file" loader makes sure those assets get served by WebpackDevServer. // "file" loader makes sure those assets get served by WebpackDevServer.
// When you `import` an asset, you get its (virtual) filename. // When you `import` an asset, you get its (virtual) filename.
...@@ -477,14 +474,14 @@ module.exports = function (webpackEnv) { ...@@ -477,14 +474,14 @@ module.exports = function (webpackEnv) {
// by webpacks internal loaders. // by webpacks internal loaders.
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/], exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
options: { options: {
name: 'static/media/[name].[hash:8].[ext]' name: PREFIX + 'static/media/[name].[hash:8].[ext]',
} },
} },
// ** STOP ** Are you adding a new loader? // ** STOP ** Are you adding a new loader?
// Make sure to add the new loader(s) before the "file" loader. // Make sure to add the new loader(s) before the "file" loader.
] ],
} },
] ],
}, },
plugins: [ plugins: [
// Generates an `index.html` file with the <script> injected. // Generates an `index.html` file with the <script> injected.
...@@ -493,7 +490,7 @@ module.exports = function (webpackEnv) { ...@@ -493,7 +490,7 @@ module.exports = function (webpackEnv) {
{}, {},
{ {
inject: true, inject: true,
template: paths.appHtml template: paths.appHtml,
}, },
isEnvProduction isEnvProduction
? { ? {
...@@ -507,8 +504,8 @@ module.exports = function (webpackEnv) { ...@@ -507,8 +504,8 @@ module.exports = function (webpackEnv) {
keepClosingSlash: true, keepClosingSlash: true,
minifyJS: true, minifyJS: true,
minifyCSS: true, minifyCSS: true,
minifyURLs: true minifyURLs: true,
} },
} }
: undefined : undefined
) )
...@@ -547,8 +544,8 @@ module.exports = function (webpackEnv) { ...@@ -547,8 +544,8 @@ module.exports = function (webpackEnv) {
new MiniCssExtractPlugin({ new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output // Options similar to the same options in webpackOptions.output
// both options are optional // both options are optional
filename: 'static/css/[name].[contenthash:8].css', filename: PREFIX + 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css' chunkFilename: PREFIX + 'static/css/[name].[contenthash:8].chunk.css',
}), }),
// Generate an asset manifest file with the following content: // Generate an asset manifest file with the following content:
// - "files" key: Mapping of all asset filenames to their corresponding // - "files" key: Mapping of all asset filenames to their corresponding
...@@ -561,16 +558,16 @@ module.exports = function (webpackEnv) { ...@@ -561,16 +558,16 @@ module.exports = function (webpackEnv) {
publicPath: paths.publicUrlOrPath, publicPath: paths.publicUrlOrPath,
generate: (seed, files, entrypoints) => { generate: (seed, files, entrypoints) => {
const manifestFiles = files.reduce((manifest, file) => { const manifestFiles = files.reduce((manifest, file) => {
manifest[file.name] = file.path manifest[file.name] = file.path;
return manifest return manifest;
}, seed) }, seed);
const entrypointFiles = entrypoints.main.filter((fileName) => !fileName.endsWith('.map')) const entrypointFiles = entrypoints.main.filter((fileName) => !fileName.endsWith('.map'));
return { return {
files: manifestFiles, files: manifestFiles,
entrypoints: entrypointFiles entrypoints: entrypointFiles,
} };
} },
}), }),
// Moment.js is an extremely popular library that bundles large locale files // Moment.js is an extremely popular library that bundles large locale files
...@@ -594,14 +591,14 @@ module.exports = function (webpackEnv) { ...@@ -594,14 +591,14 @@ module.exports = function (webpackEnv) {
// as they're likely a resource and not a SPA route. // as they're likely a resource and not a SPA route.
// URLs containing a "?" character won't be blacklisted as they're likely // URLs containing a "?" character won't be blacklisted as they're likely
// a route with query params (e.g. auth callbacks). // a route with query params (e.g. auth callbacks).
new RegExp('/[^/?]+\\.[^/]+$') new RegExp('/[^/?]+\\.[^/]+$'),
] ],
}), }),
// TypeScript type checking // TypeScript type checking
useTypeScript && useTypeScript &&
new ForkTsCheckerWebpackPlugin({ new ForkTsCheckerWebpackPlugin({
typescript: resolve.sync('typescript', { typescript: resolve.sync('typescript', {
basedir: paths.appNodeModules basedir: paths.appNodeModules,
}), }),
async: isEnvDevelopment, async: isEnvDevelopment,
useTypescriptIncrementalApi: true, useTypescriptIncrementalApi: true,
...@@ -612,11 +609,8 @@ module.exports = function (webpackEnv) { ...@@ -612,11 +609,8 @@ module.exports = function (webpackEnv) {
reportFiles: ['**', '!**/__tests__/**', '!**/?(*.)(spec|test).*', '!**/src/setupProxy.*', '!**/src/setupTests.*'], reportFiles: ['**', '!**/__tests__/**', '!**/?(*.)(spec|test).*', '!**/src/setupProxy.*', '!**/src/setupTests.*'],
silent: true, silent: true,
// The formatter is invoked directly in WebpackDevServerUtils during development // The formatter is invoked directly in WebpackDevServerUtils during development
formatter: isEnvProduction ? typescriptFormatter : undefined formatter: isEnvProduction ? typescriptFormatter : undefined,
}) }),
// new vConsolePlugin({
// enable: (process.env.DEPLOY_ENV === 'prod' || process.env.DEPLOY_ENV === 'beta') ? false : true
// })
].filter(Boolean), ].filter(Boolean),
// Some libraries import Node modules but don't use them in the browser. // 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. // Tell webpack to provide empty mocks for them so importing them works.
...@@ -628,10 +622,10 @@ module.exports = function (webpackEnv) { ...@@ -628,10 +622,10 @@ module.exports = function (webpackEnv) {
http2: 'empty', http2: 'empty',
net: 'empty', net: 'empty',
tls: 'empty', tls: 'empty',
child_process: 'empty' child_process: 'empty',
}, },
// Turn off performance processing because we utilize // Turn off performance processing because we utilize
// our own hints via the FileSizeReporter // our own hints via the FileSizeReporter
performance: false performance: false,
} };
} };
...@@ -110,6 +110,7 @@ ...@@ -110,6 +110,7 @@
"start:dev1": "cross-env DEPLOY_ENV=dev node scripts/start.js", "start:dev1": "cross-env DEPLOY_ENV=dev node scripts/start.js",
"start:rc": "cross-env DEPLOY_ENV=rc node scripts/start.js", "start:rc": "cross-env DEPLOY_ENV=rc node scripts/start.js",
"start:gray": "cross-env DEPLOY_ENV=gray node scripts/start.js", "start:gray": "cross-env DEPLOY_ENV=gray node scripts/start.js",
"start:prod": "cross-env DEPLOY_ENV=prod node scripts/start.js",
"build:dev": "cross-env DEPLOY_ENV=dev node scripts/build.js", "build:dev": "cross-env DEPLOY_ENV=dev node scripts/build.js",
"build:dev1": "cross-env DEPLOY_ENV=dev node scripts/build.js", "build:dev1": "cross-env DEPLOY_ENV=dev node scripts/build.js",
"build:rc": "cross-env DEPLOY_ENV=rc node scripts/build.js", "build:rc": "cross-env DEPLOY_ENV=rc node scripts/build.js",
...@@ -143,7 +144,6 @@ ...@@ -143,7 +144,6 @@
"@types/ali-oss": "^6.0.5", "@types/ali-oss": "^6.0.5",
"@types/jquery": "^3.5.4", "@types/jquery": "^3.5.4",
"ali-oss": "^6.12.0", "ali-oss": "^6.12.0",
"react-sortable-hoc": "^1.11.0", "react-sortable-hoc": "^1.11.0"
"vconsole-webpack-plugin": "^1.5.2"
} }
} }
...@@ -88,8 +88,6 @@ class ImgClipModal extends React.Component { ...@@ -88,8 +88,6 @@ class ImgClipModal extends React.Component {
checkOrientation={false} checkOrientation={false}
cropBoxResizable={false} cropBoxResizable={false}
center={true} center={true}
// minCropBoxWidth={cropBoxWidth}
// minCropBoxHeight={cropBoxHeight}
cropBoxMovable={false} cropBoxMovable={false}
dragMode='move' dragMode='move'
onInitialized={(instance) => { onInitialized={(instance) => {
...@@ -108,13 +106,6 @@ class ImgClipModal extends React.Component { ...@@ -108,13 +106,6 @@ class ImgClipModal extends React.Component {
console.log("ratio++++",ratio); console.log("ratio++++",ratio);
this.state.cropperInstace.setCanvasData({width:500}); this.state.cropperInstace.setCanvasData({width:500});
const that = this; const that = this;
// const containerData = this.state.cropperInstace.getContainerData();
// // Zoom to 50% from the center of the container.
// this.state.cropperInstace.zoomTo(.5, {
// x: containerData.width / 2,
// y: containerData.height / 2,
// });
document.querySelector('.cropper__box').addEventListener('dblclick', function (e) { document.querySelector('.cropper__box').addEventListener('dblclick', function (e) {
that.state.cropperInstace.rotate(90) that.state.cropperInstace.rotate(90)
......
...@@ -41,6 +41,9 @@ ...@@ -41,6 +41,9 @@
.ant-switch-small:after { .ant-switch-small:after {
top: 0.3px; top: 0.3px;
} }
tbody .ant-switch-small {
min-width: 32px;
}
.ant-pagination { .ant-pagination {
display: inline !important; display: inline !important;
......
...@@ -14,8 +14,14 @@ ...@@ -14,8 +14,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<title>小麦企学院_搭建数字化企业大学</title> <title>小麦企学院_搭建数字化企业大学</title>
<meta name="description" content="小麦企学院,一站式企业培训数字化服务商,通过“工具+内容”,帮助企业快速从0到1搭建数字化培训体系,并让整个培训过程可视化,降低培训成本,提升培训效率。"> <meta
<meta name="keywords" content="企业培训,员工培训,企业大学,内训,外训,培训计划,企培,企训,资料云盘,定制培训计划,学习数据,资料共享,数字化,培训saas,,企业学院平台,酷学院,小鹅通,云学堂,时代光华,云课堂,魔学院,云大学,米知云"> name="description"
content="小麦企学院,一站式企业培训数字化服务商,通过“工具+内容”,帮助企业快速从0到1搭建数字化培训体系,并让整个培训过程可视化,降低培训成本,提升培训效率。"
/>
<meta
name="keywords"
content="企业培训,员工培训,企业大学,内训,外训,培训计划,企培,企训,资料云盘,定制培训计划,学习数据,资料共享,数字化,培训saas,,企业学院平台,酷学院,小鹅通,云学堂,时代光华,云课堂,魔学院,云大学,米知云"
/>
<!-- <link rel="apple-touch-icon" href="../src/common/images/logo.png" /> --> <!-- <link rel="apple-touch-icon" href="../src/common/images/logo.png" /> -->
<link rel="shortcut icon" href="https://image.xiaomaiketang.com/xm/c4KiP2epBP.png" /> <link rel="shortcut icon" href="https://image.xiaomaiketang.com/xm/c4KiP2epBP.png" />
...@@ -45,6 +51,7 @@ ...@@ -45,6 +51,7 @@
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div> <div id="root"></div>
<!-- <!--
This HTML file is a template. This HTML file is a template.
If you open it directly in the browser, you will see an empty page. If you open it directly in the browser, you will see an empty page.
...@@ -55,166 +62,5 @@ ...@@ -55,166 +62,5 @@
To begin the development, run `npm start` or `yarn start`. To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`. To create a production bundle, use `npm run build` or `yarn build`.
--> -->
<script>
// (function (root, factory) {
// if (typeof define === 'function' && define.amd) {
// // AMD. Register as an anonymous module.
// define([], factory);
// } else if (typeof exports === 'object') {
// // Node. Does not work with strict CommonJS, but
// // only CommonJS-like environments that support module.exports,
// // like Node.
// module.exports = factory();
// } else {
// // Browser globals (root is window)
// root.download = factory();
// }
// })(this, function () {
// return function download(data, strFileName, strMimeType) {
// var self = window, // this script is only for browsers anyway...
// defaultMime = 'application/octet-stream', // this default mime also triggers iframe downloads
// mimeType = strMimeType || defaultMime,
// payload = data,
// url = !strFileName && !strMimeType && payload,
// anchor = document.createElement('a'),
// toString = function (a) {
// return String(a);
// },
// myBlob = self.Blob || self.MozBlob || self.WebKitBlob || toString,
// fileName = strFileName || 'download',
// blob,
// reader;
// myBlob = myBlob.call ? myBlob.bind(self) : Blob;
// if (String(this) === 'true') {
// //reverse arguments, allowing download.bind(true, "text/xml", "export.xml") to act as a callback
// payload = [payload, mimeType];
// mimeType = payload[0];
// payload = payload[1];
// }
// if (url && url.length < 2048) {
// // if no filename and no mime, assume a url was passed as the only argument
// fileName = url.split('/').pop().split('?')[0];
// anchor.href = url; // assign href prop to temp anchor
// if (anchor.href.indexOf(url) !== -1) {
// // if the browser determines that it's a potentially valid url path:
// var ajax = new XMLHttpRequest();
// ajax.open('GET', url, true);
// ajax.responseType = 'blob';
// ajax.onload = function (e) {
// download(e.target.response, fileName, defaultMime);
// };
// setTimeout(function () {
// ajax.send();
// }, 0); // allows setting custom ajax headers using the return:
// return ajax;
// } // end if valid url?
// } // end if url?
// //go ahead and download dataURLs right away
// if (/^data\:[\w+\-]+\/[\w+\-]+[,;]/.test(payload)) {
// if (payload.length > 1024 * 1024 * 1.999 && myBlob !== toString) {
// payload = dataUrlToBlob(payload);
// mimeType = payload.type || defaultMime;
// } else {
// return navigator.msSaveBlob // IE10 can't do a[download], only Blobs:
// ? navigator.msSaveBlob(dataUrlToBlob(payload), fileName)
// : saver(payload); // everyone else can save dataURLs un-processed
// }
// } //end if dataURL passed?
// blob = payload instanceof myBlob ? payload : new myBlob([payload], { type: mimeType });
// function dataUrlToBlob(strUrl) {
// var parts = strUrl.split(/[:;,]/),
// type = parts[1],
// decoder = parts[2] == 'base64' ? atob : decodeURIComponent,
// binData = decoder(parts.pop()),
// mx = binData.length,
// i = 0,
// uiArr = new Uint8Array(mx);
// for (i; i < mx; ++i) uiArr[i] = binData.charCodeAt(i);
// return new myBlob([uiArr], { type: type });
// }
// function saver(url, winMode) {
// if ('download' in anchor) {
// //html5 A[download]
// anchor.href = url;
// anchor.setAttribute('download', fileName);
// anchor.className = 'download-js-link';
// anchor.innerHTML = 'downloading...';
// anchor.style.display = 'none';
// document.body.appendChild(anchor);
// setTimeout(function () {
// anchor.click();
// document.body.removeChild(anchor);
// if (winMode === true) {
// setTimeout(function () {
// self.URL.revokeObjectURL(anchor.href);
// }, 250);
// }
// }, 66);
// return true;
// }
// // handle non-a[download] safari as best we can:
// if (/(Version)\/(\d+)\.(\d+)(?:\.(\d+))?.*Safari\//.test(navigator.userAgent)) {
// url = url.replace(/^data:([\w\/\-\+]+)/, defaultMime);
// if (!window.open(url)) {
// // popup blocked, offer direct download:
// if (confirm('Displaying New Document\n\nUse Save As... to download, then click back to return to this page.')) {
// location.href = url;
// }
// }
// return true;
// }
// //do iframe dataURL download (old ch+FF):
// var f = document.createElement('iframe');
// document.body.appendChild(f);
// if (!winMode) {
// // force a mime that will download:
// url = 'data:' + url.replace(/^data:([\w\/\-\+]+)/, defaultMime);
// }
// f.src = url;
// setTimeout(function () {
// document.body.removeChild(f);
// }, 333);
// } //end saver
// if (navigator.msSaveBlob) {
// // IE10+ : (has Blob, but not a[download] or URL)
// return navigator.msSaveBlob(blob, fileName);
// }
// if (self.URL) {
// // simple fast and modern way using Blob and URL:
// saver(self.URL.createObjectURL(blob), true);
// } else {
// // handle non-Blob()+non-URL browsers:
// if (typeof blob === 'string' || blob.constructor === toString) {
// try {
// return saver('data:' + mimeType + ';base64,' + self.btoa(blob));
// } catch (y) {
// return saver('data:' + mimeType + ',' + encodeURIComponent(blob));
// }
// }
// // Blob but not URL support:
// reader = new FileReader();
// reader.onload = function (e) {
// saver(this.result);
// };
// reader.readAsDataURL(blob);
// }
// return true;
// }; /* end download() */
// });
</script>
</body> </body>
</html> </html>
...@@ -2,16 +2,13 @@ ...@@ -2,16 +2,13 @@
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-04-27 20:35:34 * @Date: 2020-04-27 20:35:34
* @LastEditors: Please set LastEditors * @LastEditors: Please set LastEditors
* @LastEditTime: 2021-07-04 16:59:14 * @LastEditTime: 2021-07-08 19:22:18
* @Description: * @Description:
*/ */
import React, { useEffect, useState } from "react"; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { HashRouter } from 'react-router-dom';
import { ConfigProvider } from 'antd';
import { createHashHistory } from 'history'; import { createHashHistory } from 'history';
import zh_CN from 'antd/es/locale/zh_CN';
import _ from 'underscore'; import _ from 'underscore';
import { RootRouter } from './routes/index'; import { RootRouter } from './routes/index';
import 'antd/dist/antd.less'; import 'antd/dist/antd.less';
...@@ -21,35 +18,33 @@ import '@/core/downloads'; ...@@ -21,35 +18,33 @@ import '@/core/downloads';
import '@/core/function'; import '@/core/function';
import '@/core/xmTD'; import '@/core/xmTD';
import User from '@/common/js/user'; import User from '@/common/js/user';
import Service from "@/common/js/service"; import Service from '@/common/js/service';
import BaseService from '@/domains/basic-domain/baseService'; import BaseService from '@/domains/basic-domain/baseService';
declare var getParameterByName: any; declare var getParameterByName: any;
declare var window: any; declare var window: any;
window.currentStoreUserInfo = {} window.currentStoreUserInfo = {};
const history = createHashHistory(); const history = createHashHistory();
window.RCHistory = _.extend({}, history, { window.RCHistory = _.extend({}, history, {
push: (obj: any) => { push: (obj: any) => {
history.push(obj) history.push(obj);
}, },
pushState: (obj: any) => { pushState: (obj: any) => {
history.push(obj) history.push(obj);
}, },
pushStateWithStatus: (obj: any) => { pushStateWithStatus: (obj: any) => {
history.push(obj) history.push(obj);
}, },
goBack: history.goBack, goBack: history.goBack,
location: history.location, location: history.location,
replace: (obj: any) => { replace: (obj: any) => {
history.replace(obj) history.replace(obj);
} },
}); });
function mount() { function mount() {
ReactDOM.render( ReactDOM.render(<RootRouter />, document.getElementById('root'));
<RootRouter />,
document.getElementById('root'));
} }
function isWeiXin() { function isWeiXin() {
...@@ -57,43 +52,43 @@ function isWeiXin() { ...@@ -57,43 +52,43 @@ function isWeiXin() {
return ua.indexOf('micromessenger') > 0; return ua.indexOf('micromessenger') > 0;
} }
console.log(isWeiXin(), 'isWeiXin()') console.log(isWeiXin(), 'isWeiXin()');
if (getParameterByName('code') && isWeiXin()) { if (getParameterByName('code') && isWeiXin()) {
Service.Hades('anon/hades/wXWorkLogin', { Service.Hades('anon/hades/wXWorkLogin', {
appTermEnum: 'XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN', appTermEnum: 'XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN',
code: getParameterByName('code') code: getParameterByName('code'),
}).then((res) => { }).then((res) => {
User.setUserId(res.result.loginInfo.userId); User.setUserId(res.result.loginInfo.userId);
User.setToken(res.result.loginInfo.xmToken); User.setToken(res.result.loginInfo.xmToken);
User.setEnterpriseId(res.result.enterpriseId); User.setEnterpriseId(res.result.enterpriseId);
window.currentStoreUserInfo = {} window.currentStoreUserInfo = {};
window.currentStoreUserInfo.userId = res.result.loginInfo.userId; window.currentStoreUserInfo.userId = res.result.loginInfo.userId;
window.currentStoreUserInfo.token = res.result.loginInfo.xmToken; window.currentStoreUserInfo.token = res.result.loginInfo.xmToken;
window.currentStoreUserInfo.enterpriseId = res.result.enterpriseId; window.currentStoreUserInfo.enterpriseId = res.result.enterpriseId;
mount() mount();
}) });
} else if(getParameterByName('from') === 'customer' && getParameterByName('enterpriseId') && getParameterByName('userId')){ } else if (getParameterByName('from') === 'customer' && getParameterByName('enterpriseId') && getParameterByName('userId')) {
User.setCustomerStoreId(getParameterByName('storeId')); User.setCustomerStoreId(getParameterByName('storeId'));
getWXWorkLoginNoCheck(getParameterByName('enterpriseId'),getParameterByName('userId')); //从C端跳转过来的学院自动执行免登录 getWXWorkLoginNoCheck(getParameterByName('enterpriseId'), getParameterByName('userId')); //从C端跳转过来的学院自动执行免登录
}else{ } else {
mount() mount();
} }
function getWXWorkLoginNoCheck(enterpriseId:string,userId:string) { function getWXWorkLoginNoCheck(enterpriseId: string, userId: string) {
const params = { const params = {
appTermEnum: 'XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN', appTermEnum: 'XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN',
enterpriseId, enterpriseId,
userId, userId,
}; };
BaseService.getWXWorkLoginNoCheck(params).then((res:any) => { BaseService.getWXWorkLoginNoCheck(params).then((res: any) => {
User.setUserId(res.result.loginInfo.userId) User.setUserId(res.result.loginInfo.userId);
User.setToken(res.result.loginInfo.xmToken) User.setToken(res.result.loginInfo.xmToken);
User.setEnterpriseId(res.result.enterpriseId) User.setEnterpriseId(res.result.enterpriseId);
window.currentStoreUserInfo = {} window.currentStoreUserInfo = {};
window.currentStoreUserInfo.userId = res.result.loginInfo.userId; window.currentStoreUserInfo.userId = res.result.loginInfo.userId;
window.currentStoreUserInfo.token = res.result.loginInfo.xmToken; window.currentStoreUserInfo.token = res.result.loginInfo.xmToken;
window.currentStoreUserInfo.enterpriseId = res.result.enterpriseId; window.currentStoreUserInfo.enterpriseId = res.result.enterpriseId;
User.setIdentifier(res.result.identifier) User.setIdentifier(res.result.identifier);
mount(); mount();
}); });
} }
...@@ -53,7 +53,6 @@ class CollegeInfoPage extends React.Component { ...@@ -53,7 +53,6 @@ class CollegeInfoPage extends React.Component {
}) })
} }
handleSelectCover = (file)=> { handleSelectCover = (file)=> {
// this.uploadImage(file);
this.setState({ this.setState({
visible: true, visible: true,
imageFile:file imageFile:file
......
...@@ -83,7 +83,6 @@ class AddLiveBasic extends React.Component { ...@@ -83,7 +83,6 @@ class AddLiveBasic extends React.Component {
visible: true, visible: true,
imageFile:file imageFile:file
}); });
// this.uploadImage(file);
} }
......
...@@ -84,9 +84,4 @@ ...@@ -84,9 +84,4 @@
} }
.preview-url-box{ .preview-url-box{
overflow: hidden; overflow: hidden;
// img{
// width:500px !important;
// height:282px !important;
// transform:none !important;
// }
} }
\ No newline at end of file
...@@ -175,7 +175,10 @@ class AddLiveClass extends React.Component { ...@@ -175,7 +175,10 @@ class AddLiveClass extends React.Component {
<Tooltip <Tooltip
overlayStyle={{maxWidth: 300, zIndex: '9999'}} overlayStyle={{maxWidth: 300, zIndex: '9999'}}
title={<div style={{width: '266px'}}>支持按上课日期批量创建直播课,创建后按“课程名称_日期”命名,例如:<br/>张三的语文课_9月18日<br/>张三的语文课_9月19日......</div>}> title={<div style={{width: '266px'}}>支持按上课日期批量创建直播课,创建后按“课程名称_日期”命名,例如:<br/>张三的语文课_9月18日<br/>张三的语文课_9月19日......</div>}>
<span className="iconfont">&#xe61d;</span> <span
style={{ color: "rgba(191, 191, 191, 1)",fontWeight: 400 }}
className="iconfont"
>&#xe61d;</span>
</Tooltip> </Tooltip>
</span> </span>
<div> <div>
......
...@@ -125,7 +125,7 @@ class LiveCourseList extends React.Component { ...@@ -125,7 +125,7 @@ class LiveCourseList extends React.Component {
columns = [ columns = [
{ {
title: '直播课', title: '直播课',
width: '23%',
key: 'course', key: 'course',
fixed: 'left', fixed: 'left',
dataIndex: 'courseName', dataIndex: 'courseName',
...@@ -284,6 +284,7 @@ class LiveCourseList extends React.Component { ...@@ -284,6 +284,7 @@ class LiveCourseList extends React.Component {
render: (val, item, index) => { render: (val, item, index) => {
return ( return (
<Switch <Switch
size='small'
checked={item.shelfState === 'YES'} checked={item.shelfState === 'YES'}
defaultChecked={item.shelfState === 'YES' ? true : false} defaultChecked={item.shelfState === 'YES' ? true : false}
onChange={(checked) => this.changeShelfState(index, item, checked)} onChange={(checked) => this.changeShelfState(index, item, checked)}
......
...@@ -53,7 +53,7 @@ class LiveCourseOpt extends React.Component { ...@@ -53,7 +53,7 @@ class LiveCourseOpt extends React.Component {
{ userRole !== "CloudLecturer" && { userRole !== "CloudLecturer" &&
<Button type="primary" onClick={this.handleCreateLiveCouese}>新建直播课</Button> <Button type="primary" onClick={this.handleCreateLiveCouese}>新建直播课</Button>
} }
{!this.state.isMac && <Button onClick={this.handleDownloadClient}>下载直播客户端</Button>} <Button onClick={this.handleDownloadClient}>下载直播客户端</Button>
</div> </div>
</div> </div>
) )
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-05 10:07:47 * @Date: 2020-08-05 10:07:47
* @LastEditors: Please set LastEditors * @LastEditors: Please set LastEditors
* @LastEditTime: 2021-07-06 14:46:40 * @LastEditTime: 2021-07-08 19:32:50
* @Description: 图文课新增/编辑页 * @Description: 图文课新增/编辑页
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -280,7 +280,6 @@ class AddGraphicsCourse extends React.Component { ...@@ -280,7 +280,6 @@ class AddGraphicsCourse extends React.Component {
}; };
handleSelectCover = (file) => { handleSelectCover = (file) => {
// this.uploadCoverImage(file);
this.setState({ this.setState({
visible: true, visible: true,
imageFile:file imageFile:file
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
import User from '@/common/js/user'; import User from '@/common/js/user';
import college from '@/common/lottie/college'; import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components'; import { PageControl, XMTable } from '@/components';
import { appId, LIVE_SHARE } from '@/domains/course-domain/constants'; import { LIVE_SHARE } from '@/domains/course-domain/constants';
import CourseService from '@/domains/course-domain/CourseService'; import CourseService from '@/domains/course-domain/CourseService';
import ShareLiveModal from '@/modules/course-manage/modal/ShareLiveModal'; import ShareLiveModal from '@/modules/course-manage/modal/ShareLiveModal';
import { Dropdown, message, Modal, Switch, Tooltip } from 'antd'; import { Dropdown, message, Modal, Switch, Tooltip } from 'antd';
...@@ -57,7 +57,7 @@ class GraphicsCourseList extends React.Component { ...@@ -57,7 +57,7 @@ class GraphicsCourseList extends React.Component {
handlePlanName = (planArray) => { handlePlanName = (planArray) => {
let planStr = ''; let planStr = '';
planArray.map((item, index) => { planArray.forEach((item, index) => {
if (index < planArray.length - 1) { if (index < planArray.length - 1) {
planStr = planStr + item.planName + '、'; planStr = planStr + item.planName + '、';
} else { } else {
...@@ -76,18 +76,21 @@ class GraphicsCourseList extends React.Component { ...@@ -76,18 +76,21 @@ class GraphicsCourseList extends React.Component {
width: 321, width: 321,
fixed: 'left', fixed: 'left',
render: (val, record) => { render: (val, record) => {
const { coverUrl, scheduleVideoUrl } = record; const { coverUrl } = record;
return ( return (
<div className='record__item'> <div className='record__item'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */} {/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
<img className='course-cover' src={coverUrl || defaultCoverUrl} /> <img className='course-cover' src={coverUrl || defaultCoverUrl} alt='' />
{record.courseName.length > 25 ? ( <Choose>
<Tooltip title={record.courseName}> <When condition={record.courseName.length > 25}>
<Tooltip title={record.courseName}>
<div className='course-name'>{record.courseName}</div>
</Tooltip>
</When>
<Otherwise>
<div className='course-name'>{record.courseName}</div> <div className='course-name'>{record.courseName}</div>
</Tooltip> </Otherwise>
) : ( </Choose>
<div className='course-name'>{record.courseName}</div>
)}
</div> </div>
); );
}, },
...@@ -144,12 +147,14 @@ class GraphicsCourseList extends React.Component { ...@@ -144,12 +147,14 @@ class GraphicsCourseList extends React.Component {
width: 120, width: 120,
dataIndex: 'courseware', dataIndex: 'courseware',
render: (val, item, index) => { render: (val, item, index) => {
return ( return (
<Switch <Switch
checked={item.shelfState === "YES"} size='small'
defaultChecked={item.shelfState==="YES"?true:false} checked={item.shelfState === 'YES'}
onChange={(checked)=>this.changeShelfState(index,item,checked)}/> defaultChecked={item.shelfState === 'YES' ? true : false}
) onChange={(checked) => this.changeShelfState(index, item, checked)}
/>
);
}, },
}, },
{ {
...@@ -168,7 +173,7 @@ class GraphicsCourseList extends React.Component { ...@@ -168,7 +173,7 @@ class GraphicsCourseList extends React.Component {
dataIndex: 'created', dataIndex: 'created',
sorter: true, sorter: true,
render: (val) => { render: (val) => {
return formatDate('YYYY-MM-DD H:i', val); return window.formatDate('YYYY-MM-DD H:i', val);
}, },
}, },
{ {
...@@ -178,7 +183,7 @@ class GraphicsCourseList extends React.Component { ...@@ -178,7 +183,7 @@ class GraphicsCourseList extends React.Component {
dataIndex: 'updated', dataIndex: 'updated',
sorter: true, sorter: true,
render: (val) => { render: (val) => {
return formatDate('YYYY-MM-DD H:i', val); return window.formatDate('YYYY-MM-DD H:i', val);
}, },
}, },
{ {
...@@ -189,19 +194,22 @@ class GraphicsCourseList extends React.Component { ...@@ -189,19 +194,22 @@ class GraphicsCourseList extends React.Component {
render: (val, record) => { render: (val, record) => {
return ( return (
<div className='related-task'> <div className='related-task'>
{record.relatedPlanList ? ( <Choose>
<Tooltip title={this.handlePlanName(record.relatedPlanList)} placement='top' arrowPointAtCenter> <When condition={record.relatedPlanList}>
{record.relatedPlanList.map((item, index) => { <Tooltip title={this.handlePlanName(record.relatedPlanList)} placement='top' arrowPointAtCenter>
return ( {record.relatedPlanList.map((item, index) => {
<span> return (
{item.planName} {index < record.relatedPlanList.length - 1 && <span></span>}{' '} <span>
</span> {item.planName} {index < record.relatedPlanList.length - 1 && <span></span>}{' '}
); </span>
})} );
</Tooltip> })}
) : ( </Tooltip>
<span></span> </When>
)} <Otherwise>
<span></span>
</Otherwise>
</Choose>
</div> </div>
); );
}, },
...@@ -297,7 +305,7 @@ class GraphicsCourseList extends React.Component { ...@@ -297,7 +305,7 @@ class GraphicsCourseList extends React.Component {
className='operate__item' className='operate__item'
key='edit' key='edit'
onClick={() => { onClick={() => {
RCHistory.push(`/create-graphics-course?type=edit&id=${item.id}`); window.RCHistory.push(`/create-graphics-course?type=edit&id=${item.id}`);
}}> }}>
编辑 编辑
</div> </div>
...@@ -305,17 +313,17 @@ class GraphicsCourseList extends React.Component { ...@@ -305,17 +313,17 @@ class GraphicsCourseList extends React.Component {
删除 删除
</div> </div>
</div> </div>
) );
} };
//改变上架状态 //改变上架状态
changeShelfState = (index,item,checked) =>{ changeShelfState = (index, item, checked) => {
let _shelfState = checked ? "YES" : "NO" let _shelfState = checked ? 'YES' : 'NO';
// if(_shelfState==='NO'){ // if(_shelfState==='NO'){
// _shelfState = "YES"; // _shelfState = "YES";
// }else{ // }else{
// _shelfState = "NO" // _shelfState = "NO"
// } // }
const params={ const params = {
courseId: item.id, courseId: item.id,
shelfState: _shelfState, shelfState: _shelfState,
}; };
...@@ -326,7 +334,7 @@ class GraphicsCourseList extends React.Component { ...@@ -326,7 +334,7 @@ class GraphicsCourseList extends React.Component {
} else { } else {
message.success('已取消展示'); message.success('已取消展示');
} }
this.props.changeShelfState(index,_shelfState) this.props.changeShelfState(index, _shelfState);
} }
}); });
}; };
...@@ -355,8 +363,6 @@ class GraphicsCourseList extends React.Component { ...@@ -355,8 +363,6 @@ class GraphicsCourseList extends React.Component {
// 显示分享弹窗 // 显示分享弹窗
handleShowShareModal = (record, needStr = false) => { handleShowShareModal = (record, needStr = false) => {
const { id, scheduleVideoUrl } = record; const { id, scheduleVideoUrl } = record;
const _appId = appId;
const htmlUrl = `${LIVE_SHARE}graphics_detail/${id}?id=${User.getStoreId()}`; const htmlUrl = `${LIVE_SHARE}graphics_detail/${id}?id=${User.getStoreId()}`;
const longUrl = htmlUrl; const longUrl = htmlUrl;
const { coverUrl, courseName } = record; const { coverUrl, courseName } = record;
......
...@@ -8,80 +8,65 @@ ...@@ -8,80 +8,65 @@
*/ */
import React from 'react'; import React from 'react';
import { import { Button, Input, Radio, message, Modal, TreeSelect, Select, Switch, TimePicker, InputNumber, Tooltip } from 'antd';
Button,
Input,
Radio,
message,
Modal,
TreeSelect,
Select,
Switch,
TimePicker,
InputNumber,
Tooltip,
} from 'antd';
import $ from 'jquery'; import $ from 'jquery';
import Bus from '@/core/bus';
import RangePicker from "@/modules/common/DateRangePicker"; import RangePicker from '@/modules/common/DateRangePicker';
import ShowTips from "@/components/ShowTips"; import ShowTips from '@/components/ShowTips';
import Breadcrumbs from "@/components/Breadcrumbs"; import Breadcrumbs from '@/components/Breadcrumbs';
import SelectStudent from '../modal/select-student';
import SelectPrepareFileModal from '../../prepare-lesson/modal/SelectPrepareFileModal'; import SelectPrepareFileModal from '../../prepare-lesson/modal/SelectPrepareFileModal';
import PreviewOfflineModal from './modal/PreviewOfflineModal'; import PreviewOfflineModal from './modal/PreviewOfflineModal';
import StoreService from "@/domains/store-domain/storeService"; import StoreService from '@/domains/store-domain/storeService';
import Service from '@/common/js/service'; import Service from '@/common/js/service';
import { randomString } from '@/domains/basic-domain/utils'; import { randomString } from '@/domains/basic-domain/utils';
import User from '@/common/js/user'; import User from '@/common/js/user';
import _ from "underscore"; import _ from 'underscore';
import moment from 'moment'; import moment from 'moment';
import Upload from '@/core/upload'; import Upload from '@/core/upload';
import GraphicsEditor from '../components/GraphicsEditor'; import GraphicsEditor from '../components/GraphicsEditor';
import MultipleDatePicker from '@/components/MultipleDatePicker'; import MultipleDatePicker from '@/components/MultipleDatePicker';
import ImgClipModal from '@/components/ImgClipModal' import ImgClipModal from '@/components/ImgClipModal';
import './AddOfflineCourse.less'; import './AddOfflineCourse.less';
const { Option } = Select; const { Option } = Select;
const defaultCoverUrl = 'https://image.xiaomaiketang.com/xm/pxbWKsYA87.png'; const defaultCoverUrl = 'https://image.xiaomaiketang.com/xm/pxbWKsYA87.png';
let cutFlag = false;
const unitList = [ const unitList = [
{ key: 'HOUR', value: '小时' }, { key: 'HOUR', value: '小时' },
{ key: 'MINUTE', value: '分钟' }, { key: 'MINUTE', value: '分钟' },
] ];
class AddOfflineCourse extends React.Component { class AddOfflineCourse extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
const courseId = getParameterByName("id"); const courseId = window.getParameterByName('id');
const pageType = getParameterByName("type"); const pageType = window.getParameterByName('type');
this.state = { this.state = {
courseId, // 线下课ID,编辑的时候从URL上带过来 courseId, // 线下课ID,编辑的时候从URL上带过来
pageType, // 页面类型: add->新建 edit->编辑 pageType, // 页面类型: add->新建 edit->编辑
imageFile: null, // 需要被截取的图片 imageFile: null, // 需要被截取的图片
courseName: null, // 线下课名称 courseName: null, // 线下课名称
courseMedia: '', courseMedia: '',
introduce: '', introduce: '',
coverId: null, // 线下封面的recourceId coverId: null, // 线下封面的recourceId
coverUrl: defaultCoverUrl, // 线下课封面 coverUrl: defaultCoverUrl, // 线下课封面
studentList: [], // 上课学员列表 studentList: [], // 上课学员列表
diskList: [], // 机构可见磁盘目录 diskList: [], // 机构可见磁盘目录
selectedFileList: [], // 已经从资料云盘中勾选的文件 selectedFileList: [], // 已经从资料云盘中勾选的文件
showCutModal: false, // 是否显示截图弹窗 showCutModal: false, // 是否显示截图弹窗
studentModal: false, studentModal: false,
categoryName:null, //分类名称 categoryName: null, //分类名称
categoryList: [], categoryList: [],
courseCatalogList:[], //分类列表 courseCatalogList: [], //分类列表
categoryId:null, //分类的Id值 categoryId: null, //分类的Id值
whetherVisitorsJoin: 'NO', // 是否允许游客加入 whetherVisitorsJoin: 'NO', // 是否允许游客加入
isContent: true, isContent: true,
teacherList: [], teacherList: [],
teacherQuery: { teacherQuery: {
size: 15, size: 15,
current: 1, current: 1,
nickName:null nickName: null,
}, },
calendarTime: [], calendarTime: [],
offlineCourseType: 'ALL_DAY_OFFLINE', offlineCourseType: 'ALL_DAY_OFFLINE',
...@@ -101,7 +86,7 @@ class AddOfflineCourse extends React.Component { ...@@ -101,7 +86,7 @@ class AddOfflineCourse extends React.Component {
isEditDisablie: false, isEditDisablie: false,
startTime: new Date().getTime() + 300000, // 批量开始时分 startTime: new Date().getTime() + 300000, // 批量开始时分
endTime: new Date().getTime() + 300000, // 批量结束时分 endTime: new Date().getTime() + 300000, // 批量结束时分
} };
} }
componentWillMount() { componentWillMount() {
...@@ -114,27 +99,27 @@ class AddOfflineCourse extends React.Component { ...@@ -114,27 +99,27 @@ class AddOfflineCourse extends React.Component {
} }
initBus = () => { initBus = () => {
Bus.bind('offlineEditorImage', this.uploadImage) Bus.bind('offlineEditorImage', this.uploadImage);
} };
removeBus = () => { removeBus = () => {
Bus.unbind('offlineEditorImage', this.uploadImage) Bus.unbind('offlineEditorImage', this.uploadImage);
} };
uploadImage = () => { uploadImage = () => {
this.setState({ showSelectImageModal: true }) this.setState({ showSelectImageModal: true });
} };
//获取分类列表 //获取分类列表
getCourseCatalogList = ()=>{ getCourseCatalogList = () => {
Service.Hades('public/hades/queryCategoryTree', { source: 0, tenantId: User.getStoreId(), count: false, userId: User.getUserId() }).then((res) => { Service.Hades('public/hades/queryCategoryTree', { source: 0, tenantId: User.getStoreId(), count: false, userId: User.getUserId() }).then((res) => {
const { categoryList = [] } = res.result; const { categoryList = [] } = res.result;
this.setState({ this.setState({
categoryList, categoryList,
courseCatalogList: this.renderTreeNodes(categoryList), courseCatalogList: this.renderTreeNodes(categoryList),
}) });
}); });
} };
renderTreeNodes = (data) => { renderTreeNodes = (data) => {
let newTreeData = data.map((item) => { let newTreeData = data.map((item) => {
...@@ -151,22 +136,21 @@ class AddOfflineCourse extends React.Component { ...@@ -151,22 +136,21 @@ class AddOfflineCourse extends React.Component {
checkDetail = (courseId) => { checkDetail = (courseId) => {
return Service.Hades('public/hades/getOfflineCourseDetail', { return Service.Hades('public/hades/getOfflineCourseDetail', {
courseId courseId,
}).then((res) => { }).then((res) => {
const { courseState } = res.result; const { courseState } = res.result;
return courseState === 'UN_START'; return courseState === 'UN_START';
}); });
} };
// 获取线下课详情 // 获取线下课详情
handleFetchScheudleDetail = (courseId) => { handleFetchScheudleDetail = (courseId) => {
return Service.Hades('public/hades/getOfflineCourseDetail',{ return Service.Hades('public/hades/getOfflineCourseDetail', {
courseId courseId,
}).then((res) => { }).then((res) => {
const { result = {} } = res || {}; const { result = {} } = res || {};
const { const {
courseName, courseName,
courseState,
categoryId, categoryId,
offlinePlace, offlinePlace,
whetherVisitorsJoin, whetherVisitorsJoin,
...@@ -197,25 +181,25 @@ class AddOfflineCourse extends React.Component { ...@@ -197,25 +181,25 @@ class AddOfflineCourse extends React.Component {
let coverId; let coverId;
let coverUrl = this.state.coverUrl; let coverUrl = this.state.coverUrl;
let hasIntro = false; let hasIntro = false;
courseMediaVOS.map((item) => { courseMediaVOS.map((item) => {
switch (item.contentType){ switch (item.contentType) {
case "COVER": case 'COVER':
coverId = item.mediaContent; coverId = item.mediaContent;
coverUrl = item.mediaUrl; coverUrl = item.mediaUrl;
break; break;
case "SCHEDULE": case 'SCHEDULE':
this.getTextDetail('courseMedia', item.mediaUrl); this.getTextDetail('courseMedia', item.mediaUrl);
break; break;
case "INTRO": case 'INTRO':
hasIntro = true; hasIntro = true;
this.getTextDetail('introduce', item.mediaUrl); this.getTextDetail('introduce', item.mediaUrl);
break; break;
default: default:
break; break;
} }
return item; return item;
}) });
this.setState({ this.setState({
loadintroduce: !hasIntro, loadintroduce: !hasIntro,
coverId, coverId,
...@@ -248,50 +232,43 @@ class AddOfflineCourse extends React.Component { ...@@ -248,50 +232,43 @@ class AddOfflineCourse extends React.Component {
signOutType, signOutType,
isEditDisablie: whetherHaveApply === 'YES', isEditDisablie: whetherHaveApply === 'YES',
}); });
}) });
} };
getTextDetail = (key, url) => { getTextDetail = (key, url) => {
$.ajax({ $.ajax({
data: {}, data: {},
type: 'GET', type: 'GET',
url, url,
contentType:'application/x-www-form-urlencoded; charset=UTF-8', contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
success: (res) => { success: (res) => {
this.setState({ [key]: res, [`load${key}`]: true }); this.setState({ [key]: res, [`load${key}`]: true });
} },
}) });
} };
handleGoBack = () => { handleGoBack = () => {
const { const { coverId, videoName, videoDuration, courseName, categoryId, whetherVisitorsJoin } = this.state;
coverId, if (videoName || videoDuration || categoryId || courseName || coverId || whetherVisitorsJoin !== whetherVisitorsJoin) {
videoName,
videoDuration,
courseName,
categoryId,
whetherVisitorsJoin
} = this.state;
if(videoName || videoDuration || categoryId || courseName || coverId || whetherVisitorsJoin !== whetherVisitorsJoin ){
Modal.confirm({ Modal.confirm({
title: '确认要返回吗?', title: '确认要返回吗?',
content: '返回后,本次编辑的内容将不被保存。', content: '返回后,本次编辑的内容将不被保存。',
okText: '确认返回', okText: '确认返回',
cancelText: '留在本页', cancelText: '留在本页',
icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>, icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
onOk: () => { onOk: () => {
window.RCHistory.push({ window.RCHistory.push({
pathname: `/offline-course`, pathname: `/offline-course`,
}); });
} },
}); });
}else{ } else {
window.RCHistory.push({ window.RCHistory.push({
pathname: `/offline-course`, pathname: `/offline-course`,
}); });
} }
} };
// 显示预览弹窗 // 显示预览弹窗
handleShowPreviewModal = () => { handleShowPreviewModal = () => {
const { const {
...@@ -339,69 +316,69 @@ class AddOfflineCourse extends React.Component { ...@@ -339,69 +316,69 @@ class AddOfflineCourse extends React.Component {
signOutStartTimeNum, signOutStartTimeNum,
signOutStartTimeUnit, signOutStartTimeUnit,
signOutEndTimeNum, signOutEndTimeNum,
signOutEndTimeUnit, signOutEndTimeUnit,
} };
const previewOfflineModal = ( const previewOfflineModal = (
<PreviewOfflineModal <PreviewOfflineModal
data={data} data={data}
close={() => { close={() => {
this.setState({ this.setState({
previewOfflineModal: null previewOfflineModal: null,
}) });
}} }}
/> />
); );
this.setState({ previewOfflineModal }); this.setState({ previewOfflineModal });
} };
handleSelectCover = (file)=> { handleSelectCover = (file) => {
// this.uploadCoverImage(file);
this.setState({ this.setState({
visible: true, visible: true,
imageFile:file imageFile: file,
}); });
} };
//获取resourceId //获取resourceId
getSignature = (blob, fileName) => { getSignature = (blob, fileName) => {
Upload.uploadBlobToOSS(blob, 'cover' + (new Date()).valueOf(),null,'signInfo').then((signInfo) => { Upload.uploadBlobToOSS(blob, 'cover' + new Date().valueOf(), null, 'signInfo').then((signInfo) => {
this.setState({ this.setState(
coverClicpPath:signInfo.fileUrl, {
coverId:signInfo.resourceId, coverClicpPath: signInfo.fileUrl,
visible: false coverId: signInfo.resourceId,
},()=>this.updateCover()) visible: false,
},
() => this.updateCover()
);
}); });
}; };
updateCover = () =>{ updateCover = () => {
const {coverClicpPath,coverId} = this.state const { coverClicpPath, coverId } = this.state;
this.setState({ this.setState({
showSelectCoverModal: false, showSelectCoverModal: false,
coverUrl:coverClicpPath, coverUrl: coverClicpPath,
coverId:coverId coverId: coverId,
}) });
} };
preSubmit = () => { preSubmit = () => {
//过期判断 //过期判断
if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) { if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) {
Modal.warning({ Modal.warning({
title:"服务已到期", title: '服务已到期',
content: "当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买", content: '当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买',
okText: "我知道了" okText: '我知道了',
}) });
return return;
} }
const { courseId } = this.state; const { courseId } = this.state;
if (courseId) { if (courseId) {
this.checkDetail(courseId).then(bool => bool ? this.handleSubmit() : message.warning('课程已开始,无法继续编辑')) this.checkDetail(courseId).then((bool) => (bool ? this.handleSubmit() : message.warning('课程已开始,无法继续编辑')));
} else { } else {
this.handleSubmit(); this.handleSubmit();
} }
} };
// 保存 // 保存
handleSubmit = () => { handleSubmit = () => {
...@@ -437,20 +414,20 @@ class AddOfflineCourse extends React.Component { ...@@ -437,20 +414,20 @@ class AddOfflineCourse extends React.Component {
isMore, isMore,
} = this.state; } = this.state;
let coverObj ={ let coverObj = {
contentType:'COVER', contentType: 'COVER',
mediaContent: coverId, mediaContent: coverId,
mediaType:'PICTURE', mediaType: 'PICTURE',
mediaUrl: coverUrl, mediaUrl: coverUrl,
} };
let scheduleMediaRequests = []; let scheduleMediaRequests = [];
if(coverId){ if (coverId) {
scheduleMediaRequests = [coverObj] scheduleMediaRequests = [coverObj];
} }
// 编辑且使用默认图时不传 // 编辑且使用默认图时不传
if (pageType === 'edit' && coverUrl === defaultCoverUrl) { if (pageType === 'edit' && coverUrl === defaultCoverUrl) {
scheduleMediaRequests = [] scheduleMediaRequests = [];
} }
const commonParams = { const commonParams = {
categoryId, categoryId,
...@@ -495,16 +472,21 @@ class AddOfflineCourse extends React.Component { ...@@ -495,16 +472,21 @@ class AddOfflineCourse extends React.Component {
// 校验必填字段:课程名称, 课程线下 // 校验必填字段:课程名称, 课程线下
this.handleValidate(commonParams).then((res) => { this.handleValidate(commonParams).then((res) => {
if (!res) return; if (!res) return;
Upload.uploadTextToOSS(introduce, `${randomString()}.txt`, (introduceId) => { Upload.uploadTextToOSS(
this.submitRemote({ introduce,
courseId, `${randomString()}.txt`,
pageType, (introduceId) => {
commonParams, this.submitRemote({
introduceId, courseId,
}); pageType,
}, () => message.warning('上传课程简介失败')); commonParams,
introduceId,
});
},
() => message.warning('上传课程简介失败')
);
}); });
} };
submitRemote = (data) => { submitRemote = (data) => {
const { courseId, pageType, commonParams, introduceId } = data; const { courseId, pageType, commonParams, introduceId } = data;
...@@ -512,65 +494,68 @@ class AddOfflineCourse extends React.Component { ...@@ -512,65 +494,68 @@ class AddOfflineCourse extends React.Component {
if (pageType === 'add') { if (pageType === 'add') {
Service.Hades('public/hades/createOfflineCourse', commonParams).then((res) => { Service.Hades('public/hades/createOfflineCourse', commonParams).then((res) => {
if (!res) return; if (!res) return;
message.success("新建成功"); message.success('新建成功');
window.RCHistory.push({ window.RCHistory.push({
pathname: `/offline-course`, pathname: `/offline-course`,
}); });
}) });
} else { } else {
const editParams = { const editParams = {
courseId:courseId, courseId: courseId,
...commonParams, ...commonParams,
} };
Service.Hades('public/hades/updateOfflineCourse', editParams).then((res) => { Service.Hades('public/hades/updateOfflineCourse', editParams).then((res) => {
if (!res) return; if (!res) return;
message.success("保存成功"); message.success('保存成功');
window.RCHistory.push({ window.RCHistory.push({
pathname: `/offline-course`, pathname: `/offline-course`,
}); });
}); });
} }
} };
handleValidate = (data) => { handleValidate = (data) => {
return new Promise((resolve) => { return new Promise((resolve) => {
if (!data.courseName) { if (!data.courseName) {
message.warning('请输入课程名称'); message.warning('请输入课程名称');
resolve(false); resolve(false);
} else if(!data.categoryId){ } else if (!data.categoryId) {
message.warning('请选择课程分类'); message.warning('请选择课程分类');
resolve(false); resolve(false);
} else if(!data.offlinePlace){ } else if (!data.offlinePlace) {
message.warning('请输入上课地点'); message.warning('请输入上课地点');
resolve(false); resolve(false);
} else if(!data.teacherId ){ } else if (!data.teacherId) {
message.warning('请选择讲师'); message.warning('请选择讲师');
resolve(false); resolve(false);
} else if(_.isEmpty(data.calendarTime)){ } else if (_.isEmpty(data.calendarTime)) {
message.warning('请选择上课日期'); message.warning('请选择上课日期');
resolve(false); resolve(false);
} else if(!data.startTime || !data.endTime){ } else if (!data.startTime || !data.endTime) {
message.warning('请选择上课时间'); message.warning('请选择上课时间');
resolve(false); resolve(false);
} else if(moment(moment(data.calendarTime[0]).format('YYYY-MM-DD') + moment(data.startTime).format(' HH:mm')).valueOf() < Date.now()){ } else if (moment(moment(data.calendarTime[0]).format('YYYY-MM-DD') + moment(data.startTime).format(' HH:mm')).valueOf() < Date.now()) {
message.warning('上课时间不能早于现在'); message.warning('上课时间不能早于现在');
resolve(false); resolve(false);
} else if(data.startTime >= data.endTime){ } else if (data.startTime >= data.endTime) {
message.warning('上课结束时间不能早于上课开始时间'); message.warning('上课结束时间不能早于上课开始时间');
resolve(false); resolve(false);
} else if(data.whetherSetApply === 'YES' && !data.startTimeApply){ } else if (data.whetherSetApply === 'YES' && !data.startTimeApply) {
message.warning('请选择报名时间'); message.warning('请选择报名时间');
resolve(false); resolve(false);
} else if(data.whetherSetApply === 'YES' && data.startTimeApply >= data.endTimeApply){ } else if (data.whetherSetApply === 'YES' && data.startTimeApply >= data.endTimeApply) {
message.warning('报名结束时间需大于报名开始时间'); message.warning('报名结束时间需大于报名开始时间');
resolve(false); resolve(false);
} else if(data.whetherSetApply === 'YES' && data.endTimeApply > moment(moment(data.calendarTime[0]).format('YYYY-MM-DD') + moment(data.endTime).format(' HH:mm:ss')).valueOf()){ } else if (
data.whetherSetApply === 'YES' &&
data.endTimeApply > moment(moment(data.calendarTime[0]).format('YYYY-MM-DD') + moment(data.endTime).format(' HH:mm:ss')).valueOf()
) {
message.warning('报名结束时间需小于上课开始时间'); message.warning('报名结束时间需小于上课开始时间');
resolve(false); resolve(false);
} else if(data.whetherSetSignIn === 'YES' && !data.signInTimeNum){ } else if (data.whetherSetSignIn === 'YES' && !data.signInTimeNum) {
message.warning('请输入签到时间'); message.warning('请输入签到时间');
resolve(false); resolve(false);
} else if(data.whetherSetSignOut === 'YES' && ((data.signOutType === 'START_LATER' && !data.signOutStartTimeNum) || !data.signOutEndTimeNum)){ } else if (data.whetherSetSignOut === 'YES' && ((data.signOutType === 'START_LATER' && !data.signOutStartTimeNum) || !data.signOutEndTimeNum)) {
message.warning('请输入签退时间'); message.warning('请输入签退时间');
resolve(false); resolve(false);
} else if (data.isMore) { } else if (data.isMore) {
...@@ -580,7 +565,7 @@ class AddOfflineCourse extends React.Component { ...@@ -580,7 +565,7 @@ class AddOfflineCourse extends React.Component {
resolve(true); resolve(true);
} }
}); });
} };
// 使用默认封面图 // 使用默认封面图
handleResetCoverUrl = () => { handleResetCoverUrl = () => {
...@@ -590,41 +575,46 @@ class AddOfflineCourse extends React.Component { ...@@ -590,41 +575,46 @@ class AddOfflineCourse extends React.Component {
if (isDefaultCover) return; if (isDefaultCover) return;
message.success('已替换为默认图'); message.success('已替换为默认图');
this.setState({ coverUrl: defaultCoverUrl }); this.setState({ coverUrl: defaultCoverUrl });
} };
// 滑动加载更多讲师列表 // 滑动加载更多讲师列表
handleScrollTeacherList = (e) => { handleScrollTeacherList = (e) => {
const { hasNext } = this.state; const { hasNext } = this.state;
const container = e.target; const container = e.target;
//判定元素是否滚动到底部 //判定元素是否滚动到底部
const scrollToBottom = container && container.scrollHeight <= container.clientHeight + container.scrollTop; const scrollToBottom = container && container.scrollHeight <= container.clientHeight + container.scrollTop;
if (scrollToBottom && hasNext) { if (scrollToBottom && hasNext) {
const { teacherQuery } = this.state; const { teacherQuery } = this.state;
let _teacherQuery = teacherQuery; let _teacherQuery = teacherQuery;
_teacherQuery.current = _teacherQuery.current + 1 _teacherQuery.current = _teacherQuery.current + 1;
this.setState({ this.setState(
teacherQuery:{..._teacherQuery} {
},()=>{this.getTeacherList(_teacherQuery.current)}) teacherQuery: { ..._teacherQuery },
} },
} () => {
this.getTeacherList(_teacherQuery.current);
getTeacherList(current = 1, selectList){ }
const { teacherQuery,teacherList} = this.state; );
}
};
getTeacherList(current = 1, selectList) {
const { teacherQuery, teacherList } = this.state;
const _query = { const _query = {
...teacherQuery, ...teacherQuery,
current, current,
size:15 size: 15,
}; };
StoreService.getStoreUserBasicPage( _query).then((res) => { StoreService.getStoreUserBasicPage(_query).then((res) => {
const { result = {} } = res; const { result = {} } = res;
const { records = [], total = 0, hasNext } = result; const { records = [], hasNext } = result;
const list = current > 1 ? teacherList.concat(records) : records; const list = current > 1 ? teacherList.concat(records) : records;
this.setState({ this.setState({
hasNext, hasNext,
teacherList: list, teacherList: list,
teacherQuery:{..._query} teacherQuery: { ..._query },
}) });
}); });
} }
...@@ -634,14 +624,14 @@ class AddOfflineCourse extends React.Component { ...@@ -634,14 +624,14 @@ class AddOfflineCourse extends React.Component {
message.warning('内容过长,不能超过1000字'); message.warning('内容过长,不能超过1000字');
} }
this.setState({ introduce: value, isMore }); this.setState({ introduce: value, isMore });
} };
selectMultiDate = (calendarTime) => { selectMultiDate = (calendarTime) => {
const dateList = _.sortBy(calendarTime); const dateList = _.sortBy(calendarTime);
this.setState({ this.setState({
calendarTime: dateList, calendarTime: dateList,
}) });
} };
handleChangeDates = (dates) => { handleChangeDates = (dates) => {
const data = {}; const data = {};
...@@ -653,15 +643,15 @@ class AddOfflineCourse extends React.Component { ...@@ -653,15 +643,15 @@ class AddOfflineCourse extends React.Component {
data.endTimeApply = dates[1].startOf('minute').valueOf() + 59000; data.endTimeApply = dates[1].startOf('minute').valueOf() + 59000;
} }
this.setState(data); this.setState(data);
} };
whetherVisitorsJoinChange = ()=>{ whetherVisitorsJoinChange = () => {
if(this.state.whetherVisitorsJoin === "NO"){ if (this.state.whetherVisitorsJoin === 'NO') {
this.setState({ whetherVisitorsJoin: 'YES' }); this.setState({ whetherVisitorsJoin: 'YES' });
}else{ } else {
this.setState({ whetherVisitorsJoin: 'NO' }); this.setState({ whetherVisitorsJoin: 'NO' });
} }
} };
handleChangeCatalogList = (value, label) => { handleChangeCatalogList = (value, label) => {
this.setState({ categoryId: value, categoryName: label[0] }); this.setState({ categoryId: value, categoryName: label[0] });
...@@ -675,14 +665,11 @@ class AddOfflineCourse extends React.Component { ...@@ -675,14 +665,11 @@ class AddOfflineCourse extends React.Component {
coverUrl, coverUrl,
introduce, introduce,
categoryId, categoryId,
categoryList,
courseCatalogList, courseCatalogList,
whetherVisitorsJoin, whetherVisitorsJoin,
loadintroduce, loadintroduce,
showSelectCoverModal, showSelectCoverModal,
visible, visible,
hasImgReady,
cutImageBlob,
teacherId, teacherId,
teacherList, teacherList,
calendarTime, calendarTime,
...@@ -710,64 +697,66 @@ class AddOfflineCourse extends React.Component { ...@@ -710,64 +697,66 @@ class AddOfflineCourse extends React.Component {
} = this.state; } = this.state;
const isDefaultCover = coverUrl === defaultCoverUrl; const isDefaultCover = coverUrl === defaultCoverUrl;
return ( return (
<div className="page add-offline-course-page"> <div className='page add-offline-course-page'>
<Breadcrumbs <Breadcrumbs navList={pageType === 'add' ? '新建线下课' : '编辑线下课'} goBack={this.handleGoBack} />
navList={pageType === "add" ? "新建线下课" : "编辑线下课"}
goBack={this.handleGoBack}
/>
<div className="box"> <div className='box'>
<div className="show-tips"> <div className='show-tips'>
<ShowTips message="请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利" /> <ShowTips message='请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利' />
</div> </div>
<div className="form"> <div className='form'>
<div className="basic-info__wrap"> <div className='basic-info__wrap'>
<div className="title">基本信息</div> <div className='title'>基本信息</div>
<div className="course-name"> <div className='course-name'>
<span className="label"><span className="require">*</span>课程名称:</span> <span className='label'>
<span className='require'>*</span>课程名称:
</span>
<Input <Input
value={courseName} value={courseName}
placeholder="请输入线下课的名称(40字以内)" placeholder='请输入线下课的名称(40字以内)'
maxLength={40} maxLength={40}
style={{ width: 240 }} style={{ width: 240 }}
onChange={(e) => { this.setState({ courseName: e.target.value }) }} onChange={(e) => {
this.setState({ courseName: e.target.value });
}}
/> />
</div> </div>
<div className="course-cover"> <div className='course-cover'>
<span className="label">封面图:</span> <span className='label'>封面图:</span>
<div className="course-cover__wrap"> <div className='course-cover__wrap'>
<div className='img-content'>
<div className="img-content"> {isDefaultCover && <span className='tag'>默认图</span>}
{ <img src={coverUrl} alt='' />
isDefaultCover && <span className="tag">默认图</span>
}
<img src={coverUrl} />
</div> </div>
<div className="opt-btns"> <div className='opt-btns'>
<Button onClick={() => { <Button
this.setState({ onClick={() => {
showSelectCoverModal: true this.setState({
}) showSelectCoverModal: true,
}}>上传图片</Button> });
<span }}>
className={`default-btn ${isDefaultCover ? 'disabled' : ''}`} 上传图片
onClick={this.handleResetCoverUrl} </Button>
>使用默认图</span> <span className={`default-btn ${isDefaultCover ? 'disabled' : ''}`} onClick={this.handleResetCoverUrl}>
<div className="tips">建议尺寸1280*720px,图片支持jpg、jpeg、png格式。</div> 使用默认图
</span>
<div className='tips'>建议尺寸1280*720px,图片支持jpg、jpeg、png格式。</div>
</div> </div>
</div> </div>
</div> </div>
<div className="course-catalog"> <div className='course-catalog'>
<span className="label special"><span className="require">*</span>课程分类:</span> <span className='label special'>
<span className='require'>*</span>课程分类:
</span>
<TreeSelect <TreeSelect
showSearch showSearch
treeNodeFilterProp="title" treeNodeFilterProp='title'
style={{ width: 240 }} style={{ width: 240 }}
dropdownStyle={{ maxHeight: 300, overflow: "auto" }} dropdownStyle={{ maxHeight: 300, overflow: 'auto' }}
treeData={courseCatalogList} treeData={courseCatalogList}
placeholder="请选择课程类型" placeholder='请选择课程类型'
allowClear allowClear
value={categoryId} value={categoryId}
treeDefaultExpandAll treeDefaultExpandAll
...@@ -776,184 +765,189 @@ class AddOfflineCourse extends React.Component { ...@@ -776,184 +765,189 @@ class AddOfflineCourse extends React.Component {
}} }}
/> />
</div> </div>
<div className="course-catalog"> <div className='course-catalog'>
<span className="label special"><span className="require">*</span>上课地点:</span> <span className='label special'>
<span className='require'>*</span>上课地点:
</span>
<Input <Input
value={offlinePlace} value={offlinePlace}
maxLength={40} maxLength={40}
style={{ width: 240 }} style={{ width: 240 }}
placeholder="请输入上课地点(40字以内)" placeholder='请输入上课地点(40字以内)'
onChange={(e) => { onChange={(e) => {
this.setState({ offlinePlace: e.target.value }) this.setState({ offlinePlace: e.target.value });
}} }}
/> />
</div> </div>
<div className="course-catalog" id="teacher"> <div className='course-catalog' id='teacher'>
<span className="label special"><span className="require">* </span>讲师:</span> <span className='label special'>
<span className='require'>* </span>讲师:
</span>
<Select <Select
placeholder="请选择讲师" placeholder='请选择讲师'
value={teacherId} value={teacherId}
style={{ width: 240 }} style={{ width: 240 }}
showSearch showSearch
allowClear allowClear
filterOption={(input, option) => option} filterOption={(input, option) => option}
dropdownClassName="offline-dropdown-box" dropdownClassName='offline-dropdown-box'
onPopupScroll={this.handleScrollTeacherList} onPopupScroll={this.handleScrollTeacherList}
suffixIcon={<span className="icon iconfont" style={{fontSize:'12px',color:'#BFBFBF'}}>&#xe835;</span>} suffixIcon={
<span className='icon iconfont' style={{ fontSize: '12px', color: '#BFBFBF' }}>
&#xe835;
</span>
}
onChange={(value, option) => { onChange={(value, option) => {
if (option) { if (option) {
this.setState({ teacherId: value, teacherName: option.children }); this.setState({ teacherId: value, teacherName: option.children });
}else{ } else {
this.setState({ teacherId: value, teacherName: "" }); this.setState({ teacherId: value, teacherName: '' });
} }
}} }}
onSearch={(value) => { onSearch={(value) => {
let _teacherQuery = {...this.state.teacherQuery}; let _teacherQuery = { ...this.state.teacherQuery };
_teacherQuery.nickName = value _teacherQuery.nickName = value;
this.setState({ this.setState(
teacherQuery: _teacherQuery {
}, () => { teacherQuery: _teacherQuery,
this.getTeacherList() },
}) () => {
this.getTeacherList();
}
);
}} }}
onClear ={(value)=>{ onClear={(value) => {
this.setState({ this.setState(
teacherQuery:{ {
size: 15, teacherQuery: {
current: 1, size: 15,
nickName:null current: 1,
nickName: null,
},
},
() => {
this.getTeacherList();
} }
}, () => { );
this.getTeacherList() }}
}) getPopupContainer={() => document.getElementById('teacher')}>
} {_.map(teacherList, (item) => {
}
getPopupContainer={() =>
document.getElementById("teacher")
}
>
{_.map(teacherList, (item, index) => {
return ( return (
<Option value={item.id} key={item.id}>{item.nickName}</Option> <Option value={item.id} key={item.id}>
{item.nickName}
</Option>
); );
})} })}
</Select> </Select>
</div> </div>
<div className="allow-tourist-join"> <div className='allow-tourist-join'>
<span className="label">观看设置:</span> <span className='label'>观看设置:</span>
<div className="content"> <div className='content'>
<div> <div>
<Switch <Switch checked={whetherVisitorsJoin === 'YES' ? true : false} onChange={this.whetherVisitorsJoinChange} />
checked={whetherVisitorsJoin === "YES" ? true : false} </div>
onChange={this.whetherVisitorsJoinChange}
/>
</div>
<div> <div>
<div className="desc"> <div className='desc'>
<div>开启:允许未绑定手机号的学员观看</div> <div>开启:允许未绑定手机号的学员观看</div>
<div>关闭:仅限绑定了手机号的学员可以进入观看线下课</div> <div>关闭:仅限绑定了手机号的学员可以进入观看线下课</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="introduce"> <div className='introduce'>
<span className="label">课程简介:</span> <span className='label'>课程简介:</span>
<div className="content"> <div className='content'>
<div className="intro-list"> <div className='intro-list'>
<div className="intro-list__item introduce-editor"> <div className='intro-list__item introduce-editor'>
{(!courseId || loadintroduce) && {(!courseId || loadintroduce) && (
<GraphicsEditor <GraphicsEditor
id="intro" id='intro'
isIntro={true} isIntro={true}
maxLimit={1000} maxLimit={1000}
detail={{ detail={{
content: introduce content: introduce,
}} }}
onChange={(val, textLength) => { onChange={(val, textLength) => {
this.changeIntro(val, textLength) this.changeIntro(val, textLength);
}} }}
/> />
} )}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="title" style={{ marginTop: 24 }}>课程设置</div> <div className='title' style={{ marginTop: 24 }}>
<div className="day"> 课程设置
<span className="label"> </div>
<span className="require">*</span> <div className='day'>
上课日期: <span className='label'>
</span> <span className='require'>*</span>
上课日期:
</span>
<div> <div>
<div className='select-day'> <div className='select-day'>
已选 <span className="mark-day">{isLongArr(calendarTime) ? calendarTime.length : 0}</span> 已选 <span className='mark-day'>{window.isLongArr(calendarTime) ? calendarTime.length : 0}</span>
</div> </div>
<MultipleDatePicker <MultipleDatePicker disabled={isEditDisablie} selectDateList={calendarTime} onSelect={this.selectMultiDate} canSelectTodayBefore={false} />
disabled={isEditDisablie}
selectDateList={calendarTime}
onSelect={this.selectMultiDate}
canSelectTodayBefore={false}
/>
</div> </div>
</div> </div>
<div className="hour" id="hour"> <div className='hour' id='hour'>
<span className="label"><span className="require">*</span>上课时间:</span> <span className='label'>
<span className='require'>*</span>上课时间:
</span>
<TimePicker <TimePicker
disabled={isEditDisablie} disabled={isEditDisablie}
className="time-picker" className='time-picker'
format="HH:mm" format='HH:mm'
value={startTime ? moment(startTime) : null} value={startTime ? moment(startTime) : null}
placeholder="开始时间" placeholder='开始时间'
showNow={false} showNow={false}
style={{ width: 100, minWidth: 100}} style={{ width: 100, minWidth: 100 }}
onSelect={(time) => { onSelect={(time) => {
this.setState({ startTime: time }); this.setState({ startTime: time });
}} }}
getPopupContainer={() => getPopupContainer={() => document.getElementById('hour')}
document.getElementById("hour") />
} &nbsp;&nbsp;~&nbsp;&nbsp;
/>&nbsp;&nbsp;~&nbsp;&nbsp;
<TimePicker <TimePicker
disabled={isEditDisablie} disabled={isEditDisablie}
className="time-picker" className='time-picker'
format="HH:mm" format='HH:mm'
value={endTime ? moment(endTime) : null} value={endTime ? moment(endTime) : null}
placeholder="结束时间" placeholder='结束时间'
showNow={false} showNow={false}
style={{ width: 100, minWidth: 100 }} style={{ width: 100, minWidth: 100 }}
onSelect={(time) => { onSelect={(time) => {
this.setState({ endTime: time }); this.setState({ endTime: time });
}} }}
getPopupContainer={() => getPopupContainer={() => document.getElementById('hour')}
document.getElementById("hour")
}
/> />
</div> </div>
<div className="course-catalog"> <div className='course-catalog'>
<span className="label"><span className="require">* </span>学员上课方式:</span> <span className='label'>
<span className='require'>* </span>学员上课方式:
</span>
<Radio.Group <Radio.Group
style={{ display: 'inline-block' }} style={{ display: 'inline-block' }}
value={offlineCourseType} value={offlineCourseType}
onChange={(e) => { onChange={(e) => {
this.setState({ offlineCourseType: e.target.value }); this.setState({ offlineCourseType: e.target.value });
}} }}
className="mt5" className='mt5'
disabled={isEditDisablie} disabled={isEditDisablie}>
> <Radio value='ALL_DAY_OFFLINE' className='mr-16'>
<Radio value="ALL_DAY_OFFLINE" className="mr-16"> <span style={{ color: '#333' }}>所选日期都要上课</span>
<span style={{ color: "#333" }}>所选日期都要上课</span>
</Radio> </Radio>
<Radio value="ANY_DAY_POFFLINE" className="mr-16"> <Radio value='ANY_DAY_POFFLINE' className='mr-16'>
<span style={{ color: "#333" }}>选择任意1天上课</span> <span style={{ color: '#333' }}>选择任意1天上课</span>
</Radio> </Radio>
</Radio.Group> </Radio.Group>
</div> </div>
<div className="course-catalog"> <div className='course-catalog'>
<span className="label">课程报名:</span> <span className='label'>课程报名:</span>
<div className="switch-box"> <div className='switch-box'>
<div className="switch-item" key="1"> <div className='switch-item' key='1'>
<Switch <Switch
disabled={isEditDisablie} disabled={isEditDisablie}
checked={whetherSetApply === 'YES'} checked={whetherSetApply === 'YES'}
...@@ -966,79 +960,118 @@ class AddOfflineCourse extends React.Component { ...@@ -966,79 +960,118 @@ class AddOfflineCourse extends React.Component {
}); });
}} }}
/> />
<span className="switch-tip">开启后可设置课程报名时间,获取报名数据</span> <span className='switch-tip'>开启后可设置课程报名时间,获取报名数据</span>
</div> </div>
{whetherSetApply === 'YES' && <div className="switch-item" key="2"> {whetherSetApply === 'YES' && (
<span className="switch-label">报名日期:</span> <div className='switch-item' key='2'>
<RangePicker <span className='switch-label'>报名日期:</span>
id="course_date_picker" <RangePicker
showTime={{ showTime: 'HH:mm' }} id='course_date_picker'
allowClear={false} showTime={{ showTime: 'HH:mm' }}
value={startTimeApply ? [moment(startTimeApply), moment(endTimeApply)] : null } allowClear={false}
format={"YYYY-MM-DD HH:mm"} value={startTimeApply ? [moment(startTimeApply), moment(endTimeApply)] : null}
onChange={(dates) => { this.handleChangeDates(dates) }} format={'YYYY-MM-DD HH:mm'}
renderExtraFooter={() => calendarTime[0] ? <div style={{ position: 'absolute', bottom: 8, cursor: 'pointer' }}> onChange={(dates) => {
<span this.handleChangeDates(dates);
onClick={() => this.setState({ startTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).subtract(1, 'days').valueOf(), endTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).valueOf() - 1000 })} }}
style={{ renderExtraFooter={() => (
color: '#2966FF', <If condition={calendarTime[0]}>
border: '1px solid #2966FF', <div style={{ position: 'absolute', bottom: 8, cursor: 'pointer' }}>
padding: '2px 8px', <span
borderRadius: '2px', onClick={() =>
marginRight: 8, this.setState({
}} startTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`)
>上课前1天</span> .subtract(1, 'days')
<span .valueOf(),
onClick={() => this.setState({ startTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).subtract(2, 'days').valueOf(), endTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).valueOf() - 1000 })} endTimeApply:
style={{ moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).valueOf() - 1000,
color: '#2966FF', })
border: '1px solid #2966FF', }
padding: '2px 8px', style={{
borderRadius: '2px', color: '#2966FF',
marginRight: 8, border: '1px solid #2966FF',
}} padding: '2px 8px',
>上课前2天</span> borderRadius: '2px',
<span marginRight: 8,
onClick={() => this.setState({ startTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).subtract(3, 'days').valueOf(), endTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).valueOf() - 1000 })} }}>
style={{ 上课前1天
color: '#2966FF', </span>
border: '1px solid #2966FF', <span
padding: '2px 8px', onClick={() =>
borderRadius: '2px', this.setState({
marginRight: 8, startTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`)
}} .subtract(2, 'days')
>上课前3天</span> .valueOf(),
</div> : null} endTimeApply:
/> moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).valueOf() - 1000,
</div>} })
{whetherSetApply === 'YES' && <div className="switch-item" key="3"> }
<span className="switch-label"> style={{
报名人数 color: '#2966FF',
<Tooltip title="报名一旦开始,报名人数不支持减少"> border: '1px solid #2966FF',
<span style={{ margin: '0 4px', color: '#999' }} className="icon iconfont">&#xe7c4;</span> padding: '2px 8px',
</Tooltip> borderRadius: '2px',
:最多 marginRight: 8,
</span> }}>
<InputNumber 上课前2天
value={quota} </span>
min={oldQuta || 1} <span
max={100000} onClick={() =>
precision={0} this.setState({
style={{ margin: '0 4px', width: 90 }} startTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`)
disabled={oldQuta < 0} .subtract(3, 'days')
onChange={(value) => { .valueOf(),
this.setState({ quota: value }) endTimeApply:
}} moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).valueOf() - 1000,
/> })
<span className="switch-label"></span> }
<span className="switch-tip">未填写时默认为不限制</span> style={{
</div>} color: '#2966FF',
border: '1px solid #2966FF',
padding: '2px 8px',
borderRadius: '2px',
marginRight: 8,
}}>
上课前3天
</span>
</div>
</If>
)}
/>
</div>
)}
{whetherSetApply === 'YES' && (
<div className='switch-item' key='3'>
<span className='switch-label'>
报名人数
<Tooltip title='报名一旦开始,报名人数不支持减少'>
<span style={{ margin: '0 4px', color: '#999' }} className='icon iconfont'>
&#xe7c4;
</span>
</Tooltip>
:最多
</span>
<InputNumber
value={quota}
min={oldQuta || 1}
max={100000}
precision={0}
style={{ margin: '0 4px', width: 90 }}
disabled={oldQuta < 0}
onChange={(value) => {
this.setState({ quota: value });
}}
/>
<span className='switch-label'></span>
<span className='switch-tip'>未填写时默认为不限制</span>
</div>
)}
</div> </div>
</div> </div>
<div className="course-catalog"> <div className='course-catalog'>
<span className="label">考勤签到:</span> <span className='label'>考勤签到:</span>
<div className="switch-box"> <div className='switch-box'>
<div className="switch-item" key="1"> <div className='switch-item' key='1'>
<Switch <Switch
checked={whetherSetSignIn === 'YES'} checked={whetherSetSignIn === 'YES'}
onChange={(value) => { onChange={(value) => {
...@@ -1047,64 +1080,68 @@ class AddOfflineCourse extends React.Component { ...@@ -1047,64 +1080,68 @@ class AddOfflineCourse extends React.Component {
signInType: 'START_AGO', signInType: 'START_AGO',
signInTimeNum: null, signInTimeNum: null,
signInTimeUnit: 'MINUTE', signInTimeUnit: 'MINUTE',
}) });
}} }}
/> />
<span className="switch-tip">开启后可设置获取签到考勤数据</span> <span className='switch-tip'>开启后可设置获取签到考勤数据</span>
</div> </div>
{whetherSetSignIn === 'YES' && <div className="switch-item" key="2"> {whetherSetSignIn === 'YES' && (
<span className="switch-label">签到时间:</span> <div className='switch-item' key='2'>
<Radio.Group <span className='switch-label'>签到时间:</span>
style={{ display: 'inline-block' }} <Radio.Group
value={signInType} style={{ display: 'inline-block' }}
onChange={(e) => { value={signInType}
this.setState({ signInType: e.target.value }); onChange={(e) => {
}} this.setState({ signInType: e.target.value });
className="mt5" }}
> className='mt5'>
<Radio value="START_AGO" className="mr-16"> <Radio value='START_AGO' className='mr-16'>
<span style={{ color: "#333" }}>课程开始前</span> <span style={{ color: '#333' }}>课程开始前</span>
</Radio> </Radio>
<Radio value="END_AGO" className="mr-16"> <Radio value='END_AGO' className='mr-16'>
<span style={{ color: "#333" }}>课程结束前</span> <span style={{ color: '#333' }}>课程结束前</span>
</Radio> </Radio>
</Radio.Group> </Radio.Group>
</div>} </div>
{whetherSetSignIn === 'YES' && <div className="switch-item" key="3"> )}
<span className="switch-label">课程{signInType === 'START_AGO' ? '开始' : '结束'}</span> {whetherSetSignIn === 'YES' && (
<InputNumber <div className='switch-item' key='3'>
value={signInTimeNum} <span className='switch-label'>课程{signInType === 'START_AGO' ? '开始' : '结束'}</span>
min={1} <InputNumber
max={signInTimeUnit === 'MINUTE' ? 1440 : 24} value={signInTimeNum}
precision={0} min={1}
style={{ margin: '0 4px', width: 90 }} max={signInTimeUnit === 'MINUTE' ? 1440 : 24}
onChange={(value) => { precision={0}
this.setState({ signInTimeNum: value }) style={{ margin: '0 4px', width: 90 }}
}} onChange={(value) => {
/> this.setState({ signInTimeNum: value });
<Select }}
style={{ width: 72, marginRight: 4 }} />
value={signInTimeUnit} <Select
onChange={(value) => { style={{ width: 72, marginRight: 4 }}
const data = { signInTimeUnit: value } value={signInTimeUnit}
if (value === 'HOUR' && signInTimeNum > 24) { onChange={(value) => {
data.signInTimeNum = 24; const data = { signInTimeUnit: value };
} if (value === 'HOUR' && signInTimeNum > 24) {
this.setState(data); data.signInTimeNum = 24;
}} }
> this.setState(data);
{unitList.map(item => ( }}>
<Option value={item.key} key={item.key}>{item.value}</Option> {unitList.map((item) => (
))} <Option value={item.key} key={item.key}>
</Select> {item.value}
<span className="switch-label">内可签到</span> </Option>
</div>} ))}
</Select>
<span className='switch-label'>内可签到</span>
</div>
)}
</div> </div>
</div> </div>
<div className="course-catalog"> <div className='course-catalog'>
<span className="label">考勤签退:</span> <span className='label'>考勤签退:</span>
<div className="switch-box"> <div className='switch-box'>
<div className="switch-item" key="1"> <div className='switch-item' key='1'>
<Switch <Switch
checked={whetherSetSignOut === 'YES'} checked={whetherSetSignOut === 'YES'}
onChange={(value) => { onChange={(value) => {
...@@ -1115,119 +1152,135 @@ class AddOfflineCourse extends React.Component { ...@@ -1115,119 +1152,135 @@ class AddOfflineCourse extends React.Component {
signOutStartTimeUnit: 'MINUTE', signOutStartTimeUnit: 'MINUTE',
signOutEndTimeNum: null, signOutEndTimeNum: null,
signOutEndTimeUnit: 'MINUTE', signOutEndTimeUnit: 'MINUTE',
}) });
}} }}
/> />
<span className="switch-tip">开启后可设置获取签退考勤数据</span> <span className='switch-tip'>开启后可设置获取签退考勤数据</span>
</div> </div>
{whetherSetSignOut === 'YES' && <div className="switch-item" key="2"> {whetherSetSignOut === 'YES' && (
<span className="switch-label">签退时间:</span> <div className='switch-item' key='2'>
<Radio.Group <span className='switch-label'>签退时间:</span>
style={{ display: 'inline-block' }} <Radio.Group
value={signOutType} style={{ display: 'inline-block' }}
onChange={(e) => { value={signOutType}
this.setState({ signOutType: e.target.value }); onChange={(e) => {
}} this.setState({ signOutType: e.target.value });
className="mt5" }}
> className='mt5'>
<Radio value="START_LATER" className="mr-16"> <Radio value='START_LATER' className='mr-16'>
<span style={{ color: "#333" }}>课程开始后</span> <span style={{ color: '#333' }}>课程开始后</span>
</Radio> </Radio>
<Radio value="END_LATER" className="mr-16"> <Radio value='END_LATER' className='mr-16'>
<span style={{ color: "#333" }}>课程结束后</span> <span style={{ color: '#333' }}>课程结束后</span>
</Radio> </Radio>
</Radio.Group> </Radio.Group>
</div>} </div>
{whetherSetSignOut === 'YES' && <div className="switch-item" key="3"> )}
<span className="switch-label">课程{signOutType === 'START_LATER' ? '开始' : '结束'}</span> {whetherSetSignOut === 'YES' && (
{signOutType === 'START_LATER' && <InputNumber <div className='switch-item' key='3'>
value={signOutStartTimeNum} <span className='switch-label'>课程{signOutType === 'START_LATER' ? '开始' : '结束'}</span>
min={1} {signOutType === 'START_LATER' && (
max={signOutStartTimeUnit === 'MINUTE' ? 1440 : 24} <InputNumber
precision={0} value={signOutStartTimeNum}
style={{ margin: '0 4px', width: 90 }} min={1}
onChange={(value) => { max={signOutStartTimeUnit === 'MINUTE' ? 1440 : 24}
this.setState({ signOutStartTimeNum: value }) precision={0}
}} style={{ margin: '0 4px', width: 90 }}
/>} onChange={(value) => {
{signOutType === 'START_LATER' && <Select this.setState({ signOutStartTimeNum: value });
style={{ width: 72, marginRight: 4 }} }}
value={signOutStartTimeUnit} />
onChange={(value) => { )}
const data = { signOutStartTimeUnit: value } {signOutType === 'START_LATER' && (
if (value === 'HOUR' && signOutStartTimeNum > 24) { <Select
data.signOutStartTimeNum = 24; style={{ width: 72, marginRight: 4 }}
} value={signOutStartTimeUnit}
this.setState(data); onChange={(value) => {
}} const data = { signOutStartTimeUnit: value };
> if (value === 'HOUR' && signOutStartTimeNum > 24) {
{unitList.map(item => ( data.signOutStartTimeNum = 24;
<Option value={item.key} key={item.key}>{item.value}</Option> }
))} this.setState(data);
</Select>} }}>
{signOutType === 'START_LATER' && <span className="switch-label">就可签退,截止签退时间为下课后</span>} {unitList.map((item) => (
<InputNumber <Option value={item.key} key={item.key}>
value={signOutEndTimeNum} {item.value}
min={1} </Option>
max={signOutEndTimeUnit === 'MINUTE' ? 1440 : 24} ))}
precision={0} </Select>
style={{ margin: '0 4px', width: 90 }} )}
onChange={(value) => { {signOutType === 'START_LATER' && <span className='switch-label'>就可签退,截止签退时间为下课后</span>}
this.setState({ signOutEndTimeNum: value }) <InputNumber
}} value={signOutEndTimeNum}
/> min={1}
<Select max={signOutEndTimeUnit === 'MINUTE' ? 1440 : 24}
style={{ width: 72, marginRight: 4 }} precision={0}
value={signOutEndTimeUnit} style={{ margin: '0 4px', width: 90 }}
onChange={(value) => { onChange={(value) => {
const data = { signOutEndTimeUnit: value } this.setState({ signOutEndTimeNum: value });
if (value === 'HOUR' && signOutEndTimeNum > 24) { }}
data.signOutEndTimeNum = 24; />
} <Select
this.setState(data); style={{ width: 72, marginRight: 4 }}
}} value={signOutEndTimeUnit}
> onChange={(value) => {
{unitList.map(item => ( const data = { signOutEndTimeUnit: value };
<Option value={item.key} key={item.key}>{item.value}</Option> if (value === 'HOUR' && signOutEndTimeNum > 24) {
))} data.signOutEndTimeNum = 24;
</Select> }
{signOutType !== 'START_LATER' && this.setState(data);
<span className="switch-label">内可签退</span> }}>
} {unitList.map((item) => (
</div>} <Option value={item.key} key={item.key}>
{item.value}
</Option>
))}
</Select>
{signOutType !== 'START_LATER' && <span className='switch-label'>内可签退</span>}
</div>
)}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="footer"> <div className='footer'>
<Button onClick={this.handleGoBack}>取消</Button> <Button onClick={this.handleGoBack}>取消</Button>
<Button onClick={this.handleShowPreviewModal}>预览</Button> <Button onClick={this.handleShowPreviewModal}>预览</Button>
<Button type="primary" onClick={_.debounce(() => this.preSubmit(), 3000, true)}>保存</Button> <Button type='primary' onClick={_.debounce(() => this.preSubmit(), 3000, true)}>
保存
</Button>
</div> </div>
{showSelectCoverModal && {showSelectCoverModal && (
<SelectPrepareFileModal <SelectPrepareFileModal
key="basic" key='basic'
operateType="select" operateType='select'
multiple={false} multiple={false}
accept="image/jpeg,image/png,image/jpg" accept='image/jpeg,image/png,image/jpg'
selectTypeList={['JPG', 'JPEG', 'PNG']} selectTypeList={['JPG', 'JPEG', 'PNG']}
tooltip='支持文件类型:jpg、jpeg、png' tooltip='支持文件类型:jpg、jpeg、png'
isOpen={showSelectCoverModal} isOpen={showSelectCoverModal}
onClose={() => { onClose={() => {
this.setState({ showSelectCoverModal: false }) this.setState({ showSelectCoverModal: false });
}} }}
onSelect={this.handleSelectCover} onSelect={this.handleSelectCover}
/> />
} )}
{ visible && {visible && (
<ImgClipModal visible={visible} imgUrl={imageFile.ossUrl} onConfirm={this.getSignature} onClose={()=>{this.setState({ visible: false });}}/> <ImgClipModal
} visible={visible}
{ this.state.previewOfflineModal } imgUrl={imageFile.ossUrl}
onConfirm={this.getSignature}
onClose={() => {
this.setState({ visible: false });
}}
/>
)}
{this.state.previewOfflineModal}
</div> </div>
) );
} }
} }
......
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-07-16 11:05:17 * @Date: 2020-07-16 11:05:17
* @Last Modified by: chenshu * @Last Modified by: chenshu
* @Last Modified time: 2021-04-06 16:17:57 * @Last Modified time: 2021-04-06 16:17:57
* @Description: 添加直播-简介 * @Description: 添加直播-简介
*/ */
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
import { Col, message, Row, Switch } from 'antd';
import React from 'react'; import React from 'react';
import { Input, message, Upload, Radio, Row, Col, Button, Popover, Switch } from 'antd';
import Service from '@/common/js/service';
import GraphicsEditor from '../../components/GraphicsEditor'; import GraphicsEditor from '../../components/GraphicsEditor';
import User from '@/common/js/user';
import UploadOss from '@/core/upload';
import './AddGraphicsIntro.less'; import './AddGraphicsIntro.less';
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
import { DISK_MAP } from '@/common/constants/academic/lessonEnum';
import { ImgCutModalNew } from '@/components';
const { TextArea } = Input;
class AddGraphicsIntro extends React.Component { class AddGraphicsIntro extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
showSelectFileModal: false, showSelectFileModal: false,
diskList: [], diskList: [],
selectType: null, selectType: null,
} };
} }
// 上传封面图 // 上传封面图
handleShowImgCutModal = (event) => { handleShowImgCutModal = (event) => {
const imageFile = event.target.files[0]; const imageFile = event.target.files[0];
if (!imageFile) return; if (!imageFile) return;
this.setState({ this.setState({
imageFile, imageFile,
showCutModal: true, showCutModal: true,
}); });
} };
// 选择暖场资源 // 选择暖场资源
handleSelectVideo = (file) => { handleSelectVideo = (file) => {
const { selectType } = this.state; const { selectType } = this.state;
this.setState({ this.setState({
showSelectFileModal: false showSelectFileModal: false,
}) });
const { ossUrl, resourceId, folderName, folderFormat, folderSize } = file; const { ossUrl, resourceId, folderName, folderFormat, folderSize } = file;
if(selectType === 'WARMUP'){ if (selectType === 'WARMUP') {
const liveCourseWarmMedia = { const liveCourseWarmMedia = {
contentType: 'WARMUP', contentType: 'WARMUP',
mediaType: folderFormat === 'MP4' ? 'VIDEO' : 'PICTURE', mediaType: folderFormat === 'MP4' ? 'VIDEO' : 'PICTURE',
mediaContent: resourceId, mediaContent: resourceId,
mediaUrl: ossUrl, mediaUrl: ossUrl,
mediaName: folderName, mediaName: folderName,
size: folderSize size: folderSize,
} };
this.props.onChange('liveCourseWarmMedia', liveCourseWarmMedia); this.props.onChange('liveCourseWarmMedia', liveCourseWarmMedia);
}else{ } else {
// 最多添加九图片 // 最多添加九图片
const { liveCourseMediaRequests } = this.props.data; const { liveCourseMediaRequests } = this.props.data;
const list = _.filter(liveCourseMediaRequests, (item) => { const list = _.filter(liveCourseMediaRequests, (item) => {
return item.mediaType == "PICTURE"; return item.mediaType == 'PICTURE';
}); });
if (list.length > 8) { if (list.length > 8) {
message.warning("最多添加9张图片"); message.warning('最多添加9张图片');
return; return;
} }
liveCourseMediaRequests.push({ liveCourseMediaRequests.push({
...@@ -78,121 +70,128 @@ class AddGraphicsIntro extends React.Component { ...@@ -78,121 +70,128 @@ class AddGraphicsIntro extends React.Component {
}); });
this.props.onChange('liveCourseMediaRequests', liveCourseMediaRequests); this.props.onChange('liveCourseMediaRequests', liveCourseMediaRequests);
} }
};
}
changeDetail = (value) => { changeDetail = (value) => {
this.props.onChange('courseMedia', value); this.props.onChange('courseMedia', value);
} };
changeIntro = (value) => { changeIntro = (value) => {
this.props.onChange('introduce', value); this.props.onChange('introduce', value);
} };
whetherVisitorsJoinChange = ()=>{ whetherVisitorsJoinChange = () => {
if(this.props.data.whetherVisitorsJoin==="NO"){ if (this.props.data.whetherVisitorsJoin === 'NO') {
this.props.onChange('whetherVisitorsJoin','YES') this.props.onChange('whetherVisitorsJoin', 'YES');
}else{ } else {
this.props.onChange('whetherVisitorsJoin','NO') this.props.onChange('whetherVisitorsJoin', 'NO');
} }
} };
shelfStateChange = ()=>{ shelfStateChange = () => {
if(this.props.data.shelfState==="NO"){ if (this.props.data.shelfState === 'NO') {
this.props.onChange('shelfState','YES') this.props.onChange('shelfState', 'YES');
}else{ } else {
this.props.onChange('shelfState','NO') this.props.onChange('shelfState', 'NO');
} }
} };
render() { render() {
const {data: { id, whetherVisitorsJoin, courseMedia, introduce, shelfState, loadcourseMedia, loadintroduce } } = this.props; const {
data: { id, whetherVisitorsJoin, courseMedia, introduce, shelfState, loadcourseMedia, loadintroduce },
} = this.props;
const { showSelectFileModal, selectType } = this.state; const { showSelectFileModal, selectType } = this.state;
return ( return (
<div className="add-video__intro-info"> <div className='add-video__intro-info'>
<div className="allow-tourist-join"> <div className='allow-tourist-join'>
<span className="label">观看设置:</span> <span className='label'>观看设置:</span>
<div className="content"> <div className='content'>
<div> <div>
<Switch checked={whetherVisitorsJoin==="YES"? true:false} onChange={this.whetherVisitorsJoinChange}/> <Switch checked={whetherVisitorsJoin === 'YES' ? true : false} onChange={this.whetherVisitorsJoinChange} />
</div> </div>
<div> <div>
<div className="desc"> <div className='desc'>
<div>开启:允许未绑定手机号的学员观看</div> <div>开启:允许未绑定手机号的学员观看</div>
<div>关闭:仅限绑定了手机号的学员可以进入观看图文课</div> <div>关闭:仅限绑定了手机号的学员可以进入观看图文课</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="store-show"> <div className='store-show'>
<span className="label">学院展示:</span> <span className='label'>学院展示:</span>
<div className="content"> <div className='content'>
<Row> <Row>
<Col span={3}> <Col span={3}>
<Switch checked={shelfState==="YES"? true:false} onChange={this.shelfStateChange}/> <Switch checked={shelfState === 'YES' ? true : false} onChange={this.shelfStateChange} />
</Col> </Col>
<Col span={21}> <Col span={21}>
<div className="desc"> <div className='desc'>
<div>开启:图文课将在学员学院图文课列表中展示</div> <div>开启:图文课将在学员学院图文课列表中展示</div>
<div>关闭:图文课将在学员学院图文课列表中隐藏</div> <div>关闭:图文课将在学员学院图文课列表中隐藏</div>
</div> </div>
</Col> </Col>
</Row> </Row>
</div> </div>
</div> </div>
<div className="introduce required"> <div className='introduce required'>
<span className="label" style={{ marginTop: 5 }}>课程内容:</span> <span className='label' style={{ marginTop: 5 }}>
<div className="content"> 课程内容:
<div className="intro-list"> </span>
<div className="intro-list__item content-editor"> <div className='content'>
{(!id || loadcourseMedia) && <div className='intro-list'>
<div className='intro-list__item content-editor'>
{(!id || loadcourseMedia) && (
<GraphicsEditor <GraphicsEditor
id="content" id='content'
detail={{ detail={{
content: courseMedia content: courseMedia,
}}
onChange={(val) => {
this.changeDetail(val);
}} }}
onChange={(val) => { this.changeDetail(val) }}
/> />
} )}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="introduce"> <div className='introduce'>
<span className="label">课程简介:</span> <span className='label'>课程简介:</span>
<div className="content"> <div className='content'>
<div className="intro-list"> <div className='intro-list'>
<div className="intro-list__item introduce-editor"> <div className='intro-list__item introduce-editor'>
{(!id || loadintroduce) && {(!id || loadintroduce) && (
<GraphicsEditor <GraphicsEditor
id="intro" id='intro'
isIntro={true} isIntro={true}
detail={{ detail={{
content: introduce content: introduce,
}}
onChange={(val) => {
this.changeIntro(val);
}} }}
onChange={(val) => { this.changeIntro(val) }}
/> />
} )}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* 选择暖场图文件弹窗 */} {/* 选择暖场图文件弹窗 */}
{ showSelectFileModal && {showSelectFileModal && (
<SelectPrepareFileModal <SelectPrepareFileModal
operateType="select" operateType='select'
accept={selectType==="INTRO"?"image/jpeg,image/png,image/jpg":"video/mp4,image/jpeg,image/png,image/jpg"} accept={selectType === 'INTRO' ? 'image/jpeg,image/png,image/jpg' : 'video/mp4,image/jpeg,image/png,image/jpg'}
selectTypeList={ selectType==="INTRO" ? ['JPG', 'JPEG', 'PNG']: ['MP4', 'JPG', 'JPEG', 'PNG'] } selectTypeList={selectType === 'INTRO' ? ['JPG', 'JPEG', 'PNG'] : ['MP4', 'JPG', 'JPEG', 'PNG']}
tooltip={ selectType==="INTRO"?'支持文件类型:jpg、jpeg、png':'支持文件类型:jpg、jpeg、png、mp4'} tooltip={selectType === 'INTRO' ? '支持文件类型:jpg、jpeg、png' : '支持文件类型:jpg、jpeg、png、mp4'}
isOpen={showSelectFileModal} isOpen={showSelectFileModal}
onClose={() => { onClose={() => {
this.setState({ showSelectFileModal: false }) this.setState({ showSelectFileModal: false });
}} }}
onSelect={this.handleSelectVideo} onSelect={this.handleSelectVideo}
/> />
} )}
</div> </div>
) );
} }
} }
......
...@@ -17,6 +17,7 @@ import OfflineCourseData from '@/modules/course-manage/offline-course/OfflineCou ...@@ -17,6 +17,7 @@ import OfflineCourseData from '@/modules/course-manage/offline-course/OfflineCou
import { Dropdown, message, Modal, Switch, Tooltip } from 'antd'; import { Dropdown, message, Modal, Switch, Tooltip } from 'antd';
import moment from 'moment'; import moment from 'moment';
import React from 'react'; import React from 'react';
import { find, reduce, last } from 'underscore';
import { Route, withRouter } from 'react-router-dom'; import { Route, withRouter } from 'react-router-dom';
import ENUM from '../../../knowledge-base/ENUM.js'; import ENUM from '../../../knowledge-base/ENUM.js';
import PreviewOfflineModal from '../modal/PreviewOfflineModal'; import PreviewOfflineModal from '../modal/PreviewOfflineModal';
...@@ -44,7 +45,7 @@ class OfflineCourseList extends React.Component { ...@@ -44,7 +45,7 @@ class OfflineCourseList extends React.Component {
handlePlanName = (planArray) => { handlePlanName = (planArray) => {
let planStr = ''; let planStr = '';
planArray.map((item, index) => { planArray.forEach((item, index) => {
if (index < planArray.length - 1) { if (index < planArray.length - 1) {
planStr = planStr + item.planName + '、'; planStr = planStr + item.planName + '、';
} else { } else {
...@@ -64,13 +65,13 @@ class OfflineCourseList extends React.Component { ...@@ -64,13 +65,13 @@ class OfflineCourseList extends React.Component {
fixed: 'left', fixed: 'left',
render: (val, record) => { render: (val, record) => {
const { courseMediaVOS, courseName, offlinePlace, calendarTime, startTime, endTime } = record; const { courseMediaVOS, courseName, offlinePlace, calendarTime, startTime, endTime } = record;
const coverUrl = (_.find(courseMediaVOS, (data) => data.contentType === 'COVER') || {}).mediaUrl; const coverUrl = (find(courseMediaVOS, (data) => data.contentType === 'COVER') || {}).mediaUrl;
let isContinue = calendarTime.length > 1; let isContinue = calendarTime.length > 1;
_.reduce(calendarTime, (a, b) => { reduce(calendarTime, (a, b) => {
isContinue = isContinue && b - a === 86400000; isContinue = isContinue && b - a === 86400000;
return b; return b;
}); });
const lastTime = _.last(calendarTime); const lastTime = last(calendarTime);
const time = `${ const time = `${
!isContinue !isContinue
? calendarTime.map((item) => moment(item).format('MM-DD')).join('、') ? calendarTime.map((item) => moment(item).format('MM-DD')).join('、')
...@@ -138,7 +139,9 @@ class OfflineCourseList extends React.Component { ...@@ -138,7 +139,9 @@ class OfflineCourseList extends React.Component {
width: 120, width: 120,
dataIndex: 'courseware', dataIndex: 'courseware',
render: (val, item, index) => { render: (val, item, index) => {
return <Switch disabled={item.courseState === 'EXPIRED'} checked={item.shelfState === 'YES'} onChange={() => this.changeShelfState(item)} />; return (
<Switch size='small' disabled={item.courseState === 'EXPIRED'} checked={item.shelfState === 'YES'} onChange={() => this.changeShelfState(item)} />
);
}, },
}, },
{ {
......
...@@ -2,32 +2,32 @@ ...@@ -2,32 +2,32 @@
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-05 10:07:47 * @Date: 2020-08-05 10:07:47
* @LastEditors: Please set LastEditors * @LastEditors: Please set LastEditors
* @LastEditTime: 2021-07-08 15:07:46 * @LastEditTime: 2021-07-08 19:34:10
* @Description: 视频课新增/编辑页 * @Description: 视频课新增/编辑页
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
*/ */
import React from 'react' import React from 'react';
import { Button, Input, Radio, message, Modal, Cascader } from 'antd' import { Button, Input, Radio, message, Modal, Cascader } from 'antd';
import { DISK_MAP, FileTypeIcon, FileVerifyMap } from '@/common/constants/academic/lessonEnum' import { DISK_MAP, FileTypeIcon, FileVerifyMap } from '@/common/constants/academic/lessonEnum';
import ShowTips from '@/components/ShowTips' import ShowTips from '@/components/ShowTips';
import Breadcrumbs from '@/components/Breadcrumbs' import Breadcrumbs from '@/components/Breadcrumbs';
import moment from 'moment' import moment from 'moment';
import AddVideoIntro from './components/AddVideoIntro' import AddVideoIntro from './components/AddVideoIntro';
import SelectStudent from '../modal/select-student' import SelectStudent from '../modal/select-student';
import SelectPrepareFileModal from '../../prepare-lesson/modal/SelectPrepareFileModal' import SelectPrepareFileModal from '../../prepare-lesson/modal/SelectPrepareFileModal';
import PreviewCourseModal from '../modal/PreviewCourseModal' import PreviewCourseModal from '../modal/PreviewCourseModal';
import StoreService from '@/domains/store-domain/storeService' import StoreService from '@/domains/store-domain/storeService';
import CourseService from '@/domains/course-domain/CourseService' import CourseService from '@/domains/course-domain/CourseService';
import Service from '@/common/js/service' import Service from '@/common/js/service';
import User from '@/common/js/user' import User from '@/common/js/user';
import _ from 'underscore' import _ from 'underscore';
import Upload from '@/core/upload' import Upload from '@/core/upload';
import { randomString } from '@/domains/basic-domain/utils' import { randomString } from '@/domains/basic-domain/utils';
import ImgClipModal from '@/components/ImgClipModal' import ImgClipModal from '@/components/ImgClipModal';
import $ from 'jquery' import $ from 'jquery';
import './AddVideoCourse.less' import './AddVideoCourse.less';
const EDIT_BOX_KEY = Math.random(); const EDIT_BOX_KEY = Math.random();
const fieldNames = { label: 'categoryName', value: 'id', children: 'sonCategoryList' }; const fieldNames = { label: 'categoryName', value: 'id', children: 'sonCategoryList' };
...@@ -261,7 +261,6 @@ class AddVideoCourse extends React.Component { ...@@ -261,7 +261,6 @@ class AddVideoCourse extends React.Component {
_.each(studentIds, (item) => { _.each(studentIds, (item) => {
studentList.push({ studentId: item }); studentList.push({ studentId: item });
}); });
// this.setState({ studentModal: null });
this.setState({ studentList }); this.setState({ studentList });
this.setState({ studentModal: false }); this.setState({ studentModal: false });
}; };
...@@ -320,16 +319,16 @@ class AddVideoCourse extends React.Component { ...@@ -320,16 +319,16 @@ class AddVideoCourse extends React.Component {
// 保存 // 保存
handleSubmit = () => { handleSubmit = () => {
//过期判断 //过期判断
if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) { if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) {
Modal.warning({ Modal.warning({
title:"服务已到期", title: '服务已到期',
content: "当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买", content: '当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买',
okText: "我知道了" okText: '我知道了',
}) });
return return;
} }
const { instId, adminId } = window.currentUserInstInfo const { instId, adminId } = window.currentUserInstInfo;
const { const {
id, id,
...@@ -429,9 +428,9 @@ class AddVideoCourse extends React.Component { ...@@ -429,9 +428,9 @@ class AddVideoCourse extends React.Component {
handleSelectCover = (file) => { handleSelectCover = (file) => {
this.setState({ this.setState({
visible: true, visible: true,
imageFile:file imageFile: file,
}); });
} };
//获取resourceId //获取resourceId
getSignature = (blob, fileName) => { getSignature = (blob, fileName) => {
Upload.uploadBlobToOSS(blob, 'cover' + new Date().valueOf(), null, 'signInfo').then((signInfo) => { Upload.uploadBlobToOSS(blob, 'cover' + new Date().valueOf(), null, 'signInfo').then((signInfo) => {
...@@ -450,9 +449,9 @@ class AddVideoCourse extends React.Component { ...@@ -450,9 +449,9 @@ class AddVideoCourse extends React.Component {
this.setState({ this.setState({
showSelectCoverModal: false, showSelectCoverModal: false,
coverUrl: coverClicpPath, coverUrl: coverClicpPath,
coverId: coverId coverId: coverId,
}) });
} };
render() { render() {
const { const {
pageType, pageType,
...@@ -655,9 +654,16 @@ class AddVideoCourse extends React.Component { ...@@ -655,9 +654,16 @@ class AddVideoCourse extends React.Component {
onSelect={this.handleSelectCover} onSelect={this.handleSelectCover}
/> />
)} )}
{ visible && {visible && (
<ImgClipModal visible={visible} imgUrl={imageFile.ossUrl} onConfirm={this.getSignature} onClose={()=>{this.setState({ visible: false });}}/> <ImgClipModal
} visible={visible}
imgUrl={imageFile.ossUrl}
onConfirm={this.getSignature}
onClose={() => {
this.setState({ visible: false });
}}
/>
)}
{this.state.previewCourseModal} {this.state.previewCourseModal}
</div> </div>
); );
......
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-07-16 11:05:17 * @Date: 2020-07-16 11:05:17
* @Last Modified by: chenshu * @Last Modified by: chenshu
* @Last Modified time: 2021-04-06 16:44:09 * @Last Modified time: 2021-04-06 16:44:09
* @Description: 添加直播-简介 * @Description: 添加直播-简介
*/ */
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
import { Col, message, Row, Switch } from 'antd';
import React from 'react'; import React from 'react';
import { Input, message, Upload, Radio, Row, Col, Button, Popover, Switch } from 'antd';
import Service from '@/common/js/service';
import GraphicsEditor from '../../components/GraphicsEditor'; import GraphicsEditor from '../../components/GraphicsEditor';
import User from '@/common/js/user'; import { filter } from 'underscore';
import UploadOss from '@/core/upload';
import './AddVideoIntro.less'; import './AddVideoIntro.less';
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
import { DISK_MAP } from '@/common/constants/academic/lessonEnum';
import { ImgCutModalNew } from '@/components';
const { TextArea } = Input;
const defaultCover = 'https://xiaomai-image.oss-cn-hangzhou.aliyuncs.com/1599635741526.png'; const defaultCover = 'https://xiaomai-image.oss-cn-hangzhou.aliyuncs.com/1599635741526.png';
class AddVideoIntro extends React.Component { class AddVideoIntro extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
warmUrl: defaultCover, warmUrl: defaultCover,
showSelectFileModal: false, showSelectFileModal: false,
diskList: [], diskList: [],
selectType:null selectType: null,
} };
} }
// 上传封面图 // 上传封面图
handleShowImgCutModal = (event) => { handleShowImgCutModal = (event) => {
const imageFile = event.target.files[0]; const imageFile = event.target.files[0];
if (!imageFile) return; if (!imageFile) return;
this.setState({ this.setState({
imageFile, imageFile,
showCutModal: true, showCutModal: true,
}); });
} };
// 选择暖场资源 // 选择暖场资源
handleSelectVideo = (file) => { handleSelectVideo = (file) => {
const { selectType } = this.state; const { selectType } = this.state;
this.setState({ this.setState({
showSelectFileModal: false showSelectFileModal: false,
}) });
const { ossUrl, resourceId, folderName, folderFormat, folderSize } = file; const { ossUrl, resourceId, folderName, folderFormat, folderSize } = file;
if(selectType === 'WARMUP'){ if (selectType === 'WARMUP') {
const liveCourseWarmMedia = { const liveCourseWarmMedia = {
contentType: 'WARMUP', contentType: 'WARMUP',
mediaType: folderFormat === 'MP4' ? 'VIDEO' : 'PICTURE', mediaType: folderFormat === 'MP4' ? 'VIDEO' : 'PICTURE',
mediaContent: resourceId, mediaContent: resourceId,
mediaUrl: ossUrl, mediaUrl: ossUrl,
mediaName: folderName, mediaName: folderName,
size: folderSize size: folderSize,
} };
this.props.onChange('liveCourseWarmMedia', liveCourseWarmMedia); this.props.onChange('liveCourseWarmMedia', liveCourseWarmMedia);
}else{ } else {
// 最多添加九图片 // 最多添加九图片
const { liveCourseMediaRequests } = this.props.data; const { liveCourseMediaRequests } = this.props.data;
const list = _.filter(liveCourseMediaRequests, (item) => { const list = filter(liveCourseMediaRequests, (item) => {
return item.mediaType == "PICTURE"; return item.mediaType === 'PICTURE';
}); });
if (list.length > 8) { if (list.length > 8) {
message.warning("最多添加9张图片"); message.warning('最多添加9张图片');
return; return;
} }
liveCourseMediaRequests.push({ liveCourseMediaRequests.push({
contentType: 'INTRO', contentType: 'INTRO',
size: folderSize, size: folderSize,
mediaName: folderName, mediaName: folderName,
mediaContent: resourceId, mediaContent: resourceId,
mediaType: 'PICTURE', mediaType: 'PICTURE',
mediaUrl: ossUrl, mediaUrl: ossUrl,
}); });
this.props.onChange('liveCourseMediaRequests', liveCourseMediaRequests); this.props.onChange('liveCourseMediaRequests', liveCourseMediaRequests);
} }
};
}
changeIntro = (value) => { changeIntro = (value) => {
this.props.onChange('introduce', value); this.props.onChange('introduce', value);
} };
handleUpload = (Blob) => { handleUpload = (Blob) => {
this.setState({ this.setState({
showSelectFileModal: true, showSelectFileModal: true,
selectType:'INTRO' selectType: 'INTRO',
}) });
} };
whetherVisitorsJoinChange = ()=>{ whetherVisitorsJoinChange = () => {
if(this.props.data.whetherVisitorsJoin==="NO"){ if (this.props.data.whetherVisitorsJoin === 'NO') {
this.props.onChange('whetherVisitorsJoin','YES') this.props.onChange('whetherVisitorsJoin', 'YES');
}else{ } else {
this.props.onChange('whetherVisitorsJoin','NO') this.props.onChange('whetherVisitorsJoin', 'NO');
} }
} };
shelfStateChange = ()=>{ shelfStateChange = () => {
if(this.props.data.shelfState==="NO"){ if (this.props.data.shelfState === 'NO') {
this.props.onChange('shelfState','YES') this.props.onChange('shelfState', 'YES');
}else{ } else {
this.props.onChange('shelfState','NO') this.props.onChange('shelfState', 'NO');
} }
} };
render() { render() {
const {data: { whetherVisitorsJoin,liveCourseMediaRequests = [], shelfState, id, introduce, loadintroduce } } = this.props; const {
const {showSelectFileModal,selectType} = this.state data: { whetherVisitorsJoin, shelfState, id, introduce, loadintroduce },
} = this.props;
const { showSelectFileModal, selectType } = this.state;
return ( return (
<div className="add-video__intro-info"> <div className='add-video__intro-info'>
<div className="allow-tourist-join"> <div className='allow-tourist-join'>
<span className="label">观看设置:</span> <span className='label'>观看设置:</span>
<div className="content"> <div className='content'>
<div> <div>
<Switch checked={whetherVisitorsJoin==="YES"? true:false} onChange={this.whetherVisitorsJoinChange}/> <Switch checked={whetherVisitorsJoin === 'YES' ? true : false} onChange={this.whetherVisitorsJoinChange} />
</div> </div>
<div> <div>
<div className="desc"> <div className='desc'>
<div>开启:允许未绑定手机号的学员观看</div> <div>开启:允许未绑定手机号的学员观看</div>
<div>关闭:仅限绑定了手机号的学员可以进入观看视频</div> <div>关闭:仅限绑定了手机号的学员可以进入观看视频</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="store-show"> <div className='store-show'>
<span className="label">学院展示:</span> <span className='label'>学院展示:</span>
<div className="content"> <div className='content'>
<Row> <Row>
<Col span={3}> <Col span={3}>
<Switch checked={shelfState==="YES"? true:false} onChange={this.shelfStateChange}/> <Switch checked={shelfState === 'YES' ? true : false} onChange={this.shelfStateChange} />
</Col> </Col>
<Col span={21}> <Col span={21}>
<div className="desc"> <div className='desc'>
<div>开启:此视频将在学员学院的视频列表中出现</div> <div>开启:此视频将在学员学院的视频列表中出现</div>
<div>关闭:此视频将在学员学院的视频列表中隐藏</div> <div>关闭:此视频将在学员学院的视频列表中隐藏</div>
</div> </div>
</Col> </Col>
</Row> </Row>
</div> </div>
</div> </div>
<div className="introduce"> <div className='introduce'>
<span className="label">视频课简介:</span> <span className='label'>视频课简介:</span>
<div className="content"> <div className='content'>
<div className="intro-list"> <div className='intro-list'>
<div className="intro-list__item introduce-editor"> <div className='intro-list__item introduce-editor'>
{(!id || loadintroduce) && {(!id || loadintroduce) && (
<GraphicsEditor <GraphicsEditor
id="intro" id='intro'
isIntro={true} isIntro={true}
detail={{ detail={{
content: introduce content: introduce,
}}
onChange={(val) => {
this.changeIntro(val);
}} }}
onChange={(val) => { this.changeIntro(val) }}
/> />
} )}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* 选择暖场图文件弹窗 */} {/* 选择暖场图文件弹窗 */}
{ showSelectFileModal && {showSelectFileModal && (
<SelectPrepareFileModal <SelectPrepareFileModal
operateType="select" operateType='select'
accept={selectType==="INTRO"?"image/jpeg,image/png,image/jpg":"video/mp4,image/jpeg,image/png,image/jpg"} accept={selectType === 'INTRO' ? 'image/jpeg,image/png,image/jpg' : 'video/mp4,image/jpeg,image/png,image/jpg'}
selectTypeList={ selectType==="INTRO" ? ['JPG', 'JPEG', 'PNG']: ['MP4', 'JPG', 'JPEG', 'PNG'] } selectTypeList={selectType === 'INTRO' ? ['JPG', 'JPEG', 'PNG'] : ['MP4', 'JPG', 'JPEG', 'PNG']}
tooltip={ selectType==="INTRO"?'支持文件类型:jpg、jpeg、png':'支持文件类型:jpg、jpeg、png、mp4'} tooltip={selectType === 'INTRO' ? '支持文件类型:jpg、jpeg、png' : '支持文件类型:jpg、jpeg、png、mp4'}
isOpen={showSelectFileModal} isOpen={showSelectFileModal}
onClose={() => { onClose={() => {
this.setState({ showSelectFileModal: false }) this.setState({ showSelectFileModal: false });
}} }}
onSelect={this.handleSelectVideo} onSelect={this.handleSelectVideo}
/> />
} )}
</div> </div>
) );
} }
} }
......
...@@ -166,6 +166,7 @@ class VideoCourseList extends React.Component { ...@@ -166,6 +166,7 @@ class VideoCourseList extends React.Component {
render: (val, item, index) => { render: (val, item, index) => {
return ( return (
<Switch <Switch
size='small'
loading={ShelfLoading} loading={ShelfLoading}
checked={item.shelfState === 'YES'} checked={item.shelfState === 'YES'}
defaultChecked={item.shelfState} defaultChecked={item.shelfState}
......
...@@ -7,17 +7,16 @@ ...@@ -7,17 +7,16 @@
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React from 'react'; import React from 'react';
import { Button, Input, Switch, Radio, Row, Col, Modal, message, Tooltip } from 'antd'; import { Button, Input, Switch, Radio, Row, Col, message, Tooltip } from 'antd';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import SelectOperatorModal from '../modal/SelectOperatorModal'; import SelectOperatorModal from '../modal/SelectOperatorModal';
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal'; import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
import Upload from '@/core/upload'; import Upload from '@/core/upload';
import ImgClipModal from '@/components/ImgClipModal' import ImgClipModal from '@/components/ImgClipModal';
import './BasicInfo.less'; import './BasicInfo.less';
const { TextArea } = Input; const { TextArea } = Input;
const defaultCover = 'https://image.xiaomaiketang.com/xm/rEAetaTEh3.png'; const defaultCover = 'https://image.xiaomaiketang.com/xm/rEAetaTEh3.png';
let cutFlag = false;
class BasicInfo extends React.Component { class BasicInfo extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
...@@ -72,10 +71,9 @@ class BasicInfo extends React.Component { ...@@ -72,10 +71,9 @@ class BasicInfo extends React.Component {
}, 1000); }, 1000);
}; };
handleSelectCover = (file) => { handleSelectCover = (file) => {
// this.uploadImage(file);
this.setState({ this.setState({
visible: true, visible: true,
imageFile:file imageFile: file,
}); });
}; };
//获取resourceId //获取resourceId
...@@ -126,7 +124,7 @@ class BasicInfo extends React.Component { ...@@ -126,7 +124,7 @@ class BasicInfo extends React.Component {
}; };
render() { render() {
const { operatorModalVisible, showSelectFileModal, visible, hasImgReady, cutImageBlob,imageFile} = this.state; const { operatorModalVisible, showSelectFileModal, visible, imageFile } = this.state;
const { data } = this.props; const { data } = this.props;
const { planName, coverUrl, instro, enableState, operateType, selectOperatorList, percentCompleteLive, percentCompleteVideo, percentCompletePicture } = const { planName, coverUrl, instro, enableState, operateType, selectOperatorList, percentCompleteLive, percentCompleteVideo, percentCompletePicture } =
data; data;
...@@ -326,9 +324,16 @@ class BasicInfo extends React.Component { ...@@ -326,9 +324,16 @@ class BasicInfo extends React.Component {
onSelect={this.handleSelectCover} onSelect={this.handleSelectCover}
/> />
)} )}
{ visible && {visible && (
<ImgClipModal visible={visible} imgUrl={imageFile.ossUrl} onConfirm={this.getSignature} onClose={()=>{this.setState({ visible: false });}}/> <ImgClipModal
} visible={visible}
imgUrl={imageFile.ossUrl}
onConfirm={this.getSignature}
onClose={() => {
this.setState({ visible: false });
}}
/>
)}
</div> </div>
); );
} }
......
...@@ -64,6 +64,7 @@ function PlanList(props) { ...@@ -64,6 +64,7 @@ function PlanList(props) {
render: (val, item, index) => { render: (val, item, index) => {
return ( return (
<Switch <Switch
size='small'
checked={item.enableState === 'NO' ? false : true} checked={item.enableState === 'NO' ? false : true}
onChange={() => changeEnableState(item)} onChange={() => changeEnableState(item)}
disabled={User.getUserRole() === 'CloudManager' || User.getUserRole() === 'StoreManager' ? false : true} disabled={User.getUserRole() === 'CloudManager' || User.getUserRole() === 'StoreManager' ? false : true}
......
...@@ -330,7 +330,7 @@ class UserLearningData extends React.Component { ...@@ -330,7 +330,7 @@ class UserLearningData extends React.Component {
<span> <span>
<span>学习进度</span> <span>学习进度</span>
<Tooltip title='学员培训计划中达到“已完成”状态的课程数/总课程数'> <Tooltip title='学员培训计划中达到“已完成”状态的课程数/总课程数'>
<i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px' }}> <i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px', fontWeight: 'normal' }}>
&#xe61d; &#xe61d;
</i> </i>
</Tooltip> </Tooltip>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: wufan * @Author: wufan
* @Date: 2020-11-30 10:47:38 * @Date: 2020-11-30 10:47:38
* @LastEditors: Please set LastEditors * @LastEditors: Please set LastEditors
* @LastEditTime: 2021-07-08 14:25:58 * @LastEditTime: 2021-07-08 19:35:17
* @Description: web学院banner页面 * @Description: web学院banner页面
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -210,10 +210,6 @@ class StoreH5Decoration extends React.Component { ...@@ -210,10 +210,6 @@ class StoreH5Decoration extends React.Component {
// 选择云盘资源 // 选择云盘资源
handleSelectImg = (file) => { handleSelectImg = (file) => {
// this.setState({
// showSelectFileModal: false,
// });
// this.uploadImage(file);
if(file){ if(file){
this.setState({ this.setState({
visible: true, visible: true,
......
...@@ -74,7 +74,6 @@ class StoreInfo extends React.Component { ...@@ -74,7 +74,6 @@ class StoreInfo extends React.Component {
}) })
} }
handleSelectCover = (file)=> { handleSelectCover = (file)=> {
// this.uploadImage(file);
this.setState({ this.setState({
visible: true, visible: true,
imageFile:file imageFile:file
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: wufan * @Author: wufan
* @Date: 2020-11-30 10:47:38 * @Date: 2020-11-30 10:47:38
* @LastEditors: Please set LastEditors * @LastEditors: Please set LastEditors
* @LastEditTime: 2021-07-08 14:26:11 * @LastEditTime: 2021-07-08 19:35:27
* @Description: web学院banner页面 * @Description: web学院banner页面
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -209,10 +209,6 @@ class StoreWebDecoration extends React.Component { ...@@ -209,10 +209,6 @@ class StoreWebDecoration extends React.Component {
// 选择云盘资源 // 选择云盘资源
handleSelectImg = (file) => { handleSelectImg = (file) => {
// this.setState({
// showSelectFileModal: false,
// });
// this.uploadImage(file);
if(file){ if(file){
this.setState({ this.setState({
visible: true, visible: true,
......
import React, { useState, useRef, useEffect, useContext } from 'react' import Service from '@/common/js/service';
import Breadcrumbs from "@/components/Breadcrumbs"; import User from '@/common/js/user';
import { Form, Alert, Input, Button, InputNumber, DatePicker, Switch, Radio, message, Modal, Tooltip } from 'antd'; import Breadcrumbs from '@/components/Breadcrumbs';
import { Route, withRouter } from 'react-router-dom'; import ShowTips from '@/components/ShowTips';
import User from "@/common/js/user"; import { Button, DatePicker, Form, Input, InputNumber, message, Modal, Radio, Switch, Tooltip } from 'antd';
import moment from 'moment' import moment from 'moment';
import Service from "@/common/js/service"; import React, { useEffect, useRef, useState } from 'react';
import _ from 'underscore' import { withRouter } from 'react-router-dom';
import GraphicsEditor from '../../course-manage/components/GraphicsEditor'; import GraphicsEditor from '../../course-manage/components/GraphicsEditor';
import SelectPaperModal from './SelectPaperModal'
import PreviewModal from './PreviewModal'
import ShowTips from "@/components/ShowTips";
import './AddExam.less'; import './AddExam.less';
import PreviewModal from './PreviewModal';
import SelectPaperModal from './SelectPaperModal';
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
function AddExam(props: any) { function AddExam(props: any) {
const paperInfoInit: any = { passScore: 60 }; const paperInfoInit: any = { passScore: 60 };
const [showModal, setShowModal] = useState(false); const [showModal, setShowModal] = useState(false);
const [paperInfo, setPaperInfo] = useState(paperInfoInit); const [paperInfo, setPaperInfo] = useState(paperInfoInit);
const [paperId, setPaperId] = useState(''); const [paperId, setPaperId] = useState('');
const [passRate, setPassRate] = useState(60);//及格线 const [passRate, setPassRate] = useState(60); //及格线
const [examStartTime, setStartTime] = useState(''); const [examStartTime, setStartTime] = useState('');
const [examEndTime, setExamEndTime] = useState(''); const [examEndTime, setExamEndTime] = useState('');
const [examName, setExamName] = useState(''); const [examName, setExamName] = useState('');
const [needPhone, setNeedPhone] = useState('DO_NOT_NEED_PHONE_VERIFY'); const [needPhone, setNeedPhone] = useState('DO_NOT_NEED_PHONE_VERIFY');
const [needOptionDisorder, setNeedOptionDisorder] = useState('OPTION_SORT'); const [needOptionDisorder, setNeedOptionDisorder] = useState('OPTION_SORT');
const [resultContent, setResultContent] = useState('PASS_AND_SCORE'); const [resultContent, setResultContent] = useState('PASS_AND_SCORE');
const [answerAnalysis, setAnswerAnalysis] = useState('RIGHT_OR_WRONG'); const [answerAnalysis, setAnswerAnalysis] = useState('RIGHT_OR_WRONG');
const [resultShow, setResultShow] = useState('IMMEDIATELY'); const [resultShow, setResultShow] = useState('IMMEDIATELY');
const [examDesc, setExamDesc] = useState(''); const [examDesc, setExamDesc] = useState('');
const [passScore, setPassScore] = useState(100); const [passScore, setPassScore] = useState(100);
const [desclen, setDescLen] = useState(0); const [desclen, setDescLen] = useState(0);
const [check, setCheck] = useState(false); const [check, setCheck] = useState(false);
const [getData, setGetData] = useState(false); const [getData, setGetData] = useState(false);
const [preview, setPreview] = useState(false); const [preview, setPreview] = useState(false);
const [examTotal, setExamTotal] = useState(0); const [examTotal, setExamTotal] = useState(0);
const request = useRef(false); const request = useRef(false);
const { match } = props; const { match } = props;
const [examDuration, setExamDuration] = useState(undefined); const [examDuration, setExamDuration] = useState(undefined);
useEffect(() => { useEffect(() => {
switch (props.type) { switch (props.type) {
case "copy": // 考试列表-复制考试进入 case 'copy': // 考试列表-复制考试进入
case "edit": // 考试列表-编辑考试进入 case 'edit': // 考试列表-编辑考试进入
queryExamDetail(); queryExamDetail();
break; break;
case "organizeExam": // 试卷列表-组织考试进入 case 'organizeExam': // 试卷列表-组织考试进入
case "newPaperToAddExam": // 组卷页面-新建保存试卷并组织考试 case 'newPaperToAddExam': // 组卷页面-新建保存试卷并组织考试
case "editPaperToAddExam": // 组卷页面-编辑保存试卷并组织考试 case 'editPaperToAddExam': // 组卷页面-编辑保存试卷并组织考试
setGetData(true); setGetData(true);
setPaperInfo(props.paperInfo); setPaperInfo(props.paperInfo);
break; break;
} }
}, []) }, [props.paperInfo, props.type, queryExamDetail]);
useEffect(() => { useEffect(() => {
setPaperId(paperInfo.paperId) setPaperId(paperInfo.paperId);
setPassRate(paperInfo.passRate) setPassRate(paperInfo.passRate);
}, [paperInfo.paperId, paperInfo.passRate]);
}, [paperInfo.paperId])
useEffect(() => {
useEffect(() => { setPassScore(Math.round((((paperInfo.totalScore || 0) * (passRate || 0)) as any) / 100));
setPassScore(Math.round((paperInfo.totalScore || 0) * (passRate || 0) as any / 100)) setExamTotal(paperInfo.singleChoiceCnt + paperInfo.multiChoiceCnt + paperInfo.judgeCnt + paperInfo.gapFillingCnt + paperInfo.indefiniteChoiceCnt || 0);
setExamTotal(paperInfo.singleChoiceCnt + paperInfo.multiChoiceCnt + paperInfo.judgeCnt + paperInfo.gapFillingCnt + paperInfo.indefiniteChoiceCnt || 0) }, [
}, [paperInfo.paperId, passRate]) paperInfo.gapFillingCnt,
paperInfo.indefiniteChoiceCnt,
function disabledDate(current: any) { paperInfo.judgeCnt,
// Can not select days before today and today paperInfo.multiChoiceCnt,
return current && current < moment().startOf('day'); paperInfo.paperId,
paperInfo.singleChoiceCnt,
paperInfo.totalScore,
passRate,
]);
function disabledDate(current: any) {
// Can not select days before today and today
return current && current < moment().startOf('day');
}
function queryExamDetail() {
Service.Hades('public/hades/queryExamDetail', {
examId: match.params.id,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0,
}).then((res) => {
const { result } = res;
setPaperInfo(result.examPaper);
setPaperId(result.examPaper.paperId);
setStartTime(props.type === 'edit' ? result.examStartTime : '');
setExamEndTime(props.type === 'edit' ? result.examEndTime : '');
setExamName(props.type === 'edit' ? result.examName : `${result.examName}(复制)`);
setPassRate(result.passRate * 100);
setNeedPhone(result.needPhone);
setExamDesc(result.examDesc);
setExamDuration((result.examDuration / 60 / 1000) as any);
setAnswerAnalysis(result.answerAnalysis);
setNeedOptionDisorder(result.needOptionDisorder);
setPassScore(result.passScore);
setResultContent(result.resultContent);
setResultShow(result.resultShow);
setGetData(true);
});
}
function handleSave() {
if (request.current) {
return;
}
setCheck(true);
const param = {
paperId,
startTime: examStartTime,
endTime: examEndTime,
examName,
passRate: passRate / 100,
examStartTime,
examEndTime,
examDesc,
needPhone,
needOptionDisorder,
resultContent,
answerAnalysis,
resultShow,
examDuration: (examDuration || 0) * 60 * 1000,
passScore,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0,
examId: '',
};
if (!param.examName) {
message.warning('请输入考试名称');
return;
}
if (param.examName && param.examName.length > 40) {
message.warning('考试名称最多40字');
return;
} }
function queryExamDetail() { if (!paperId) {
Service.Hades("public/hades/queryExamDetail", { message.warning('请选择试卷');
examId: match.params.id, return;
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0
}).then((res) => {
const { result } = res
setPaperInfo(result.examPaper)
setPaperId(result.examPaper.paperId)
setStartTime(props.type === 'edit' ? result.examStartTime : '')
setExamEndTime(props.type === 'edit' ? result.examEndTime : '')
setExamName(props.type === 'edit' ? result.examName : `${result.examName}(复制)`)
setPassRate(result.passRate * 100)
setNeedPhone(result.needPhone)
setExamDesc(result.examDesc)
setExamDuration(result.examDuration / 60 / 1000 as any)
setAnswerAnalysis(result.answerAnalysis)
setNeedOptionDisorder(result.needOptionDisorder)
setPassScore(result.passScore)
setResultContent(result.resultContent)
setResultShow(result.resultShow)
setGetData(true)
})
} }
function handleSave() { if (!passRate) {
if (request.current) { message.warning('请输入及格线');
return return;
} }
if (!examStartTime || !examEndTime) {
message.warning('请选择考试起止时间');
return;
}
if (Number(examStartTime) < moment().valueOf()) {
message.warning('开始时间不能早于现在');
return;
}
if (!examDuration) {
message.warning('请输入考试时长');
return;
}
setCheck(true); if (examStartTime + (examDuration as any) * 60 * 1000 > examEndTime) {
const param = { message.warning('考试时长不得超过考试有效期时长');
return;
}
if (desclen > 1000) {
message.warning('内容过长,不能超过1000字');
return;
}
request.current = true;
setTimeout(() => {
request.current = false;
}, 2000);
if (props.type === 'edit') {
param.examId = match.params.id;
}
Service.Hades(props.type === 'edit' ? 'public/hades/editExam' : 'public/hades/createExam', param).then((res) => {
message.success(props.type === 'edit' ? '编辑成功' : '创建成功');
switch (props.type) {
case 'organizeExam': // 试卷列表-组织考试进入
case 'newPaperToAddExam': // 组卷保存组织考试
case 'editPaperToAddExam':
window.RCHistory.push('/examination-manage-index');
break;
case 'add':
case 'edit': // 考试列表-新建或编辑
case 'copy': // 考试列表-新建或编辑
props.freshList();
props.history.goBack();
break;
}
});
}
function disabledRangeTime(date: any, type: any) {
if (moment(date).isSame(moment(), 'day')) {
return {
disabledHours: () => {
const hours = [];
for (let i = 0; i < moment().hour(); i++) {
hours.push(i);
}
return hours;
},
disabledMinutes: () => {
const currentMinute = moment().minute();
const currentHour = moment(date).hour();
const minutes = [];
if (currentHour === moment().hour()) {
for (let i = 0; i < currentMinute; i++) {
minutes.push(i);
}
}
return minutes;
},
};
}
return {
disabledHours: () => [],
disabledMinutes: () => [],
disabledSeconds: () => [],
};
}
function handleGoBack() {
Modal.confirm({
title: '确定要返回吗?',
content: '返回后,本次编辑的内容将不被保存',
okText: '确认返回',
cancelText: '留在本页',
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
onOk: () => {
window.RCHistory.push('/examination-manage-index');
},
});
}
let title = '';
switch (props.type) {
case 'add':
case 'organizeExam':
case 'newPaperToAddExam':
case 'editPaperToAddExam':
title = '新建考试';
break;
case 'edit':
title = '编辑考试';
break;
case 'copy':
title = '复制考试';
break;
default:
break;
}
return (
<div className='page examPage'>
<Breadcrumbs navList={title} goBack={handleGoBack} />
<div className='box'>
<div className='show-tips'>
<ShowTips message='请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利' />
</div>{' '}
<div className='form'>
<div className='title'>基本信息</div>
<Form labelCol={{ span: 3 }} wrapperCol={{ span: 14 }} layout='horizontal'>
<Form.Item
label='考试名称'
validateStatus={check && (!examName ? '请输入考试名称' : examName.length > 40 && '考试名称最多40字') ? 'error' : ''}
help={check && (!examName ? '请输入考试名称' : examName.length > 40 && '考试名称最多40字')}
required>
<Input
placeholder='请输入考试名称(40字以内)'
maxLength={40}
value={examName}
onChange={(e) => {
setExamName(e.target.value);
}}
style={{ width: 320 }}
/>
</Form.Item>
<Form.Item label='选择试卷' validateStatus={check && !paperId ? 'error' : ''} help={check && !paperId && '请选择试卷'} required>
<Button
onClick={() => {
setShowModal(true);
}}>
{paperInfo.paperId ? '重新选择' : '选择试卷'}
</Button>
{paperInfo.paperId && (
<div className='paperTitle'>
<img src='https://image.xiaomaiketang.com/xm/pY5imEhjzw.png' alt='' /> {paperInfo.paperName}
</div>
)}
{paperInfo.paperId && (
<div className='table'>
<div className='header'>
<div className='item'>单选题</div>
<div className='item'>多选题</div>
<div className='item'>判断题</div>
<div className='item'>填空题</div>
<div className='item long'>不定项选择题</div>
<div className='item'>合计</div>
</div>
<div className='body-list'>
<div className='item'>{paperInfo.singleChoiceCnt || 0}</div>
<div className='item'>{paperInfo.multiChoiceCnt || 0}</div>
<div className='item'>{paperInfo.judgeCnt || 0}</div>
<div className='item'>{paperInfo.gapFillingCnt || 0}</div>
<div className='item long'>{paperInfo.indefiniteChoiceCnt || 0}</div>
<div className='item'>{examTotal}</div>
</div>
<div className='body-list'>
<div className='item'>{paperInfo.singleChoiceScore || 0}</div>
<div className='item'>{paperInfo.multiChoiceScore || 0}</div>
<div className='item'>{paperInfo.judgeScore || 0}</div>
<div className='item'>{paperInfo.gapFillingScore || 0}</div>
<div className='item long'>{paperInfo.indefiniteChoiceScore || 0}</div>
<div className='item'>{paperInfo.totalScore || 0}</div>
</div>
</div>
)}
</Form.Item>
<Form.Item
label={
<div>
<span>及格线</span>
<Tooltip title='默认为选中试卷所设置的及格线,可修改'>
<span className='icon iconfont' style={{ color: '#BFBFBF', marginLeft: 4 }}>
&#xe61d;
</span>
</Tooltip>
</div>
}
style={{ marginTop: 24 }}
validateStatus={check && !passRate ? 'error' : ''}
help={check && !passRate && '请输入及格线'}
required>
<InputNumber
value={passRate}
min={0}
max={100}
onChange={(value: any) => {
setPassRate(parseInt(value));
}}
style={{ width: 100 }}
/>
<span style={{ marginLeft: 4 }}>%</span>
<span style={{ marginLeft: 16, color: '#999' }}>
{` 总分(${paperInfo.totalScore || 0})*及格线(${passRate || 0}%)=及格分数(${passScore})`}
</span>
</Form.Item>
<Form.Item
label='考试有效期'
validateStatus={check && !examStartTime ? 'error' : ''}
help={check && !examStartTime && '请选择考试起止时间'}
required>
<RangePicker
style={{ width: 320 }}
showTime={{ defaultValue: [moment().add(5, 'minutes'), moment().add(5, 'minutes')] }}
ranges={{
近七天: [moment().add(5, 'minute'), moment().add(6, 'day').endOf('day')],
1个月: [moment().add(5, 'minute'), moment().add(1, 'month').endOf('day')],
3个月: [moment().add(5, 'minute'), moment().add(3, 'month').endOf('day')],
}}
disabledDate={disabledDate}
value={[examStartTime ? moment(Number(examStartTime)) : null, examEndTime ? moment(Number(examEndTime)) : null]}
disabledTime={disabledRangeTime}
format='YYYY/MM/DD HH:mm'
onChange={(date: any) => {
setStartTime(date && date[0]?.valueOf());
setExamEndTime(date && date[1]?.valueOf());
}}
/>
</Form.Item>
<Form.Item label='考试时长' validateStatus={check && !examDuration ? 'error' : ''} help={check && !examDuration && '请输入考试时长'} required>
<InputNumber
value={examDuration}
max={1440}
min={1}
onChange={(value: any) => {
setExamDuration(parseInt(value) as any);
}}
style={{ width: 100 }}
/>
<span style={{ marginLeft: 4 }}>分钟</span>
<span style={{ marginLeft: 16, color: '#999' }}>{` 时长不能超过1440分钟(24小时)`}</span>
</Form.Item>
<Form.Item label='考试说明' validateStatus={check && desclen > 1000 ? 'error' : ''} help={check && desclen > 1000 && '最多只能输入1000个字'}>
{(getData || props.type === 'add') && (
<GraphicsEditor
maxLimit={1000}
isIntro={true}
detail={{
content: examDesc,
}}
onChange={(val: any, len: any) => {
setExamDesc(val);
setDescLen(len);
}}
/>
)}
</Form.Item>
<div className='title' style={{ marginTop: 40 }}>
考试设置
</div>
<Form.Item label='身份验证' required>
<div style={{ display: 'flex', marginLeft: 4 }}>
<Switch
style={{ position: 'relative', top: 6 }}
checked={needPhone === 'NEED_PHONE_VERIFY'}
onChange={(val) => {
setNeedPhone(val ? 'NEED_PHONE_VERIFY' : 'DO_NOT_NEED_PHONE_VERIFY');
}}></Switch>
<div style={{ position: 'relative', top: 3, left: 8, color: '#999' }}>
<p>开启:需要绑定手机号的学员才能参加考试</p>
<p>关闭:微信/企业微信登陆直接参加考试</p>
</div>
</div>
</Form.Item>
<Form.Item label='选项乱序' required>
<div style={{ display: 'flex', marginLeft: 4 }}>
<Switch
style={{ position: 'relative', top: 6 }}
checked={needOptionDisorder === 'OPTION_RANDOM'}
onChange={(val) => {
setNeedOptionDisorder(val ? 'OPTION_RANDOM' : 'OPTION_SORT');
}}></Switch>
<div style={{ position: 'relative', top: 3, left: 8, color: '#999' }}>
<p>开启:选择题的选项随机排序</p>
<p>关闭:选择题按题目原有顺序展示</p>
</div>
</div>
</Form.Item>
<Form.Item label='考试结果查看' required>
<Radio.Group
onChange={(e: any) => {
setResultShow(e.target.value);
}}
value={resultShow}>
<Radio value={'IMMEDIATELY'}>交卷后立即显示考试结果</Radio>
<Radio value={'AFTER_EXAM_END'}>到达考试截止日期才显示结果</Radio>
</Radio.Group>
</Form.Item>
<Form.Item label=' 考试结果内容' required>
<Radio.Group
onChange={(e: any) => {
setResultContent(e.target.value);
}}
value={resultContent}>
<Radio value={'PASS_AND_SCORE'}>显示考试分数和是否及格</Radio>
<Radio value={'ONLY_SCORE'}>仅显示考试分数</Radio>
<Radio value={'ONLY_PASS'}>仅显示是否及格</Radio>
</Radio.Group>
</Form.Item>
<Form.Item label='答案与解析' required>
<Radio.Group
onChange={(e: any) => {
setAnswerAnalysis(e.target.value);
}}
value={answerAnalysis}>
<Radio value={'ANALYSE_AND_RIGHT_OR_WRONG'}>显示对错与解析</Radio>
<Radio value={'RIGHT_OR_WRONG'}>仅显示对错</Radio>
<Radio value={'CAN_NOT_CHECK'}>都不显示</Radio>
</Radio.Group>
</Form.Item>
</Form>
</div>
</div>
{showModal && (
<SelectPaperModal
onSelect={(info: any) => {
setPaperInfo(info);
}}
paperInfo={paperInfo}
close={() => {
setShowModal(false);
}}></SelectPaperModal>
)}
<div className='footer shrink-footer'>
<Button onClick={handleGoBack}>取消</Button>
<Button
onClick={() => {
setPreview(true);
}}>
预览
</Button>
<Button type='primary' onClick={handleSave}>
保存
</Button>
</div>
{preview && (
<PreviewModal
info={{
paperId, paperId,
startTime: examStartTime, startTime: examStartTime,
endTime: examEndTime, endTime: examEndTime,
...@@ -119,384 +525,17 @@ function AddExam(props: any) { ...@@ -119,384 +525,17 @@ function AddExam(props: any) {
resultContent, resultContent,
answerAnalysis, answerAnalysis,
resultShow, resultShow,
examDuration: (examDuration || 0) * 60 * 1000, examDuration,
passScore, passScore,
tenantId: User.getStoreId(), examTotal,
userId: User.getStoreUserId(), totalScore: paperInfo.totalScore,
source: 0, }}
examId: '' onClose={() => {
} setPreview(false);
}}></PreviewModal>
if (!param.examName) { )}
message.warning('请输入考试名称');
return
}
if (param.examName && param.examName.length > 40) {
message.warning('考试名称最多40字');
return
}
if (!paperId) {
message.warning('请选择试卷');
return
}
if (!passRate) {
message.warning('请输入及格线');
return
}
if (!examStartTime || !examEndTime) {
message.warning('请选择考试起止时间');
return
}
if (Number(examStartTime) < moment().valueOf()) {
message.warning('开始时间不能早于现在');
return
}
if (!examDuration) {
message.warning('请输入考试时长');
return
}
if (examStartTime + (examDuration as any) * 60 * 1000 > examEndTime) {
message.warning('考试时长不得超过考试有效期时长');
return
}
if (desclen > 1000) {
message.warning('内容过长,不能超过1000字');
return
}
request.current = true;
setTimeout(() => {
request.current = false
}, 2000)
if (props.type === 'edit') {
param.examId = match.params.id;
}
Service.Hades(props.type === 'edit' ? 'public/hades/editExam' : "public/hades/createExam", param).then((res) => {
message.success(props.type === 'edit' ? '编辑成功' : '创建成功');
switch (props.type) {
case "organizeExam": // 试卷列表-组织考试进入
case "newPaperToAddExam": // 组卷保存组织考试
case "editPaperToAddExam":
window.RCHistory.push("/examination-manage-index")
break;
case "add":
case "edit": // 考试列表-新建或编辑
case "copy": // 考试列表-新建或编辑
props.freshList()
props.history.goBack();
break;
}
})
}
function disabledRangeTime(date: any, type: any) {
if (moment(date).isSame(moment(), 'day')) {
return {
disabledHours: () => {
const hours = [];
for (let i = 0; i < moment().hour(); i++) {
hours.push(i);
}
return hours;
},
disabledMinutes: () => {
const currentMinute = moment().minute();
const currentHour = moment(date).hour();
const minutes = [];
if (currentHour === moment().hour()) {
for (let i = 0; i < currentMinute; i++) {
minutes.push(i);
}
}
return minutes;
},
};
}
return {
disabledHours: () => [],
disabledMinutes: () => [],
disabledSeconds: () => [],
};
}
function handleGoBack() {
Modal.confirm({
title: '确定要返回吗?',
content: '返回后,本次编辑的内容将不被保存',
okText: '确认返回',
cancelText: '留在本页',
icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>,
onOk: () => {
window.RCHistory.push("/examination-manage-index")
}
})
}
let title = '';
switch (props.type) {
case 'add':
case "organizeExam":
case "newPaperToAddExam":
case "editPaperToAddExam":
title = '新建考试';
break;
case 'edit':
title = '编辑考试';
break;
case 'copy':
title = '复制考试';
break;
default:
break;
}
return <div className="page examPage">
<Breadcrumbs navList={title} goBack={handleGoBack} />
<div className="box">
<div className="show-tips">
<ShowTips message="请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利" />
</div> <div className="form">
<div className="title">基本信息</div>
<Form
labelCol={{ span: 3 }}
wrapperCol={{ span: 14 }}
layout="horizontal"
>
<Form.Item label="考试名称"
validateStatus={(check && (!examName ? '请输入考试名称' : (examName.length > 40) && '考试名称最多40字')) ? 'error' : ''}
help={check && (!examName ? '请输入考试名称' : (examName.length > 40) && '考试名称最多40字')}
required>
<Input placeholder='请输入考试名称(40字以内)' maxLength={40} value={examName} onChange={(e) => {
setExamName(e.target.value)
}} style={{ width: 320 }} />
</Form.Item>
<Form.Item label="选择试卷"
validateStatus={(check && !paperId) ? 'error' : ''}
help={check && !paperId && '请选择试卷'}
required>
<Button onClick={() => { setShowModal(true) }} >{paperInfo.paperId ? '重新选择' : '选择试卷'}</Button>
{
paperInfo.paperId && <div className="paperTitle"><img src="https://image.xiaomaiketang.com/xm/pY5imEhjzw.png" alt="" /> {paperInfo.paperName}</div>
}
{
paperInfo.paperId && <div className="table">
<div className="header">
<div className="item">单选题</div>
<div className="item">多选题</div>
<div className="item">判断题</div>
<div className="item">填空题</div>
<div className="item long">不定项选择题</div>
<div className="item">合计</div>
</div>
<div className="body-list">
<div className="item">{paperInfo.singleChoiceCnt || 0}</div>
<div className="item">{paperInfo.multiChoiceCnt || 0}</div>
<div className="item">{paperInfo.judgeCnt || 0}</div>
<div className="item">{paperInfo.gapFillingCnt || 0}</div>
<div className="item long">{paperInfo.indefiniteChoiceCnt || 0}</div>
<div className="item">{examTotal}</div>
</div>
<div className="body-list">
<div className="item">{paperInfo.singleChoiceScore || 0}</div>
<div className="item">{paperInfo.multiChoiceScore || 0}</div>
<div className="item">{paperInfo.judgeScore || 0}</div>
<div className="item">{paperInfo.gapFillingScore || 0}</div>
<div className="item long">{paperInfo.indefiniteChoiceScore || 0}</div>
<div className="item">{paperInfo.totalScore || 0}</div>
</div>
</div>
}
</Form.Item>
<Form.Item
label={<div>
<span>及格线</span>
<Tooltip title="默认为选中试卷所设置的及格线,可修改">
<span className="icon iconfont" style={{ color: '#BFBFBF', marginLeft: 4 }}>&#xe61d;</span>
</Tooltip>
</div>}
style={{ marginTop: 24 }}
validateStatus={(check && !passRate) ? 'error' : ''}
help={check && !passRate && '请输入及格线'}
required
>
<InputNumber value={passRate} min={0} max={100} onChange={(value: any) => { setPassRate(parseInt(value)) }} style={{ width: 100 }} />
<span style={{ marginLeft: 4 }}>%
</span>
<span style={{ marginLeft: 16, color: "#999" }}>
{` 总分(${paperInfo.totalScore || 0})*及格线(${passRate || 0}%)=及格分数(${passScore})`}</span>
</Form.Item>
<Form.Item label="考试有效期"
validateStatus={(check && !examStartTime) ? 'error' : ''}
help={check && !examStartTime && '请选择考试起止时间'}
required>
<RangePicker
style={{ width: 320 }}
showTime={{ defaultValue: [moment().add(5, 'minutes'), moment().add(5, 'minutes')] }}
ranges={{
'近七天': [moment().add(5, 'minute'), moment().add(6, 'day').endOf('day')],
'近1个月': [moment().add(5, 'minute'), moment().add(1, 'month').endOf('day')],
'近3个月': [moment().add(5, 'minute'), moment().add(3, 'month').endOf('day')],
}}
disabledDate={disabledDate}
value={[
examStartTime ? moment(Number(examStartTime)) : null,
examEndTime ? moment(Number(examEndTime)) : null
]}
disabledTime={disabledRangeTime}
format="YYYY/MM/DD HH:mm"
onChange={(date: any) => {
setStartTime(date && date[0]?.valueOf());
setExamEndTime(date && date[1]?.valueOf());
}}
/>
</Form.Item>
<Form.Item label="考试时长"
validateStatus={(check && !examDuration) ? 'error' : ''}
help={check && !examDuration && '请输入考试时长'}
required>
<InputNumber value={examDuration} max={1440} min={1} onChange={(value: any) => { setExamDuration(parseInt(value) as any) }} style={{ width: 100 }} />
<span style={{ marginLeft: 4 }}>分钟
</span>
<span style={{ marginLeft: 16, color: "#999" }}>
{` 时长不能超过1440分钟(24小时)`}</span>
</Form.Item>
<Form.Item label="考试说明"
validateStatus={(check && (desclen > 1000)) ? 'error' : ''}
help={check && (desclen > 1000) && '最多只能输入1000个字'}
>
{
(getData || (props.type === 'add')) && <GraphicsEditor
maxLimit={1000}
isIntro={true}
detail={{
content: examDesc
}}
onChange={(val: any, len: any) => { setExamDesc(val); setDescLen(len) }}
/>
}
</Form.Item>
<div className="title" style={{ marginTop: 40 }}>考试设置</div>
<Form.Item label="身份验证" required>
<div style={{ display: 'flex', marginLeft: 4, }}>
<Switch style={{ position: 'relative', top: 6 }}
checked={needPhone == 'NEED_PHONE_VERIFY'}
onChange={(val) => { setNeedPhone(val ? 'NEED_PHONE_VERIFY' : 'DO_NOT_NEED_PHONE_VERIFY') }}
></Switch>
<div style={{ position: 'relative', top: 3, left: 8, color: "#999" }}><p>开启:需要绑定手机号的学员才能参加考试</p>
<p>关闭:微信/企业微信登陆直接参加考试</p></div>
</div>
</Form.Item>
<Form.Item label="选项乱序" required>
<div style={{ display: 'flex', marginLeft: 4, }}>
<Switch style={{ position: 'relative', top: 6 }}
checked={needOptionDisorder == 'OPTION_RANDOM'}
onChange={(val) => { setNeedOptionDisorder(val ? 'OPTION_RANDOM' : 'OPTION_SORT') }}
></Switch>
<div style={{ position: 'relative', top: 3, left: 8, color: "#999" }}><p>开启:选择题的选项随机排序</p>
<p>关闭:选择题按题目原有顺序展示</p></div>
</div>
</Form.Item>
<Form.Item label="考试结果查看" required>
<Radio.Group onChange={(e: any) => { setResultShow(e.target.value) }} value={resultShow}>
<Radio value={'IMMEDIATELY'}>交卷后立即显示考试结果</Radio>
<Radio value={'AFTER_EXAM_END'}>到达考试截止日期才显示结果</Radio>
</Radio.Group>
</Form.Item>
<Form.Item label=" 考试结果内容" required>
<Radio.Group onChange={(e: any) => { setResultContent(e.target.value) }} value={resultContent}>
<Radio value={'PASS_AND_SCORE'}>显示考试分数和是否及格</Radio>
<Radio value={'ONLY_SCORE'}>仅显示考试分数</Radio>
<Radio value={'ONLY_PASS'}>仅显示是否及格</Radio>
</Radio.Group>
</Form.Item>
<Form.Item label="答案与解析" required>
<Radio.Group onChange={(e: any) => { setAnswerAnalysis(e.target.value) }} value={answerAnalysis}>
<Radio value={'ANALYSE_AND_RIGHT_OR_WRONG'}>显示对错与解析</Radio>
<Radio value={'RIGHT_OR_WRONG'}>仅显示对错</Radio>
<Radio value={'CAN_NOT_CHECK'}>都不显示</Radio>
</Radio.Group>
</Form.Item>
</Form>
</div>
</div>
{
showModal && <SelectPaperModal onSelect={(info: any) => {
setPaperInfo(info)
}} paperInfo={paperInfo} close={() => { setShowModal(false) }}></SelectPaperModal>
}
<div className="footer shrink-footer">
<Button onClick={handleGoBack}>取消</Button>
<Button onClick={() => { setPreview(true) }}>预览</Button>
<Button type="primary" onClick={handleSave}>保存</Button>
</div>
{
preview && <PreviewModal
info={{
paperId,
startTime: examStartTime,
endTime: examEndTime,
examName,
passRate: passRate / 100,
examStartTime,
examEndTime,
examDesc,
needPhone,
needOptionDisorder,
resultContent,
answerAnalysis,
resultShow,
examDuration,
passScore,
examTotal,
totalScore: paperInfo.totalScore
}}
onClose={() => { setPreview(false) }}></PreviewModal>
}
</div> </div>
);
} }
export default withRouter(AddExam);
export default withRouter(AddExam);
\ No newline at end of file
import React, { useState, useRef, useEffect, useContext } from 'react'; import React, { useState, useRef, useEffect } from "react";
import Service from '@/common/js/service'; import Service from "@/common/js/service";
import { PageControl } from '@/components'; import { PageControl } from "@/components";
import { Input, Select, Tooltip, Button, Table } from 'antd'; import { Input, Select, Tooltip, Button } from "antd";
import User from '@/common/js/user'; import User from "@/common/js/user";
import moment from 'moment'; import { XMTable } from "@/components";
import './userData.less'; import college from "@/common/lottie/college.json";
const { Search } = Input; import "./userData.less";
const { Option } = Select;
interface sortType { interface sortType {
type: 'ascend' | 'descend' | null | undefined; type: "ascend" | "descend" | null | undefined;
} }
function ExamData(props: any) { function ExamData(props: any) {
...@@ -18,37 +17,37 @@ function ExamData(props: any) { ...@@ -18,37 +17,37 @@ function ExamData(props: any) {
}; };
const examDataInit: any = {}; const examDataInit: any = {};
const queryInit: any = { current: 1, size: 10, order: 'SORT_ASC' }; const queryInit: any = { current: 1, size: 10, order: "SORT_ASC" };
const [examData, setUserData] = useState(examDataInit); const [examData, setUserData] = useState(examDataInit);
const [list, setList] = useState([]); const [list, setList] = useState([]);
const [query, setQuery] = useState(queryInit); const [query, setQuery] = useState(queryInit);
const [total, setTotal] = useState(0); const [total, setTotal] = useState(0);
const [field, setfield] = useState(''); const [field, setfield] = useState("");
const [allData, setAllData] = useState(0); const [allData, setAllData] = useState(0);
const [order, setOrder] = useState(sortStatus.type); const [order, setOrder] = useState(sortStatus.type);
const questionTypeList = { const questionTypeList = {
SINGLE_CHOICE: '单选题', SINGLE_CHOICE: "单选题",
MULTI_CHOICE: '多选题', MULTI_CHOICE: "多选题",
JUDGE: '判断题', JUDGE: "判断题",
GAP_FILLING: '填空题', GAP_FILLING: "填空题",
INDEFINITE_CHOICE: '不定项选择题', INDEFINITE_CHOICE: "不定项选择题",
}; };
const userTypeEnum = { const userTypeEnum = {
WORK_WE_CHAT: '企业微信', WORK_WE_CHAT: "企业微信",
WE_CHAT: '微信', WE_CHAT: "微信",
}; };
const userExamStateEnum = { const userExamStateEnum = {
EXAM: '进行中', EXAM: "进行中",
LACK_EXAM: '缺考', LACK_EXAM: "缺考",
FINISH_EXAM: '已考试', FINISH_EXAM: "已考试",
}; };
const orderEnum = { const orderEnum = {
currentAccuracy: { currentAccuracy: {
ascend: 'ACCURACY_ASC', ascend: "ACCURACY_ASC",
descend: 'ACCURACY_DESC', descend: "ACCURACY_DESC",
}, },
}; };
...@@ -64,7 +63,7 @@ function ExamData(props: any) { ...@@ -64,7 +63,7 @@ function ExamData(props: any) {
}, [query]); }, [query]);
function queryExamUserData() { function queryExamUserData() {
Service.Hades('public/hades/queryExamQuestionData', { Service.Hades("public/hades/queryExamQuestionData", {
examId: props.examId, examId: props.examId,
tenantId: User.getStoreId(), tenantId: User.getStoreId(),
userId: User.getStoreUserId(), userId: User.getStoreUserId(),
...@@ -75,7 +74,7 @@ function ExamData(props: any) { ...@@ -75,7 +74,7 @@ function ExamData(props: any) {
} }
function queryExamUserDataList() { function queryExamUserDataList() {
Service.Hades('public/hades/queryExamQuestionDataList', { Service.Hades("public/hades/queryExamQuestionDataList", {
...query, ...query,
examId: props.examId, examId: props.examId,
tenantId: User.getStoreId(), tenantId: User.getStoreId(),
...@@ -92,35 +91,38 @@ function ExamData(props: any) { ...@@ -92,35 +91,38 @@ function ExamData(props: any) {
const columns = [ const columns = [
{ {
title: '序号', title: "序号",
dataIndex: 'sort', dataIndex: "sort",
width: 60, width: 60,
render: (text: any, record: any, index: any) => <span>{index + 1}</span>, render: (text: any, record: any, index: any) => <span>{index + 1}</span>,
}, },
{ {
title: '题目', title: "题目",
dataIndex: 'questionStem', dataIndex: "questionStem",
ellipsis: true, ellipsis: true,
width: 350, width: 350,
render: (val: any) => { render: (val: any) => {
var handleVal = val; var handleVal = val;
handleVal = handleVal.replace(/<(?!img|input).*?>/g, ''); handleVal = handleVal.replace(/<(?!img|input).*?>/g, "");
handleVal = handleVal.replace(/<\s?input[^>]*>/gi, '_、'); handleVal = handleVal.replace(/<\s?input[^>]*>/gi, "_、");
handleVal = handleVal.replace(/\&nbsp\;/gi, ' '); handleVal = handleVal.replace(/\&nbsp\;/gi, " ");
return ( return (
<Tooltip <Tooltip
overlayClassName='aid-tool-list' overlayClassName="aid-tool-list"
title={<div style={{ maxWidth: 700, width: 'auto' }}>{handleVal}</div>} title={
placement='topLeft' <div style={{ maxWidth: 700, width: "auto" }}>{handleVal}</div>
overlayStyle={{ maxWidth: 700 }}> }
placement="topLeft"
overlayStyle={{ maxWidth: 700 }}
>
{handleVal} {handleVal}
</Tooltip> </Tooltip>
); );
}, },
}, },
{ {
title: '题型', title: "题型",
dataIndex: 'questionType', dataIndex: "questionType",
render: (text: any) => <span>{(questionTypeList as any)[text]}</span>, render: (text: any) => <span>{(questionTypeList as any)[text]}</span>,
filters: Object.keys(questionTypeList).map((key) => { filters: Object.keys(questionTypeList).map((key) => {
return { return {
...@@ -130,25 +132,33 @@ function ExamData(props: any) { ...@@ -130,25 +132,33 @@ function ExamData(props: any) {
}), }),
}, },
{ {
title: '本次正确率', title: "本次正确率",
dataIndex: 'currentAccuracy', dataIndex: "currentAccuracy",
sorter: true, sorter: true,
sortOrder: field === 'currentAccuracy' ? order : sortStatus.type, sortOrder: field === "currentAccuracy" ? order : sortStatus.type,
render: (text: any) => <span>{parseInt((text * 100) as any)}%</span>, render: (text: any) => <span>{parseInt((text * 100) as any)}%</span>,
}, },
{ {
title: ( title: (
<div> <div>
历史正确率{' '} 历史正确率{" "}
<Tooltip overlayClassName='tool-list' title='包含本次考试正确率' placement='top' overlayStyle={{ maxWidth: 700 }}> <Tooltip
{' '} overlayClassName="tool-list"
<span style={{ color: 'rgba(191, 191, 191, 1)' }} className='icon iconfont'> title="包含本次考试正确率"
placement="top"
overlayStyle={{ maxWidth: 700 }}
>
{" "}
<span
style={{ color: "rgba(191, 191, 191, 1)",fontWeight: 400 }}
className="icon iconfont"
>
&#xe61d; &#xe61d;
</span> </span>
</Tooltip> </Tooltip>
</div> </div>
), ),
dataIndex: 'totalAccuracy', dataIndex: "totalAccuracy",
render: (text: any) => <span>{parseInt((text * 100) as any)}%</span>, render: (text: any) => <span>{parseInt((text * 100) as any)}%</span>,
}, },
]; ];
...@@ -174,78 +184,97 @@ function ExamData(props: any) { ...@@ -174,78 +184,97 @@ function ExamData(props: any) {
} }
function download() { function download() {
Service.Hades('public/hades/exportExamData', { Service.Hades("public/hades/exportExamData", {
// ...query, // ...query,
examId: props.examId, examId: props.examId,
exportDataType: 'EXAM_QUESTION_DATA', exportDataType: "EXAM_QUESTION_DATA",
tenantId: User.getStoreId(), tenantId: User.getStoreId(),
userId: User.getStoreUserId(), userId: User.getStoreUserId(),
source: 0, source: 0,
}).then((res) => { }).then((res) => {
const dom = (document as any).getElementById('load-play-back-excel'); const dom = (document as any).getElementById("load-play-back-excel");
dom.setAttribute('href', res.result); dom.setAttribute("href", res.result);
dom.click(); dom.click();
}); });
} }
return ( return (
<div className='rr'> <div className="rr">
<a download id='load-play-back-excel' style={{ position: 'absolute', left: '-10000px' }}></a> <a
<div className='dataPanal'> download
id="load-play-back-excel"
style={{ position: "absolute", left: "-10000px" }}
></a>
<div className="dataPanal">
{!!examData.singleChoiceCnt && ( {!!examData.singleChoiceCnt && (
<div className='item'> <div className="item">
<div className='num'>{Math.round((examData.singleChoiceAccuracy || 0) * 100)}%</div> <div className="num">
<div className='percent'>正确率</div> {Math.round((examData.singleChoiceAccuracy || 0) * 100)}%
<div className='subTitle'> </div>
<div className='type'> <div className="percent">正确率</div>
<span className='icon iconfont'>&#xe7fa;</span>单选题 <span>(共{examData.singleChoiceCnt}题)</span> <div className="subTitle">
<div className="type">
<span className="icon iconfont">&#xe7fa;</span>单选题{" "}
<span>(共{examData.singleChoiceCnt}题)</span>
</div> </div>
</div> </div>
</div> </div>
)} )}
{!!examData.multiChoiceCnt && ( {!!examData.multiChoiceCnt && (
<div className='item'> <div className="item">
<div className='num'>{Math.round((examData.multiChoiceAccuracy || 0) * 100)}%</div> <div className="num">
<div className='percent'>正确率</div> {Math.round((examData.multiChoiceAccuracy || 0) * 100)}%
<div className='subTitle'> </div>
<div className='type'> <div className="percent">正确率</div>
<span className='icon iconfont'>&#xe7fb;</span>多选题<span>(共{examData.multiChoiceCnt}题)</span> <div className="subTitle">
<div className="type">
<span className="icon iconfont">&#xe7fb;</span>多选题
<span>(共{examData.multiChoiceCnt}题)</span>
</div> </div>
</div> </div>
</div> </div>
)} )}
{!!examData.judgeCnt && ( {!!examData.judgeCnt && (
<div className='item'> <div className="item">
<div className='num'>{Math.round((examData.judgeAccuracy || 0) * 100)}%</div> <div className="num">
<div className='percent'>正确率</div> {Math.round((examData.judgeAccuracy || 0) * 100)}%
<div className='subTitle'> </div>
<div className='type'> <div className="percent">正确率</div>
<span className='icon iconfont'>&#xe7fc;</span>判断题<span>(共{examData.judgeCnt}题)</span> <div className="subTitle">
<div className="type">
<span className="icon iconfont">&#xe7fc;</span>判断题
<span>(共{examData.judgeCnt}题)</span>
</div> </div>
</div> </div>
</div> </div>
)} )}
{!!examData.gapFillingCnt && ( {!!examData.gapFillingCnt && (
<div className='item'> <div className="item">
<div className='num'>{Math.round((examData.gapFillingAccuracy || 0) * 100)}%</div> <div className="num">
<div className='percent'>正确率</div> {Math.round((examData.gapFillingAccuracy || 0) * 100)}%
<div className='subTitle'> </div>
<div className='type'> <div className="percent">正确率</div>
<span className='icon iconfont'>&#xe7fd;</span>填空题<span>(共{examData.gapFillingCnt}题)</span> <div className="subTitle">
<div className="type">
<span className="icon iconfont">&#xe7fd;</span>填空题
<span>(共{examData.gapFillingCnt}题)</span>
</div> </div>
</div> </div>
</div> </div>
)} )}
{!!examData.indefiniteChoiceCnt && ( {!!examData.indefiniteChoiceCnt && (
<div className='item'> <div className="item">
<div className='num'>{Math.round((examData.indefiniteChoiceAccuracy || 0) * 100)}%</div> <div className="num">
<div className='percent'>正确率</div> {Math.round((examData.indefiniteChoiceAccuracy || 0) * 100)}%
<div className='subTitle'> </div>
<div className='type'> <div className="percent">正确率</div>
<span className='icon iconfont'>&#xe7fe;</span>不定项选择题 <span>(共{examData.indefiniteChoiceCnt}题)</span> <div className="subTitle">
<div className="type">
<span className="icon iconfont">&#xe7fe;</span>不定项选择题{" "}
<span>(共{examData.indefiniteChoiceCnt}题)</span>
</div> </div>
</div> </div>
</div> </div>
...@@ -257,11 +286,22 @@ function ExamData(props: any) { ...@@ -257,11 +286,22 @@ function ExamData(props: any) {
</Button> </Button>
)} )}
<div className='content'> <div className="content">
<Table bordered size='small' columns={columns} dataSource={list} onChange={onChange} pagination={false}></Table> <XMTable
renderEmpty={{
image: college,
description: '暂无数据'
}}
bordered
size="small"
columns={columns}
dataSource={list}
onChange={onChange}
pagination={false}
></XMTable>
{total > 0 && ( {total > 0 && (
<PageControl <PageControl
size='small' size="small"
current={query.current - 1} current={query.current - 1}
pageSize={query.size} pageSize={query.size}
total={total} total={total}
......
...@@ -11,6 +11,8 @@ import { XMContext } from '@/store/context'; ...@@ -11,6 +11,8 @@ import { XMContext } from '@/store/context';
import ExamShareModal from './ExamShareModal'; import ExamShareModal from './ExamShareModal';
import DataAnalysic from './DataAnalysic'; import DataAnalysic from './DataAnalysic';
import PreviewModal from './PreviewModal'; import PreviewModal from './PreviewModal';
import college from '@/common/lottie/college.json';
import './index.less'; import './index.less';
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
const { Search } = Input; const { Search } = Input;
...@@ -426,6 +428,7 @@ function ExaminationManager(props: any) { ...@@ -426,6 +428,7 @@ function ExaminationManager(props: any) {
pagination={false} pagination={false}
style={{ margin: '0px 0 16px' }} style={{ margin: '0px 0 16px' }}
renderEmpty={{ renderEmpty={{
image: college,
description: <span style={{ display: 'block', paddingBottom: 24 }}>暂无数据</span>, description: <span style={{ display: 'block', paddingBottom: 24 }}>暂无数据</span>,
}}></XMTable> }}></XMTable>
{total > 0 && ( {total > 0 && (
......
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useEffect } from "react";
import Service from '@/common/js/service'; import Service from "@/common/js/service";
import { PageControl } from '@/components'; import { PageControl } from "@/components";
import { Input, Select, Tooltip, Table, Button } from 'antd'; import { Input, Select, Tooltip, Table, Button } from "antd";
import { ColumnsType } from 'antd/es/table'; import { ColumnsType } from "antd/es/table";
import User from '@/common/js/user'; import User from "@/common/js/user";
import moment from 'moment'; import moment from "moment";
import './userData.less'; import { XMTable } from "@/components";
import college from "@/common/lottie/college.json";
import "./userData.less";
const { Search } = Input; const { Search } = Input;
const { Option } = Select; const { Option } = Select;
declare var window: any; declare var window: any;
interface sortType { interface sortType {
type: 'ascend' | 'descend' | null | undefined; type: "ascend" | "descend" | null | undefined;
} }
interface User { interface User {
key: number; key: number;
...@@ -29,45 +31,45 @@ function DataAnalysic(props: any) { ...@@ -29,45 +31,45 @@ function DataAnalysic(props: any) {
const [list, setList] = useState([]); const [list, setList] = useState([]);
const [query, setQuery] = useState(queryInit); const [query, setQuery] = useState(queryInit);
const [total, setTotal] = useState(0); const [total, setTotal] = useState(0);
const [field, setfield] = useState(''); const [field, setfield] = useState("");
const [allData, setAllData] = useState(0); const [allData, setAllData] = useState(0);
const [order, setOrder] = useState(sortStatus.type); const [order, setOrder] = useState(sortStatus.type);
const userTypeEnum = { const userTypeEnum = {
WORK_WE_CHAT: '企业微信', WORK_WE_CHAT: "企业微信",
WE_CHAT: '微信', WE_CHAT: "微信",
}; };
const userExamStateEnum = { const userExamStateEnum = {
EXAM: '进行中', EXAM: "进行中",
LACK_EXAM: '缺考', LACK_EXAM: "缺考",
FINISH_EXAM: '已考试', FINISH_EXAM: "已考试",
}; };
const ExamPassColorEnum = { const ExamPassColorEnum = {
EXAM_FAIL: 'rgba(255, 79, 79, 1)', EXAM_FAIL: "rgba(255, 79, 79, 1)",
EXAM_PASS: 'rgba(59, 189, 170, 1)', EXAM_PASS: "rgba(59, 189, 170, 1)",
}; };
const ExamPassEnum = { const ExamPassEnum = {
EXAM_FAIL: '不及格', EXAM_FAIL: "不及格",
EXAM_PASS: '及格', EXAM_PASS: "及格",
}; };
const userExamStateColorEnum = { const userExamStateColorEnum = {
EXAM: 'rgba(35, 143, 255, 1)', EXAM: "rgba(35, 143, 255, 1)",
LACK_EXAM: 'rgba(204, 204, 204, 1)', LACK_EXAM: "rgba(204, 204, 204, 1)",
FINISH_EXAM: 'rgba(47, 200, 60, 1)', FINISH_EXAM: "rgba(47, 200, 60, 1)",
}; };
const orderEnum = { const orderEnum = {
score: { score: {
ascend: 'EXAM_SCORE_ASC', ascend: "EXAM_SCORE_ASC",
descend: 'EXAM_SCORE_DESC', descend: "EXAM_SCORE_DESC",
}, },
userDuration: { userDuration: {
ascend: 'USER_DURATION_ASC', ascend: "USER_DURATION_ASC",
descend: 'USER_DURATION_DESC', descend: "USER_DURATION_DESC",
}, },
}; };
...@@ -83,7 +85,7 @@ function DataAnalysic(props: any) { ...@@ -83,7 +85,7 @@ function DataAnalysic(props: any) {
}, [query]); }, [query]);
function queryExamUserData() { function queryExamUserData() {
Service.Hades('public/hades/queryExamUserData', { Service.Hades("public/hades/queryExamUserData", {
examId: props.examId, examId: props.examId,
tenantId: User.getStoreId(), tenantId: User.getStoreId(),
userId: User.getStoreUserId(), userId: User.getStoreUserId(),
...@@ -94,7 +96,7 @@ function DataAnalysic(props: any) { ...@@ -94,7 +96,7 @@ function DataAnalysic(props: any) {
} }
function queryExamUserDataList() { function queryExamUserDataList() {
Service.Hades('public/hades/queryExamUserDataList', { Service.Hades("public/hades/queryExamUserDataList", {
...query, ...query,
examId: props.examId, examId: props.examId,
tenantId: User.getStoreId(), tenantId: User.getStoreId(),
...@@ -111,79 +113,102 @@ function DataAnalysic(props: any) { ...@@ -111,79 +113,102 @@ function DataAnalysic(props: any) {
const columns: ColumnsType<User> = [ const columns: ColumnsType<User> = [
{ {
title: '学员', title: "学员",
dataIndex: 'userName', dataIndex: "userName",
render: (text: any, record: any) => ( render: (text: any, record: any) => (
<span> <span>
{text} {text}
<span style={{ color: record.userSource === 'WORK_WE_CHAT' ? 'rgba(255, 157, 20, 1)' : 'rgba(29, 204, 101, 1)' }}> <span
style={{
color:
record.userSource === "WORK_WE_CHAT"
? "rgba(255, 157, 20, 1)"
: "rgba(29, 204, 101, 1)",
}}
>
@{(userTypeEnum as any)[record.userSource]} @{(userTypeEnum as any)[record.userSource]}
</span> </span>
</span> </span>
), ),
}, },
{ {
title: '手机号', title: "手机号",
dataIndex: 'phone', dataIndex: "phone",
}, },
{ {
title: '考试状态', title: "考试状态",
dataIndex: 'userExamState', dataIndex: "userExamState",
render: (text: any) => ( render: (text: any) => (
<span> <span>
{' '} {" "}
<span className='exstatus' style={{ background: (userExamStateColorEnum as any)[text] }}></span> {(userExamStateEnum as any)[text]} <span
className="exstatus"
style={{ background: (userExamStateColorEnum as any)[text] }}
></span>{" "}
{(userExamStateEnum as any)[text]}
</span> </span>
), ),
}, },
{ {
title: '考试成绩', title: "考试成绩",
dataIndex: 'score', dataIndex: "score",
sorter: true, sorter: true,
sortOrder: field === 'score' ? order : sortStatus.type, sortOrder: field === "score" ? order : sortStatus.type,
render: (text: any, record: any) => ( render: (text: any, record: any) => (
<span> <span>
{' '} {" "}
{text}{' '} {text}{" "}
<span <span
style={{ style={{
border: `1px solid ${(ExamPassColorEnum as any)[record.examPass]}`, border: `1px solid ${
(ExamPassColorEnum as any)[record.examPass]
}`,
fontSize: 12, fontSize: 12,
color: (ExamPassColorEnum as any)[record.examPass], color: (ExamPassColorEnum as any)[record.examPass],
display: 'inline-block', display: "inline-block",
padding: '0px 2px', padding: "0px 2px",
}}> }}
>
{(ExamPassEnum as any)[record.examPass]} {(ExamPassEnum as any)[record.examPass]}
</span> </span>
</span> </span>
), ),
}, },
{ {
title: '进入考试时间', title: "进入考试时间",
dataIndex: 'examStartTime', dataIndex: "examStartTime",
render: (text: any) => <span>{moment(text).format('YYYY-MM-DD HH:mm')}</span>, render: (text: any) => (
<span>{moment(text).format("YYYY-MM-DD HH:mm")}</span>
),
}, },
{ {
title: '考试用时', title: "考试用时",
dataIndex: 'userDuration', dataIndex: "userDuration",
sorter: true, sorter: true,
sortOrder: field === 'userDuration' ? order : sortStatus.type, sortOrder: field === "userDuration" ? order : sortStatus.type,
render: (text: any, record: any) => <span>{record.userExamState === 'FINISH_EXAM' ? window.formatHourTime(text) : '-'} </span>, render: (text: any, record: any) => (
<span>
{record.userExamState === "FINISH_EXAM"
? window.formatHourTime(text)
: "-"}{" "}
</span>
),
}, },
//TODO: //TODO:
{ {
title: '操作', title: "操作",
key: '', key: "",
dataIndex: 'edit', dataIndex: "edit",
render: (value: any, record: any) => { render: (value: any, record: any) => {
return ( return (
<Choose> <Choose>
<When condition={record.userExamState === 'FINISH_EXAM'}> <When condition={record.userExamState === "FINISH_EXAM"}>
<div <div
className='answer-detail' className="answer-detail"
onClick={() => { onClick={() => {
checkAnswerDetail(record); checkAnswerDetail(record);
}}> }}
>
答题详情 答题详情
</div> </div>
</When> </When>
...@@ -204,16 +229,16 @@ function DataAnalysic(props: any) { ...@@ -204,16 +229,16 @@ function DataAnalysic(props: any) {
} }
function download() { function download() {
Service.Hades('public/hades/exportExamData', { Service.Hades("public/hades/exportExamData", {
// ...query, // ...query,
examId: props.examId, examId: props.examId,
exportDataType: 'EXAM_USER_DATA', exportDataType: "EXAM_USER_DATA",
tenantId: User.getStoreId(), tenantId: User.getStoreId(),
userId: User.getStoreUserId(), userId: User.getStoreUserId(),
source: 0, source: 0,
}).then((res) => { }).then((res) => {
const dom = (document as any).getElementById('load-play-back-excel'); const dom = (document as any).getElementById("load-play-back-excel");
dom.setAttribute('href', res.result); dom.setAttribute("href", res.result);
dom.click(); dom.click();
}); });
} }
...@@ -227,69 +252,92 @@ function DataAnalysic(props: any) { ...@@ -227,69 +252,92 @@ function DataAnalysic(props: any) {
} }
return ( return (
<div className='rr'> <div className="rr">
<a target='_blank' download id='load-play-back-excel' style={{ position: 'absolute', left: '-10000px' }}> <a
target="_blank"
download
id="load-play-back-excel"
style={{ position: "absolute", left: "-10000px" }}
>
111 111
</a> </a>
<div className='dataPanal'> <div className="dataPanal">
<div className='item'> <div className="item">
<div className='num'>{useData.joinCnt || 0}</div> <div className="num">{useData.joinCnt || 0}</div>
<div className='percent'></div> <div className="percent"></div>
<div className='subTitle'>参与人数</div> <div className="subTitle">参与人数</div>
</div> </div>
<div className='item'> <div className="item">
<div className='num'>{useData.finishCnt || 0}</div> <div className="num">{useData.finishCnt || 0}</div>
<div className='percent'>占比{parseInt(((useData.finishCnt || 0) / (useData.joinCnt || 1)) * 100 + '')}%</div> <div className="percent">
<div className='subTitle'>完成考试数 (人)</div> 占比
{parseInt(
((useData.finishCnt || 0) / (useData.joinCnt || 1)) * 100 + ""
)}
%
</div>
<div className="subTitle">完成考试数 (人)</div>
</div> </div>
<div className='item'> <div className="item">
<div className='num'>{useData.passCnt || 0}</div> <div className="num">{useData.passCnt || 0}</div>
<div className='percent'>占比{parseInt(((useData.passCnt || 0) / (useData.finishCnt || 1)) * 100 + '')}%</div> <div className="percent">
<div className='subTitle'>及格数 (人)</div> 占比
{parseInt(
((useData.passCnt || 0) / (useData.finishCnt || 1)) * 100 + ""
)}
%
</div>
<div className="subTitle">及格数 (人)</div>
</div> </div>
<div className='item'> <div className="item">
<div className='num'>{useData.averageScore || 0}</div> <div className="num">{useData.averageScore || 0}</div>
<div className='percent'>总分{props.examDetail?.examPaper?.totalScore}</div> <div className="percent">
<div className='subTitle'>平均分</div> 总分{props.examDetail?.examPaper?.totalScore}
</div>
<div className="subTitle">平均分</div>
</div> </div>
<div className='item'> <div className="item">
<div className='num'> {window.formatHourTime(useData.averageDuration || 0)} </div> <div className="num">
<div className='percent'></div> {" "}
<div className='subTitle'>平均用时</div> {window.formatHourTime(useData.averageDuration || 0)}{" "}
</div>
<div className="percent"></div>
<div className="subTitle">平均用时</div>
</div> </div>
</div> </div>
<div className='xm-search-filter' style={{ marginTop: '24px' }}> <div className="xm-search-filter" style={{ marginTop: "24px" }}>
<div style={{ display: 'flex' }}> <div style={{ display: "flex" }}>
<div className='search-condition'> <div className="search-condition">
<div className='search-condition__item'> <div className="search-condition__item">
<span className='search-name'>学员:</span> <span className="search-name">学员:</span>
<Search <Search
value={query.examName} value={query.examName}
className='search-input' className="search-input"
placeholder='搜索学员名或手机号' placeholder="搜索学员名或手机号"
onChange={(e) => { onChange={(e) => {
const _query = { ...query }; const _query = { ...query };
_query.searchKey = e.target.value; _query.searchKey = e.target.value;
setQuery(_query); setQuery(_query);
}} }}
onSearch={() => {}} onSearch={() => {}}
enterButton={<span className='icon iconfont'>&#xe832;</span>} enterButton={<span className="icon iconfont">&#xe832;</span>}
/> />
</div> </div>
<div className='search-condition__item'> <div className="search-condition__item">
<span className='search-name'>学员类型:</span> <span className="search-name">学员类型:</span>
<Select <Select
value={query.userSource} value={query.userSource}
placeholder='请选择学员类型' placeholder="请选择学员类型"
onChange={(val) => { onChange={(val) => {
const _query = { ...query }; const _query = { ...query };
_query.userSource = val; _query.userSource = val;
setQuery(_query); setQuery(_query);
}} }}
className='search-input' className="search-input"
allowClear> allowClear
>
{Object.keys(userTypeEnum).map((key: any) => { {Object.keys(userTypeEnum).map((key: any) => {
return ( return (
<Option value={key} key={key}> <Option value={key} key={key}>
...@@ -300,18 +348,19 @@ function DataAnalysic(props: any) { ...@@ -300,18 +348,19 @@ function DataAnalysic(props: any) {
</Select> </Select>
</div> </div>
<div className='search-condition__item'> <div className="search-condition__item">
<span className='search-name'>考试状态:</span> <span className="search-name">考试状态:</span>
<Select <Select
value={query.userExamState} value={query.userExamState}
placeholder='请选择考试状态' placeholder="请选择考试状态"
onChange={(val) => { onChange={(val) => {
const _query = { ...query }; const _query = { ...query };
_query.userExamState = val; _query.userExamState = val;
setQuery(_query); setQuery(_query);
}} }}
className='search-input' className="search-input"
allowClear> allowClear
>
{Object.keys(userExamStateEnum).map((key: any) => { {Object.keys(userExamStateEnum).map((key: any) => {
return ( return (
<Option value={key} key={key}> <Option value={key} key={key}>
...@@ -322,15 +371,16 @@ function DataAnalysic(props: any) { ...@@ -322,15 +371,16 @@ function DataAnalysic(props: any) {
</Select> </Select>
</div> </div>
</div> </div>
<div className='reset-fold-area'> <div className="reset-fold-area">
<Tooltip title='清空筛选'> <Tooltip title="清空筛选">
<span <span
className='resetBtn iconfont icon' className="resetBtn iconfont icon"
onClick={() => { onClick={() => {
setfield(''); setfield("");
setQuery({ current: 1, size: 10 }); setQuery({ current: 1, size: 10 });
}}> }}
&#xe61b;{' '} >
&#xe61b;{" "}
</span> </span>
</Tooltip> </Tooltip>
</div> </div>
...@@ -342,11 +392,23 @@ function DataAnalysic(props: any) { ...@@ -342,11 +392,23 @@ function DataAnalysic(props: any) {
</Button> </Button>
)} )}
<div className='content analysic-content'> <div className="content analysic-content">
<Table bordered size='small' rowClassName='analysic-content-row' columns={columns} dataSource={list} onChange={onChange} pagination={false}></Table> <XMTable
renderEmpty={{
image: college,
description: '暂无数据'
}}
bordered
size="small"
rowClassName="analysic-content-row"
columns={columns}
dataSource={list}
onChange={onChange}
pagination={false}
></XMTable>
{total > 0 && ( {total > 0 && (
<PageControl <PageControl
size='small' size="small"
current={query.current - 1} current={query.current - 1}
pageSize={query.size} pageSize={query.size}
total={total} total={total}
......
...@@ -31,6 +31,8 @@ import _ from "underscore"; ...@@ -31,6 +31,8 @@ import _ from "underscore";
import PaperPreviewModal from "../modal/PreviewPaperModal"; import PaperPreviewModal from "../modal/PreviewPaperModal";
import MoveModal from '../../modal/MoveModal'; import MoveModal from '../../modal/MoveModal';
import Bus from "@/core/bus"; import Bus from "@/core/bus";
import college from '@/common/lottie/college';
const { Search } = Input; const { Search } = Input;
...@@ -625,6 +627,7 @@ class PaperList extends Component { ...@@ -625,6 +627,7 @@ class PaperList extends Component {
bordered bordered
loading={loading} loading={loading}
renderEmpty={{ renderEmpty={{
image: college,
description: <span style={{ display: 'block', paddingBottom: 24 }}>还没有试卷</span> description: <span style={{ display: 'block', paddingBottom: 24 }}>还没有试卷</span>
}} }}
/> />
...@@ -641,6 +644,7 @@ class PaperList extends Component { ...@@ -641,6 +644,7 @@ class PaperList extends Component {
pagination={false} pagination={false}
bordered bordered
renderEmpty={{ renderEmpty={{
image: college,
description: <span style={{ display: 'block', paddingBottom: 24 }}>还没有试卷</span> description: <span style={{ display: 'block', paddingBottom: 24 }}>还没有试卷</span>
}} }}
/> />
......
...@@ -25,6 +25,8 @@ import AidToolService from "@/domains/aid-tool-domain/AidToolService"; ...@@ -25,6 +25,8 @@ import AidToolService from "@/domains/aid-tool-domain/AidToolService";
import _ from "underscore"; import _ from "underscore";
import Bus from "@/core/bus"; import Bus from "@/core/bus";
import moment from 'moment'; import moment from 'moment';
import { XMTable } from '@/components';
import college from '@/common/lottie/college';
const { Search } = Input; const { Search } = Input;
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
...@@ -201,19 +203,6 @@ class SelectQuestionList extends Component { ...@@ -201,19 +203,6 @@ class SelectQuestionList extends Component {
return columns; return columns;
}; };
// 自定义表格空状态
customizeRenderEmpty = () => {
return (
<Empty
image="https://image.xiaomaiketang.com/xm/emptyTable.png"
imageStyle={{
height: 100,
}}
description={"还没有题目"}
></Empty>
);
};
onShowSizeChange = (current, size) => { onShowSizeChange = (current, size) => {
if (current == size) { if (current == size) {
return; return;
...@@ -431,8 +420,11 @@ class SelectQuestionList extends Component { ...@@ -431,8 +420,11 @@ class SelectQuestionList extends Component {
)} )}
</div> </div>
<div className="select-question-content"> <div className="select-question-content">
<ConfigProvider renderEmpty={this.customizeRenderEmpty}> <XMTable
<Table renderEmpty={{
image: college,
description: '还没有题目'
}}
rowSelection={rowSelection} rowSelection={rowSelection}
rowKey={(record) => record.id} rowKey={(record) => record.id}
dataSource={dataSource} dataSource={dataSource}
...@@ -441,7 +433,6 @@ class SelectQuestionList extends Component { ...@@ -441,7 +433,6 @@ class SelectQuestionList extends Component {
onChange={this.handleChangeTable} onChange={this.handleChangeTable}
bordered bordered
/> />
</ConfigProvider>
<div className="box-footer"> <div className="box-footer">
<PageControl <PageControl
current={current - 1} current={current - 1}
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Table, ConfigProvider, Empty, Row, Input, Select, Tooltip, Space, Button, Modal, message, Menu, Dropdown, DatePicker } from 'antd'; import { Row, Input, Select, Tooltip, Space, Button, Modal, message, Menu, Dropdown, DatePicker } from 'antd';
import _ from 'underscore'; import _ from 'underscore';
import { Route, withRouter } from 'react-router-dom'; import { Route, withRouter } from 'react-router-dom';
import { DownOutlined } from '@ant-design/icons'; import { DownOutlined } from '@ant-design/icons';
...@@ -21,6 +21,8 @@ import Bus from '@/core/bus'; ...@@ -21,6 +21,8 @@ import Bus from '@/core/bus';
import moment from 'moment'; import moment from 'moment';
import Service from '@/common/js/service'; import Service from '@/common/js/service';
import MoveModal from '../../modal/MoveModal'; import MoveModal from '../../modal/MoveModal';
import college from '@/common/lottie/college';
import './QuestionList.less'; import './QuestionList.less';
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
...@@ -671,6 +673,7 @@ class QuestionList extends Component { ...@@ -671,6 +673,7 @@ class QuestionList extends Component {
onChange={this.handleChangeTable} onChange={this.handleChangeTable}
rowSelection={rowSelection} rowSelection={rowSelection}
renderEmpty={{ renderEmpty={{
image: college,
description: ( description: (
<span style={{ display: 'block', paddingBottom: 24 }}> <span style={{ display: 'block', paddingBottom: 24 }}>
<span>还没有题目</span> <span>还没有题目</span>
......
...@@ -3,108 +3,86 @@ ...@@ -3,108 +3,86 @@
* @Date: 2020-04-28 18:05:30 * @Date: 2020-04-28 18:05:30
* @LastEditors: wufan * @LastEditors: wufan
* @LastEditTime: 2020-12-26 14:37:23 * @LastEditTime: 2020-12-26 14:37:23
* @Description: * @Description:
*/ */
import mainRoutes from './config/mainRoutes'; import mainRoutes from './config/mainRoutes';
import redirectRoutes from './config/redirectRoutes'; import redirectRoutes from './config/redirectRoutes';
import React from 'react' import React from 'react';
import { Redirect,HashRouter as Router,Route ,Switch} from 'react-router-dom'; import { HashRouter as Router, Route, Switch } from 'react-router-dom';
import { createHashHistory } from 'history'; import { createHashHistory } from 'history';
import App from '../modules/root/App';
import AppContext from '@/modules/root/AppContent'; import AppContext from '@/modules/root/AppContent';
import Login from '../modules/root/Login'; import Login from '../modules/root/Login';
import CollegeManagePage from '../modules/root/CollegeManagePage'; import CollegeManagePage from '../modules/root/CollegeManagePage';
import CreateCollege from '../modules/root/CreateCollege'; import CreateCollege from '../modules/root/CreateCollege';
import _ from 'underscore'; import _ from 'underscore';
import { asyncComponent } from 'react-async-component'
import SwitchRoute from '@/modules/root/SwitchRoute'; import SwitchRoute from '@/modules/root/SwitchRoute';
import ErrorCollege from '@/modules/root/ErrorCollege'; import ErrorCollege from '@/modules/root/ErrorCollege';
const history = createHashHistory(); const history = createHashHistory();
window.RCHistory = _.extend({}, history, { window.RCHistory = _.extend({}, history, {
push: (obj: any) => { push: (obj: any) => {
history.push(obj) history.push(obj);
}, },
pushState: (obj: any) => { pushState: (obj: any) => {
history.push(obj) history.push(obj);
}, },
pushStateWithStatus: (obj: any) => { pushStateWithStatus: (obj: any) => {
history.push(obj) history.push(obj);
}, },
goBack: history.goBack, goBack: history.goBack,
location: history.location, location: history.location,
replace: (obj: any) => { replace: (obj: any) => {
history.replace(obj) history.replace(obj);
} },
}); });
const cache:any = {
path: '',
component: null
}
function dynamic (component:any) {
const resolveComponent = component
return asyncComponent({
resolve: () => {
const ts = resolveComponent()
return ts
},
})
}
export const RootRouter = () => { export const RootRouter = () => {
return ( return (
<Router {...history}> <Router {...history}>
<Switch> <Switch>
<Route key="1" exact path="/login" render={() => <Login />} /> <Route key='1' exact path='/login' render={() => <Login />} />
<Route key="2" exact path="/switch-route" render={() => <SwitchRoute />} /> <Route key='2' exact path='/switch-route' render={() => <SwitchRoute />} />
<Route key="3" exact path="/college-manage" render={() => <CollegeManagePage />} /> <Route key='3' exact path='/college-manage' render={() => <CollegeManagePage />} />
<Route key="4" exact path="/college-manage/create" render={() => <CreateCollege />} /> <Route key='4' exact path='/college-manage/create' render={() => <CreateCollege />} />
<Route key="6" exact path="/error-college" render={() => <ErrorCollege />} /> <Route key='6' exact path='/error-college' render={() => <ErrorCollege />} />
<Route key="5" path="/" render={() => <AppContext />} /> <Route key='5' path='/' render={() => <AppContext />} />
</Switch> </Switch>
</Router> </Router>
) );
} };
export const MainRoutes = () => { export const MainRoutes = () => {
return ( return (
<Switch> <Switch>
{ {_.map(mainRoutes, ({ path, component }, key) => {
_.map(mainRoutes, ({ return (
path, <Route
component, key={key}
}, key) => { path={path}
return <Route render={() => {
key={key} const Component = component;
path={path} return <Component />;
render={() => { }}
const Component = component; />
return <Component /> );
}} })}
/> </Switch>
}) );
} };
</Switch>
)
}
export const RedirectRoutes = () => { export const RedirectRoutes = () => {
return ( return (
<Switch> <Switch>
{ {_.map(redirectRoutes, ({ path, component }, key) => {
_.map(redirectRoutes, ({ return (
path, <Route
component, key={key}
}, key) => { path={path}
return <Route render={() => {
key={key} const Component = component;
path={path} return <Component />;
render={() => { }}
const Component = component; />
return <Component /> );
}} })}
/> </Switch>
}) );
} };
</Switch>
)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment