Commit 8aae5704 by wufan

Merge branch 'feat/pangguoming/20210528/external_courses' into 'master'

Feat/pangguoming/20210528/external courses

See merge request !24
parents 63ca17b4 830dd7e3
#ignore obj and lib file
dist
rev
node_modules
coverage
*/vendor.js
*/vendor.*.*
*/vendor-manifest.json
coverage
npm-shrinkwrap.json
### OSX template
.DS_Store .DS_Store
node_modules/ .AppleDouble
dist/ .LSOverride
yarn.lock
yarn-error.lock
# IntelliJ project files
.idea
*.iml
out
gen
### Sass template
.sass-cache/
*.css.map
### Vim template
# swap
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags
### SublimeText template
# cache files for sublime text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
# workspace files are user-specific
*.sublime-workspace
# project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using SublimeText
# *.sublime-project
# sftp configuration file
sftp-config.json
npm-debug.log npm-debug.log
*.zip
build/vendor.js.map
package-lock.json
.vscode/*
demo.js
debug.log
'use strict'; 'use strict'
const fs = require('fs'); const fs = require('fs')
const path = require('path'); const path = require('path')
const webpack = require('webpack'); const webpack = require('webpack')
const resolve = require('resolve'); const resolve = require('resolve')
const PnpWebpackPlugin = require('pnp-webpack-plugin'); const PnpWebpackPlugin = require('pnp-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin')
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin'); const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin')
const TerserPlugin = require('terser-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const safePostCssParser = require('postcss-safe-parser'); const safePostCssParser = require('postcss-safe-parser')
const ManifestPlugin = require('webpack-manifest-plugin'); const ManifestPlugin = require('webpack-manifest-plugin')
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin')
const WorkboxWebpackPlugin = require('workbox-webpack-plugin'); const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin'); const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin')
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin')
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent'); const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent')
const paths = require('./paths'); const paths = require('./paths')
const modules = require('./modules'); const modules = require('./modules')
const vConsolePlugin = require('vconsole-webpack-plugin'); const vConsolePlugin = require('vconsole-webpack-plugin')
const getClientEnvironment = require('./env'); const getClientEnvironment = require('./env')
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin'); const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin')
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin'); const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin')
const typescriptFormatter = require('react-dev-utils/typescriptFormatter'); const typescriptFormatter = require('react-dev-utils/typescriptFormatter')
const postcssNormalize = require('postcss-normalize'); 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( const imageInlineSizeLimit = parseInt(process.env.IMAGE_INLINE_SIZE_LIMIT || '10000')
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 = const isEnvProductionProfile = isEnvProduction && process.argv.includes('--profile')
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))
// common function to get style loaders // common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => { const getStyleLoaders = (cssOptions, preProcessor) => {
...@@ -76,13 +73,11 @@ module.exports = function (webpackEnv) { ...@@ -76,13 +73,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('.') options: paths.publicUrlOrPath.startsWith('.') ? { publicPath: '../../' } : {}
? { 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
...@@ -97,32 +92,32 @@ module.exports = function (webpackEnv) { ...@@ -97,32 +92,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'),
...@@ -130,26 +125,22 @@ module.exports = function (webpackEnv) { ...@@ -130,26 +125,22 @@ module.exports = function (webpackEnv) {
lessOptions: { lessOptions: {
modifyVars: { modifyVars: {
'primary-color': 'rgba(255, 183, 20, 1)', 'primary-color': 'rgba(255, 183, 20, 1)',
'border-radius-base': '4px', 'border-radius-base': '4px'
},
javascriptEnabled: true,
}, },
javascriptEnabled: true
}
} }
} }
); )
}
return loaders
} }
return loaders;
};
return { return {
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development', mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
// Stop compilation early in production // Stop compilation early in production
bail: isEnvProduction, bail: isEnvProduction,
devtool: isEnvProduction devtool: isEnvProduction ? (shouldUseSourceMap ? 'source-map' : false) : isEnvDevelopment && 'cheap-module-source-map',
? shouldUseSourceMap
? 'source-map'
: false
: isEnvDevelopment && 'cheap-module-source-map',
// These are the "entry points" to our application. // These are the "entry points" to our application.
// This means they will be the "root" imports that are included in JS bundle. // This means they will be the "root" imports that are included in JS bundle.
entry: [ entry: [
...@@ -163,10 +154,9 @@ module.exports = function (webpackEnv) { ...@@ -163,10 +154,9 @@ module.exports = function (webpackEnv) {
// the line below with these two lines if you prefer the stock client: // the line below with these two lines if you prefer the stock client:
// require.resolve('webpack-dev-server/client') + '?/', // require.resolve('webpack-dev-server/client') + '?/',
isEnvDevelopment && require.resolve('webpack/hot/dev-server'), isEnvDevelopment && require.resolve('webpack/hot/dev-server'),
isEnvDevelopment && isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient'),
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.
...@@ -178,27 +168,19 @@ module.exports = function (webpackEnv) { ...@@ -178,27 +168,19 @@ 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 filename: isEnvProduction ? 'static/js/[name].[contenthash:8].js' : isEnvDevelopment && 'static/js/bundle.js',
? 'static/js/[name].[contenthash:8].js'
: isEnvDevelopment && '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 chunkFilename: isEnvProduction ? 'static/js/[name].[contenthash:8].chunk.js' : isEnvDevelopment && 'static/js/[name].chunk.js',
? 'static/js/[name].[contenthash:8].chunk.js'
: isEnvDevelopment && '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.
publicPath: isEnvDevelopment ? '/' : './', publicPath: isEnvDevelopment ? '/' : './',
// Point sourcemap entries to original disk location (format as URL on Windows) // Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: isEnvProduction devtoolModuleFilenameTemplate: isEnvProduction
? info => ? (info) => path.relative(paths.appSrc, info.absoluteResourcePath).replace(/\\/g, '/')
path : isEnvDevelopment && ((info) => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/')
: isEnvDevelopment &&
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
// Prevents conflicts when multiple webpack runtimes (from different apps) // Prevents conflicts when multiple webpack runtimes (from different apps)
// are used on the same page. // are used on the same page.
jsonpFunction: `webpackJsonp${appPackageJson.name}`, jsonpFunction: `webpackJsonp${appPackageJson.name}`,
...@@ -206,9 +188,8 @@ module.exports = function (webpackEnv) { ...@@ -206,9 +188,8 @@ module.exports = function (webpackEnv) {
// module chunks which are built will work in web workers as well. // module chunks which are built will work in web workers as well.
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,
...@@ -222,7 +203,7 @@ module.exports = function (webpackEnv) { ...@@ -222,7 +203,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,
...@@ -236,10 +217,10 @@ module.exports = function (webpackEnv) { ...@@ -236,10 +217,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,
...@@ -249,10 +230,10 @@ module.exports = function (webpackEnv) { ...@@ -249,10 +230,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({
...@@ -265,57 +246,53 @@ module.exports = function (webpackEnv) { ...@@ -265,57 +246,53 @@ 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.
// We placed these paths second because we want `node_modules` to "win" // We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism. // if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebook/create-react-app/issues/253 // https://github.com/facebook/create-react-app/issues/253
modules: ['node_modules', paths.appNodeModules].concat( modules: ['node_modules', paths.appNodeModules].concat(modules.additionalModulePaths || []),
modules.additionalModulePaths || []
),
// These are the reasonable defaults supported by the Node ecosystem. // These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support // We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see: // some tools, although we do not recommend using it, see:
// https://github.com/facebook/create-react-app/issues/290 // https://github.com/facebook/create-react-app/issues/290
// `web` extension prefixes have been added for better support // `web` extension prefixes have been added for better support
// for React Native Web. // for React Native Web.
extensions: paths.moduleFileExtensions extensions: paths.moduleFileExtensions.map((ext) => `.${ext}`).filter((ext) => useTypeScript || !ext.includes('ts')),
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
alias: { alias: {
"@": path.resolve(__dirname, '../src'), '@': path.resolve(__dirname, '../src'),
// Support React Native Web // Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web', 'react-native': 'react-native-web',
// 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
...@@ -326,15 +303,15 @@ module.exports = function (webpackEnv) { ...@@ -326,15 +303,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,
...@@ -353,13 +330,12 @@ module.exports = function (webpackEnv) { ...@@ -353,13 +330,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
...@@ -374,8 +350,8 @@ module.exports = function (webpackEnv) { ...@@ -374,8 +350,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: '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.
...@@ -384,9 +360,7 @@ module.exports = function (webpackEnv) { ...@@ -384,9 +360,7 @@ module.exports = function (webpackEnv) {
include: paths.appSrc, include: paths.appSrc,
loader: require.resolve('babel-loader'), loader: require.resolve('babel-loader'),
options: { options: {
customize: require.resolve( customize: require.resolve('babel-preset-react-app/webpack-overrides'),
'babel-preset-react-app/webpack-overrides'
),
plugins: [ plugins: [
[ [
...@@ -394,12 +368,11 @@ module.exports = function (webpackEnv) { ...@@ -394,12 +368,11 @@ module.exports = function (webpackEnv) {
{ {
loaderMap: { loaderMap: {
svg: { svg: {
ReactComponent: ReactComponent: '@svgr/webpack?-svgo,+titleProp,+ref![path]'
'@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/
...@@ -407,8 +380,8 @@ module.exports = function (webpackEnv) { ...@@ -407,8 +380,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.
...@@ -420,12 +393,7 @@ module.exports = function (webpackEnv) { ...@@ -420,12 +393,7 @@ module.exports = function (webpackEnv) {
babelrc: false, babelrc: false,
configFile: false, configFile: false,
compact: false, compact: false,
presets: [ presets: [[require.resolve('babel-preset-react-app/dependencies'), { helpers: true }]],
[
require.resolve('babel-preset-react-app/dependencies'),
{ helpers: true },
],
],
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,
...@@ -434,8 +402,8 @@ module.exports = function (webpackEnv) { ...@@ -434,8 +402,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.
...@@ -449,13 +417,13 @@ module.exports = function (webpackEnv) { ...@@ -449,13 +417,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
...@@ -465,9 +433,9 @@ module.exports = function (webpackEnv) { ...@@ -465,9 +433,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
...@@ -478,11 +446,11 @@ module.exports = function (webpackEnv) { ...@@ -478,11 +446,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,
...@@ -491,7 +459,7 @@ module.exports = function (webpackEnv) { ...@@ -491,7 +459,7 @@ 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'
) )
...@@ -509,14 +477,14 @@ module.exports = function (webpackEnv) { ...@@ -509,14 +477,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: '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.
...@@ -525,7 +493,7 @@ module.exports = function (webpackEnv) { ...@@ -525,7 +493,7 @@ module.exports = function (webpackEnv) {
{}, {},
{ {
inject: true, inject: true,
template: paths.appHtml, template: paths.appHtml
}, },
isEnvProduction isEnvProduction
? { ? {
...@@ -539,8 +507,8 @@ module.exports = function (webpackEnv) { ...@@ -539,8 +507,8 @@ module.exports = function (webpackEnv) {
keepClosingSlash: true, keepClosingSlash: true,
minifyJS: true, minifyJS: true,
minifyCSS: true, minifyCSS: true,
minifyURLs: true, minifyURLs: true
}, }
} }
: undefined : undefined
) )
...@@ -548,9 +516,7 @@ module.exports = function (webpackEnv) { ...@@ -548,9 +516,7 @@ module.exports = function (webpackEnv) {
// Inlines the webpack runtime script. This script is too small to warrant // Inlines the webpack runtime script. This script is too small to warrant
// a network request. // a network request.
// https://github.com/facebook/create-react-app/issues/5358 // https://github.com/facebook/create-react-app/issues/5358
isEnvProduction && isEnvProduction && shouldInlineRuntimeChunk && new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
shouldInlineRuntimeChunk &&
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
// Makes some environment variables available in index.html. // Makes some environment variables available in index.html.
// The public URL is available as %PUBLIC_URL% in index.html, e.g.: // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico"> // <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
...@@ -576,14 +542,13 @@ module.exports = function (webpackEnv) { ...@@ -576,14 +542,13 @@ module.exports = function (webpackEnv) {
// to restart the development server for webpack to discover it. This plugin // to restart the development server for webpack to discover it. This plugin
// makes the discovery automatic so you don't have to restart. // makes the discovery automatic so you don't have to restart.
// See https://github.com/facebook/create-react-app/issues/186 // See https://github.com/facebook/create-react-app/issues/186
isEnvDevelopment && isEnvDevelopment && new WatchMissingNodeModulesPlugin(paths.appNodeModules),
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
isEnvProduction && isEnvProduction &&
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: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css', chunkFilename: '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
...@@ -596,22 +561,18 @@ module.exports = function (webpackEnv) { ...@@ -596,22 +561,18 @@ 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( const entrypointFiles = entrypoints.main.filter((fileName) => !fileName.endsWith('.map'))
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
// by default due to how webpack interprets its code. This is a practical // by default due to how webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales. // solution that requires the user to opt into importing specific locales.
...@@ -633,36 +594,26 @@ module.exports = function (webpackEnv) { ...@@ -633,36 +594,26 @@ 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,
checkSyntacticErrors: true, checkSyntacticErrors: true,
resolveModuleNameModule: process.versions.pnp resolveModuleNameModule: process.versions.pnp ? `${__dirname}/pnpTs.js` : undefined,
? `${__dirname}/pnpTs.js` resolveTypeReferenceDirectiveModule: process.versions.pnp ? `${__dirname}/pnpTs.js` : undefined,
: undefined,
resolveTypeReferenceDirectiveModule: process.versions.pnp
? `${__dirname}/pnpTs.js`
: undefined,
tsconfig: paths.appTsConfig, tsconfig: paths.appTsConfig,
reportFiles: [ reportFiles: ['**', '!**/__tests__/**', '!**/?(*.)(spec|test).*', '!**/src/setupProxy.*', '!**/src/setupTests.*'],
'**',
'!**/__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({ // new vConsolePlugin({
// enable: (process.env.DEPLOY_ENV === 'prod' || process.env.DEPLOY_ENV === 'beta') ? false : true // enable: (process.env.DEPLOY_ENV === 'prod' || process.env.DEPLOY_ENV === 'beta') ? false : true
// }) // })
...@@ -677,10 +628,10 @@ module.exports = function (webpackEnv) { ...@@ -677,10 +628,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
}; }
}; }
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
"case-sensitive-paths-webpack-plugin": "2.3.0", "case-sensitive-paths-webpack-plugin": "2.3.0",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"cropper": "^3.1.4", "cropper": "^3.1.4",
"cross-env": "^7.0.2", "cross-env": "^7.0.3",
"css-loader": "3.4.2", "css-loader": "3.4.2",
"dom-to-image": "^2.6.0", "dom-to-image": "^2.6.0",
"dotenv": "8.2.0", "dotenv": "8.2.0",
......
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-07-23 14:54:16 * @Date: 2020-07-23 14:54:16
* @LastEditors: wufan * @LastEditors: fusanqiasng
* @LastEditTime: 2021-02-02 19:32:21 * @LastEditTime: 2021-05-24 21:04:42
* @Description: 大班直播课预览弹窗 * @Description: 大班直播课预览弹窗
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
*/ */
import React from 'react'; import React from 'react'
import { Modal } from 'antd'; import { Modal } from 'antd'
import moment from 'moment'; import moment from 'moment'
import './PreviewCourseModal.less'; import './PreviewCourseModal.less'
class PreviewCourseModal extends React.Component { class PreviewCourseModal extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {}
}
} }
dealWithTime = (startTime, endTime) => { dealWithTime = (startTime, endTime) => {
const startDate = new Date(Number(startTime)); const startDate = new Date(Number(startTime))
const endDate = new Date(Number(endTime)); const endDate = new Date(Number(endTime))
const year = startDate.getFullYear(); const year = startDate.getFullYear()
const month = (startDate.getMonth() + 1) < 10 ? `0${startDate.getMonth() + 1}` : startDate.getMonth() + 1; const month = startDate.getMonth() + 1 < 10 ? `0${startDate.getMonth() + 1}` : startDate.getMonth() + 1
const day = startDate.getDate() < 10 ? `0${startDate.getDate()}` : startDate.getDate(); const day = startDate.getDate() < 10 ? `0${startDate.getDate()}` : startDate.getDate()
const startHour = startDate.getHours() < 10 ? `0${startDate.getHours()}` : startDate.getHours(); const startHour = startDate.getHours() < 10 ? `0${startDate.getHours()}` : startDate.getHours()
const startMinute = startDate.getMinutes() < 10 ? `0${startDate.getMinutes()}` : startDate.getMinutes(); const startMinute = startDate.getMinutes() < 10 ? `0${startDate.getMinutes()}` : startDate.getMinutes()
const endHour = endDate.getHours() < 10 ? `0${endDate.getHours()}` : endDate.getHours(); const endHour = endDate.getHours() < 10 ? `0${endDate.getHours()}` : endDate.getHours()
const endMinute = endDate.getMinutes() < 10 ? `0${endDate.getMinutes()}` : endDate.getMinutes(); const endMinute = endDate.getMinutes() < 10 ? `0${endDate.getMinutes()}` : endDate.getMinutes()
const liveDateStr = `${year}-${month}-${day}`; const liveDateStr = `${year}-${month}-${day}`
const startTimeStr = `${startHour}:${startMinute}`; const startTimeStr = `${startHour}:${startMinute}`
const endTimeStr = `${endHour}:${endMinute}`; const endTimeStr = `${endHour}:${endMinute}`
return { return {
liveDateStr, liveDateStr,
startTimeStr, startTimeStr,
endTimeStr, endTimeStr
}; }
} }
render() { render() {
const { courseBasinInfo, courseClassInfo = {}, courseIntroInfo, type } = this.props; const { courseBasinInfo, courseClassInfo = {}, courseIntroInfo, type } = this.props
const { coverUrl, courseName, scheduleVideoUrl } = courseBasinInfo; const { coverUrl, courseName, scheduleVideoUrl } = courseBasinInfo
const { liveDate, timeHorizonStart, timeHorizonEnd, nickname } = courseClassInfo; const { liveDate, timeHorizonStart, timeHorizonEnd, nickname } = courseClassInfo
const { liveCourseMediaRequests } = courseIntroInfo; const { liveCourseMediaRequests } = courseIntroInfo
let liveDateStr, startTimeStr, endTimeStr; let liveDateStr, startTimeStr, endTimeStr
if (liveDate) { if (liveDate) {
const _liveDate = moment(liveDate).format("YYYY-MM-DD"); const _liveDate = moment(liveDate).format('YYYY-MM-DD')
const _timeHorizonStart = moment(timeHorizonStart).format('HH:mm'); const _timeHorizonStart = moment(timeHorizonStart).format('HH:mm')
const _timeHorizonEnd = moment(timeHorizonEnd).format('HH:mm'); const _timeHorizonEnd = moment(timeHorizonEnd).format('HH:mm')
const startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x'); const startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x')
const endTime = moment(_liveDate + ' ' + _timeHorizonEnd).format('x'); const endTime = moment(_liveDate + ' ' + _timeHorizonEnd).format('x')
const { const { liveDateStr: _liveDateStr, startTimeStr: _startTimeStr, endTimeStr: _endTimeStr } = this.dealWithTime(startTime, endTime)
liveDateStr: _liveDateStr,
startTimeStr: _startTimeStr, liveDateStr = _liveDateStr
endTimeStr: _endTimeStr startTimeStr = _startTimeStr
} = this.dealWithTime(startTime, endTime); endTimeStr = _endTimeStr
liveDateStr = _liveDateStr;
startTimeStr = _startTimeStr;
endTimeStr = _endTimeStr;
} }
return ( return (
<Modal <Modal
title="预览" title='预览'
visible={true} visible={true}
width={680} width={680}
onCancel={this.props.close} onCancel={this.props.close}
footer={null} footer={null}
maskClosable={false} maskClosable={false}
className="preview-live-course-modal" className='preview-live-course-modal'
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>} closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}>
> <div className='container__wrap'>
<div className="container__wrap"> <div className='container'>
<div className="container"> <div className='container__header'>
<div className="container__header">
<Choose> <Choose>
<When condition={type === 'videoCourse'}> <When condition={type === 'videoCourse'}>
<video <video
controls controls
src={scheduleVideoUrl} src={scheduleVideoUrl}
poster={coverUrl ? coverUrl : `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`} poster={coverUrl ? coverUrl : `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`}
className="course-url" className='course-url'
/> />
</When> </When>
<Otherwise> <Otherwise>
<img src={coverUrl} className="course-cover" alt="course-cover" /> <img src={coverUrl} className='course-cover' alt='course-cover' />
</Otherwise> </Otherwise>
</Choose> </Choose>
</div> </div>
<Choose> <Choose>
<When condition={type === 'videoCourse'}> <When condition={type === 'videoCourse'}>
<div className="container__body"> <div className='container__body'>
<div className="title__name">{courseName}</div> <div className='title__name'>{courseName}</div>
<div className="title__inst-name">{window.currentUserInstInfo.name}</div> <div className='title__inst-name'>{window.currentUserInstInfo.name}</div>
</div> </div>
</When> </When>
<Otherwise> <Otherwise>
<div className="container__body"> <div className='container__body'>
<div className="container__body__title"> <div className='container__body__title'>
<div className="title__name">{courseName}</div> <div className='title__name'>{courseName}</div>
<div className="title__state">待开课</div> <div className='title__state'>待开课</div>
</div> </div>
<div className="container__body__time"> <div className='container__body__time'>
<span className="time__label">上课时间:</span> <span className='time__label'>上课时间:</span>
<span className="time__value"> <span className='time__value'>
{ {liveDate &&
liveDate && timeHorizonStart && timeHorizonEnd && timeHorizonStart &&
[ timeHorizonEnd && [
<span>{liveDateStr}&nbsp;</span>, <span>{liveDateStr}&nbsp;</span>,
<span>{startTimeStr}~{endTimeStr }</span> <span>
] {startTimeStr}~{endTimeStr}
} </span>
]}
</span> </span>
</div> </div>
<div className="container__body__teacher"> <div className='container__body__teacher'>
<span className="teacher__label">上课老师:</span> <span className='teacher__label'>上课老师:</span>
<span className="teacher__value">{nickname}</span> <span className='teacher__value'>{nickname}</span>
</div> </div>
</div> </div>
</Otherwise> </Otherwise>
</Choose> </Choose>
<div className="container__introduction"> <div className='container__introduction'>
<div className="container__introduction__title">直播课简介</div> <div className='container__introduction__title'>直播课简介</div>
<div className="container__introduction__list editor-box"> <div className='container__introduction__list editor-box'>
{ {liveCourseMediaRequests.map((item, index) => {
liveCourseMediaRequests.map((item, index) => {
if (item.mediaType === 'TEXT') { if (item.mediaType === 'TEXT') {
return ( return (
<div <div
className="intro-item text" className='intro-item text'
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: item.mediaContent __html: item.mediaContent
}} }}
...@@ -153,14 +143,13 @@ class PreviewCourseModal extends React.Component { ...@@ -153,14 +143,13 @@ class PreviewCourseModal extends React.Component {
if (item.mediaType === 'PICTURE') { if (item.mediaType === 'PICTURE') {
return ( return (
<div className="intro-item picture"> <div className='intro-item picture'>
<img src={item.mediaUrl} alt="intro-item__picture" /> <img src={item.mediaUrl} alt='intro-item__picture' />
</div> </div>
) )
} }
return item; return item
}) })}
}
</div> </div>
</div> </div>
</div> </div>
...@@ -170,4 +159,4 @@ class PreviewCourseModal extends React.Component { ...@@ -170,4 +159,4 @@ class PreviewCourseModal extends React.Component {
} }
} }
export default PreviewCourseModal; export default PreviewCourseModal
@import '../core/variables.less'; @import "../core/variables.less";
.xm-breadCrumb{ .xm-breadCrumb {
margin-left: 16px; margin-left: 16px;
color: #989898; color: #989898;
margin-top:10px; margin-top: 10px;
margin-bottom: 10px; margin-bottom: 10px;
+.statistic-tips{ + .statistic-tips {
position: relative; position: relative;
top: -8px; top: -8px;
margin-bottom: 0px !important; margin-bottom: 0px !important;
...@@ -20,26 +20,25 @@ ...@@ -20,26 +20,25 @@
font-size: @xm-font-size-m; font-size: @xm-font-size-m;
line-height: 25px; line-height: 25px;
} }
.ant-breadcrumb{ .ant-breadcrumb {
display: inline-block; display: inline-block;
span{ span {
color: #333; color: #333;
font-weight: 400; font-weight: 400;
} }
} }
.divide{ .divide {
display: inline-block; display: inline-block;
position: relative; position: relative;
height: 12px; height: 12px;
width: 25px; width: 25px;
&:after{ &:after {
content:''; content: "";
position: absolute; position: absolute;
left: 12px; left: 12px;
height: 16px; height: 16px;
top: 0px; top: 0px;
border-left:1px solid #989898; border-left: 1px solid #989898;
} }
} }
} }
...@@ -2,28 +2,26 @@ ...@@ -2,28 +2,26 @@
* @Description: * @Description:
* @Author: zangsuyun * @Author: zangsuyun
* @Date: 2021-03-13 10:57:14 * @Date: 2021-03-13 10:57:14
* @LastEditors: zangsuyun * @LastEditors: fusanqiasng
* @LastEditTime: 2021-03-13 17:16:44 * @LastEditTime: 2021-05-24 22:10:50
* @Copyright: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyright: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import PropTypes from 'prop-types'; import PropTypes from 'prop-types'
import React from 'react' import React from 'react'
// import { Modal } from 'antd'; // import { Modal } from 'antd';
import './TableSelectedData.less'; import './TableSelectedData.less'
class TableSelectedData extends React.Component { class TableSelectedData extends React.Component {
constructor(props) {
super(props);
}
render() { render() {
return ( return (
<div className={this.props.className+' selected-data-box'}> <div className={this.props.className + ' selected-data-box'}>
<span className="icon iconfont">&#xe61d;</span> <span className='icon iconfont'>&#xe61d;</span>
<span className="selected-text">{'已选择'+this.props.selectedNum+'项'}</span> <span className='selected-text'>{'已选择' + this.props.selectedNum + '项'}</span>
<span className="click-clear" onClick={this.props.clearSelectedData}>清空</span> <span className='click-clear' onClick={this.props.clearSelectedData}>
清空
</span>
</div> </div>
) )
} }
...@@ -32,14 +30,13 @@ class TableSelectedData extends React.Component { ...@@ -32,14 +30,13 @@ class TableSelectedData extends React.Component {
TableSelectedData.propTypes = { TableSelectedData.propTypes = {
className: PropTypes.string, // class className: PropTypes.string, // class
selectedNum: PropTypes.number, // 已选择人数 selectedNum: PropTypes.number, // 已选择人数
clearSelectedData: PropTypes.func, // 取消全部选择 clearSelectedData: PropTypes.func // 取消全部选择
}; }
TableSelectedData.defaultProps = { TableSelectedData.defaultProps = {
className: '', className: '',
selectedNum: 0, selectedNum: 0,
clearSelectedData: function () { clearSelectedData: function () {}
}
} }
export default TableSelectedData export default TableSelectedData
...@@ -6,11 +6,11 @@ ...@@ -6,11 +6,11 @@
*/ */
@import './variables.less'; @import './variables.less';
@active-color: #ffb000; @active-color: #2966ff;
// 消息提示框 // 消息提示框
.ant-message { .ant-message {
&>span { & > span {
display: block !important; display: block !important;
} }
} }
...@@ -21,13 +21,12 @@ ...@@ -21,13 +21,12 @@
} }
} }
.ant-steps-item .ant-steps-item-icon>.ant-steps-icon { .ant-steps-item .ant-steps-item-icon > .ant-steps-icon {
top: -6%; top: -6%;
} }
.ant-calendar-month-panel { .ant-calendar-month-panel {
.ant-calendar-month-panel-header { .ant-calendar-month-panel-header {
.ant-calendar-month-panel-next-year-btn, .ant-calendar-month-panel-next-year-btn,
.ant-calendar-month-panel-prev-year-btn { .ant-calendar-month-panel-prev-year-btn {
height: auto; height: auto;
...@@ -35,12 +34,10 @@ ...@@ -35,12 +34,10 @@
} }
} }
.ant-form-item-children { .ant-form-item-children {
display: block; display: block;
} }
.ant-switch-small:after { .ant-switch-small:after {
top: 0.3px; top: 0.3px;
} }
...@@ -88,7 +85,7 @@ ...@@ -88,7 +85,7 @@
} }
.ant-dropdown-link { .ant-dropdown-link {
color: #5D5D5E !important; color: #5d5d5e !important;
} }
.ant-table-fixed-header { .ant-table-fixed-header {
...@@ -135,7 +132,7 @@ ...@@ -135,7 +132,7 @@
} }
.ant-input[disabled] { .ant-input[disabled] {
background-color: #F0F2F5 !important; background-color: #f0f2f5 !important;
} }
.ant-dropdown-link { .ant-dropdown-link {
...@@ -171,7 +168,6 @@ background-color: #F0F2F5 !important; ...@@ -171,7 +168,6 @@ background-color: #F0F2F5 !important;
td { td {
background: @xm-table-body-active !important; background: @xm-table-body-active !important;
} }
} }
tr { tr {
...@@ -189,18 +185,17 @@ background-color: #F0F2F5 !important; ...@@ -189,18 +185,17 @@ background-color: #F0F2F5 !important;
td { td {
background: @xm-table-body-active !important; background: @xm-table-body-active !important;
} }
} }
} }
} }
} }
.ant-table-bordered { .ant-table-bordered {
.ant-table-body { .ant-table-body {
border: 1px solid #e8e8e8; border: 1px solid #e8e8e8;
} }
.ant-table-header>table,.ant-table-body>table { .ant-table-header > table,
.ant-table-body > table {
border: none !important; border: none !important;
border-radius: 3px; border-radius: 3px;
.icon { .icon {
...@@ -217,14 +212,10 @@ background-color: #F0F2F5 !important; ...@@ -217,14 +212,10 @@ background-color: #F0F2F5 !important;
border-right: 1px solid #e8e8e8 !important; border-right: 1px solid #e8e8e8 !important;
} }
.ant-input-search-button { .ant-input-search-button {
height: 32px !important; height: 32px !important;
} }
.ant-avatar { .ant-avatar {
width: 35px; width: 35px;
height: 35px; height: 35px;
...@@ -237,7 +228,7 @@ background-color: #F0F2F5 !important; ...@@ -237,7 +228,7 @@ background-color: #F0F2F5 !important;
padding: 8px 20px 12px !important; padding: 8px 20px 12px !important;
font-size: 16px !important; font-size: 16px !important;
>span { > span {
font-size: 16px !important; font-size: 16px !important;
} }
} }
...@@ -268,8 +259,8 @@ background-color: #F0F2F5 !important; ...@@ -268,8 +259,8 @@ background-color: #F0F2F5 !important;
display: inline-block; display: inline-block;
position: absolute; position: absolute;
width: 30px; width: 30px;
height: 2px; height: 4px;
background: #ffb000 !important; background: #2966ff !important;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
} }
...@@ -282,7 +273,7 @@ background-color: #F0F2F5 !important; ...@@ -282,7 +273,7 @@ background-color: #F0F2F5 !important;
} }
} }
.ant-checkbox-wrapper+.ant-checkbox-wrapper { .ant-checkbox-wrapper + .ant-checkbox-wrapper {
margin-left: 0 !important; margin-left: 0 !important;
} }
.ant-checkbox-inner { .ant-checkbox-inner {
...@@ -310,8 +301,6 @@ background-color: #F0F2F5 !important; ...@@ -310,8 +301,6 @@ background-color: #F0F2F5 !important;
left: 0px; left: 0px;
} }
.ml16 { .ml16 {
margin-left: 16px !important; margin-left: 16px !important;
} }
...@@ -322,7 +311,6 @@ background-color: #F0F2F5 !important; ...@@ -322,7 +311,6 @@ background-color: #F0F2F5 !important;
ml0 { ml0 {
margin-left: 0 !important; margin-left: 0 !important;
} }
mr0 { mr0 {
...@@ -339,28 +327,24 @@ mr0 { ...@@ -339,28 +327,24 @@ mr0 {
transform: translate(-8px, -8px) scale(0.7) !important; transform: translate(-8px, -8px) scale(0.7) !important;
} }
.ant-select-selection { .ant-select-selection {
border-color: @xm-color-border !important; border-color: @xm-color-border !important;
} }
.ant-select-dropdown-menu-item-active:not(.ant-select-dropdown-menu-item-disabled) { .ant-select-dropdown-menu-item-active:not(.ant-select-dropdown-menu-item-disabled) {
background: #F3F6FA; background: #f3f6fa;
.ant-select-selected-icon { .ant-select-selected-icon {
color:#ffb000 !important; color: #2966ff !important;
} }
} }
.ant-select-dropdown-menu-item-selected { .ant-select-dropdown-menu-item-selected {
background: none; background: none;
font-weight: 400 !important; font-weight: 400 !important;
color:#ffb000; color: #2966ff;
} }
.ant-select-open .ant-select-selection { .ant-select-open .ant-select-selection {
box-shadow: none; box-shadow: none;
} }
...@@ -391,19 +375,17 @@ mr0 { ...@@ -391,19 +375,17 @@ mr0 {
} }
} }
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 6px; width: 6px;
height: 6px; height: 6px;
display: block; display: block;
} }
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/ /*滚动条里面小方块*/
border-radius: 3px; // background:rgba(204,204,204,1); border-radius: 3px; // background:rgba(204,204,204,1);
background: rgba(204, 204, 204, 1); background: rgba(204, 204, 204, 1);
} }
.ant-avatar { .ant-avatar {
background: #e8e8e8; background: #e8e8e8;
...@@ -413,7 +395,7 @@ mr0 { ...@@ -413,7 +395,7 @@ mr0 {
border-radius: 0 !important; border-radius: 0 !important;
} }
.ant-table-small>.ant-table-content>.ant-table-body { .ant-table-small > .ant-table-content > .ant-table-body {
margin: 0px; margin: 0px;
} }
...@@ -444,14 +426,13 @@ mr0 { ...@@ -444,14 +426,13 @@ mr0 {
} }
} }
.table-no-scrollbar { .table-no-scrollbar {
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 0; width: 0;
} }
} }
.ant-table-small>.ant-table-content>.ant-table-body { .ant-table-small > .ant-table-content > .ant-table-body {
margin: 0 0px !important; margin: 0 0px !important;
} }
...@@ -459,7 +440,7 @@ mr0 { ...@@ -459,7 +440,7 @@ mr0 {
.ant-modal { .ant-modal {
.ant-input { .ant-input {
&:focus { &:focus {
border: 1px solid #ffb000 !important; border: 1px solid #2966ff !important;
} }
} }
} }
...@@ -472,8 +453,8 @@ mr0 { ...@@ -472,8 +453,8 @@ mr0 {
.ant-calendar-footer-extra { .ant-calendar-footer-extra {
.ant-tag-blue { .ant-tag-blue {
background: #fff; background: #fff;
color: #ffb000; color: #2966ff;
border-color: #ffb000; border-color: #2966ff;
} }
} }
} }
...@@ -488,10 +469,8 @@ mr0 { ...@@ -488,10 +469,8 @@ mr0 {
margin: 0 12px; margin: 0 12px;
} }
.ant-modal-confirm-info .ant-modal-confirm-body > .anticon { .ant-modal-confirm-info .ant-modal-confirm-body > .anticon {
color: #ffb000!important; color: #2966ff !important;
} }
.ant-breadcrumb > span:last-child .ant-breadcrumb-separator { .ant-breadcrumb > span:last-child .ant-breadcrumb-separator {
...@@ -511,18 +490,18 @@ mr0 { ...@@ -511,18 +490,18 @@ mr0 {
text-align: inherit !important; text-align: inherit !important;
} }
.ant-modal-confirm-info .ant-modal-confirm-body>.anticon { .ant-modal-confirm-info .ant-modal-confirm-body > .anticon {
color: #FFBB54 !important; color: #ffbb54 !important;
} }
.ant-modal-confirm-info .ant-modal-confirm-body>.default-confirm-icon { .ant-modal-confirm-info .ant-modal-confirm-body > .default-confirm-icon {
font-size: 22px !important; font-size: 22px !important;
line-height: 22px !important; line-height: 22px !important;
float: left !important; float: left !important;
color:#ffb000 !important; color: #2966ff !important;
margin-right: 16px !important; margin-right: 16px !important;
&.blue { &.blue {
color: #5CBAFF !important; color: #5cbaff !important;
} }
} }
...@@ -537,19 +516,19 @@ mr0 { ...@@ -537,19 +516,19 @@ mr0 {
// confirm弹窗自定义图标 相关样式调整 // confirm弹窗自定义图标 相关样式调整
.ant-modal-body .ant-modal-confirm-body-wrapper .ant-modal-confirm-body { .ant-modal-body .ant-modal-confirm-body-wrapper .ant-modal-confirm-body {
>.solid-icon { > .solid-icon {
color: #faad14; color: #faad14;
font-size: 22px; font-size: 22px;
line-height: 22px; line-height: 22px;
float: left; float: left;
margin-right: 16px; margin-right: 16px;
} }
>.confirm-icon{ > .confirm-icon {
color: #ffb000; color: #2966ff;
} }
.ant-modal-confirm-content { .ant-modal-confirm-content {
margin-left: 38px; margin-left: 38px;
>.solid-icon-content { > .solid-icon-content {
margin-left: 40px; margin-left: 40px;
} }
} }
...@@ -592,47 +571,44 @@ mr0 { ...@@ -592,47 +571,44 @@ mr0 {
} }
//滚动条的公共样式处理 //滚动条的公共样式处理
.ant-select-dropdown .rc-virtual-list-scrollbar .rc-virtual-list-scrollbar-thumb{ .ant-select-dropdown .rc-virtual-list-scrollbar .rc-virtual-list-scrollbar-thumb {
background: #D6D6D6 !important; background: #d6d6d6 !important;
width:6px !important; width: 6px !important;
height:80px !important; height: 80px !important;
} }
.ant-select-dropdown .rc-virtual-list-scrollbar .rc-virtual-list-scrollbar-thumb:hover{ .ant-select-dropdown .rc-virtual-list-scrollbar .rc-virtual-list-scrollbar-thumb:hover {
background: #ADADAD !important; background: #adadad !important;
} }
//table的公共的样式的处理 //table的公共的样式的处理
.ant-table.ant-table-bordered > .ant-table-container > .ant-table-content > table > thead > tr > .ant-table-cell-fix-right-first::after, .ant-table.ant-table-bordered > .ant-table-container > .ant-table-content > table > thead > tr > .ant-table-cell-fix-right-first::after,
.ant-table.ant-table-bordered > .ant-table-container > .ant-table-content > table > tbody > tr > .ant-table-cell-fix-right-first::after .ant-table.ant-table-bordered > .ant-table-container > .ant-table-content > table > tbody > tr > .ant-table-cell-fix-right-first::after {
{ border-right: none !important;
border-right:none !important;
} }
// .ant-table tbody tr:nth-child(2n) td{ // .ant-table tbody tr:nth-child(2n) td{
// background: #FAFAFA !important; // background: #FAFAFA !important;
// } // }
.ant-table-thead > tr > th{ .ant-table-thead > tr > th {
font-weight:normal !important; font-weight: bold !important;
color: #333 !important;
} }
td.ant-table-column-sort{ td.ant-table-column-sort {
background: none; background: none;
} }
.ant-modal-content .ant-table-thead > tr > th{ .ant-modal-content .ant-table-thead > tr > th {
padding:9px 24px; padding: 9px 24px;
} }
.ant-modal-content tr > td{ .ant-modal-content tr > td {
padding:14px 24px !important; padding: 14px 24px !important;
} }
//弹框里的table样式的处理 //弹框里的table样式的处理
//按钮的样式的公共处理 //按钮的样式的公共处理
.ant-btn-loading { .ant-btn-loading {
padding-left:18px !important; padding-left: 18px !important;
} }
.ant-input, .ant-input,
...@@ -640,27 +616,25 @@ td.ant-table-column-sort{ ...@@ -640,27 +616,25 @@ td.ant-table-column-sort{
border-color: @xm-color-border; border-color: @xm-color-border;
} }
//普通按钮样式 //普通按钮样式
.ant-btn { .ant-btn {
height:28px !important; height: 28px !important;
font-weight: normal !important; font-weight: normal !important;
border: 1px solid #e8e8e8; border: 1px solid #e8e8e8;
box-shadow: none !important; box-shadow: none !important;
padding:0px 12px !important; padding: 0px 12px !important;
color:#666 !important; color: #666 !important;
.span{ .span {
line-height:1; line-height: 1;
} }
&:hover { &:hover {
border:1px solid rgba(232, 232, 232, 1) !important; border: 1px solid rgba(232, 232, 232, 1) !important;
opacity: 0.7; opacity: 0.7;
} }
&:focus, &:focus,
&:active{ &:active {
color:#666 !important; color: #666 !important;
border:1px solid #C6C6C6 !important; border: 1px solid #c6c6c6 !important;
} }
} }
...@@ -677,75 +651,74 @@ td.ant-table-column-sort{ ...@@ -677,75 +651,74 @@ td.ant-table-column-sort{
} }
background-color: @xm-color-text-select-primary !important; background-color: @xm-color-text-select-primary !important;
border-color: @xm-color-text-select-primary !important; border-color: @xm-color-text-select-primary !important;
border:none !important; border: none !important;
&:hover { &:hover {
background-color: #FFB714 !important; background-color: #2966ff !important;
opacity: 0.8 !important; opacity: 0.8 !important;
} }
&:focus, &:focus,
&:active{ &:active {
background-color:#FFA100 !important; background-color: #5c8aff !important;
} }
} }
//警示按钮样式 //警示按钮样式
.ant-btn-dangerous{ .ant-btn-dangerous {
background:rgba(255, 79, 79, 1) !important; background: rgba(255, 79, 79, 1) !important;
color:#FFF !important; color: #fff !important;
border:none !important; border: none !important;
} }
.ant-btn-dangerous:hover{ .ant-btn-dangerous:hover {
background:rgba(255, 79, 79, 0.7) !important; background: rgba(255, 79, 79, 0.7) !important;
color:#FFF !important; color: #fff !important;
border:none !important; border: none !important;
} }
.ant-btn-dangerous:focus, .ant-btn-dangerous:focus,
.ant-btn-dangerous:active{ .ant-btn-dangerous:active {
background:rgba(255, 79, 79,1) !important; background: rgba(255, 79, 79, 1) !important;
color:#FFF !important; color: #fff !important;
border:none !important; border: none !important;
} }
//disabled的按钮的样式 //disabled的按钮的样式
.ant-btn-primary[disabled], .ant-btn-primary[disabled]:hover, .ant-btn-primary[disabled]:focus, .ant-btn-primary[disabled]:active{ .ant-btn-primary[disabled],
background: #F7F8F9 !important; .ant-btn-primary[disabled]:hover,
border: 1px solid #E8E8E8 !important; .ant-btn-primary[disabled]:focus,
color: #CCCCCC !important; .ant-btn-primary[disabled]:active {
span{ background: #f7f8f9 !important;
color: #CCCCCC !important; border: 1px solid #e8e8e8 !important;
color: #cccccc !important;
span {
color: #cccccc !important;
} }
} }
//时间选择器后的按钮样式 //时间选择器后的按钮样式
.ant-picker-ok{ .ant-picker-ok {
.ant-btn{ .ant-btn {
padding:0 8px !important; padding: 0 8px !important;
} }
} }
//搜索框后面的按钮样式 //搜索框后面的按钮样式
.ant-input-search-button{ .ant-input-search-button {
height:32px !important; height: 32px !important;
} }
.ant-input-search > .ant-input-group > .ant-input-group-addon:last-child .ant-input-search-button{ .ant-input-search > .ant-input-group > .ant-input-group-addon:last-child .ant-input-search-button {
background: #FFF !important; background: #fff !important;
border-top:1px solid #dfdfdf !important; border-top: 1px solid #dfdfdf !important;
border-right:1px solid #dfdfdf !important; border-right: 1px solid #dfdfdf !important;
border-bottom:1px solid #dfdfdf !important; border-bottom: 1px solid #dfdfdf !important;
} }
.ant-input-search .ant-input:hover, .ant-input-search .ant-input:focus{ .ant-input-search .ant-input:hover,
.ant-input-search .ant-input:focus {
border-color: #ffcb3d; border-color: #ffcb3d;
border: 1px solid #d9d9d9 !important; border: 1px solid #d9d9d9 !important;
box-shadow: none !important; box-shadow: none !important;
border-right: 0px !important; border-right: 0px !important;
} }
// .ant-input-search:focus{ .ant-input-search > .ant-input-group > .ant-input-group-addon:last-child .ant-input-search-button span {
// border-top:1px solid #FFA100 !important; color: #bfbfbf !important;
// border-right:1px solid #FFA100 !important; font-size: 12px;
// border-bottom:1px solid #FFA100 !important;
// }
.ant-input-search > .ant-input-group > .ant-input-group-addon:last-child .ant-input-search-button span{
color:#bfbfbf !important;
font-size:12px;
} }
.ant-modal-footer { .ant-modal-footer {
...@@ -756,44 +729,43 @@ td.ant-table-column-sort{ ...@@ -756,44 +729,43 @@ td.ant-table-column-sort{
//模态框的样式的公共处理 //模态框的样式的公共处理
//删除模态框的样式的的处理 //删除模态框的样式的的处理
.ant-modal-confirm{ .ant-modal-confirm {
.ant-modal-body{ .ant-modal-body {
padding:32px 32px 24px 32px !important; padding: 32px 32px 24px 32px !important;
.ant-modal-confirm-title{ .ant-modal-confirm-title {
color: rgba(0, 0, 0, 0.85) color: rgba(0, 0, 0, 0.85);
} }
.ant-modal-confirm-content{ .ant-modal-confirm-content {
min-height:44px; min-height: 44px;
color: rgba(0, 0, 0, 0.65) color: rgba(0, 0, 0, 0.65);
} }
} }
} }
//普通模态框的样式的处理 //普通模态框的样式的处理
.ant-modal{ .ant-modal {
.ant-modal-title{ .ant-modal-title {
color:#333; color: #333;
font-weight:400; font-weight: 400;
} }
.ant-modal-close-x{ .ant-modal-close-x {
.modal-close-icon{ .modal-close-icon {
color:#999; color: #999;
font-size:12px; font-size: 12px;
margin-right:6px; margin-right: 6px;
font-weight:400; font-weight: 400;
} }
} }
.ant-modal-footer{ .ant-modal-footer {
padding:10px 24px; padding: 10px 24px;
} }
} }
// 排序小三角 // 排序小三角
.ant-table-column-sorter { .ant-table-column-sorter {
margin-left: 2px!important; margin-left: 2px !important;
} }
.ant-table-column-sorter-full{ .ant-table-column-sorter-full {
margin-top:-0.32rem !important; margin-top: -0.32rem !important;
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
right: 0; right: 0;
bottom: 0; bottom: 0;
z-index: 101; z-index: 101;
background-color: #F0F2F5; background-color: #f0f2f5;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
.page { .page {
...@@ -19,14 +19,14 @@ ...@@ -19,14 +19,14 @@
bottom: 0; bottom: 0;
z-index: 102; z-index: 102;
overflow: auto; overflow: auto;
.box{ .box {
&:first-child{ &:first-child {
margin-bottom: 8px; margin-bottom: 8px;
} }
&+.box{ & + .box {
margin: 8px 16px; margin: 8px 16px;
} }
&:last-child{ &:last-child {
margin-bottom: 16px; margin-bottom: 16px;
} }
} }
...@@ -42,38 +42,40 @@ ...@@ -42,38 +42,40 @@
animation: all 0.75; animation: all 0.75;
padding-bottom: 16px; padding-bottom: 16px;
} }
.content-sub-header{ .content-sub-header {
padding: 0px 16px 0; padding: 0px 16px 0;
line-height: 30px; line-height: 30px;
} }
.content-header { .content-header {
padding: 10px 16px; padding: 16px 16px;
line-height: 30px; line-height: 30px;
background: #fff;
margin: 16px 16px 0;
font-size: 24px;
color: #333;
font-weight: 500;
h1 { h1 {
font-size: 14px;
color: #898989;
font-weight: normal; font-weight: normal;
display: inline-block; display: inline-block;
} }
} }
.box{ .box {
padding: 16px; padding: 16px;
margin: 0 16px 16px; margin: 0 16px;
margin-bottom: 8px;
background: #ffffff; background: #ffffff;
// min-height: 400px; // min-height: 400px;
.box-header { .box-header {
line-height: 30px; line-height: 30px;
padding-bottom: 12px; padding-bottom: 12px;
&.searchOnly{ &.searchOnly {
padding-bottom: 0px; padding-bottom: 0px;
} }
.filter-row { .filter-row {
min-height: 30px; min-height: 30px;
margin-bottom: 10px; margin-bottom: 10px;
>* { > * {
float: left; float: left;
margin-left: 16px; margin-left: 16px;
height: 40px; height: 40px;
...@@ -89,16 +91,12 @@ ...@@ -89,16 +91,12 @@
.box-footer { .box-footer {
margin-top: 16px; margin-top: 16px;
} }
&:first-child{ &:first-child {
margin-bottom: 8px; margin-bottom: 8px;
} }
&+.box{ & + .box {
margin: 8px 16px; margin: 8px 16px;
} }
&:last-child{
margin-bottom: 13px;
}
} }
// .ant-calendar-picker{ // .ant-calendar-picker{
// top:-1px; // top:-1px;
...@@ -108,26 +106,25 @@ ...@@ -108,26 +106,25 @@
.box-header { .box-header {
.ant-row-flex { .ant-row-flex {
padding-top: 2px; padding-top: 2px;
>div:nth-child(1) { > div:nth-child(1) {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
align-items:space-between; align-items: space-between;
.flex(1); .flex(1);
>*{ > * {
flex-basis: 30%; flex-basis: 30%;
margin-right: 3%; margin-right: 3%;
margin-bottom: 16px; margin-bottom: 16px;
} }
} }
&.lastRow{ &.lastRow {
>div:nth-child(1) { > div:nth-child(1) {
>*{ > * {
margin-bottom: 0px; margin-bottom: 0px;
} }
} }
} }
} }
} }
.full-screen-page { .full-screen-page {
...@@ -139,5 +136,5 @@ ...@@ -139,5 +136,5 @@
bottom: 0; bottom: 0;
right: 0; right: 0;
user-select: none; user-select: none;
background-color: #F0F2F5; background-color: #f0f2f5;
} }
/* /*
* @Author: yuananting * @Author: yuananting
* @Date: 2021-03-03 15:13:12 * @Date: 2021-03-03 15:13:12
* @LastEditors: yuananting * @LastEditors: fusanqiasng
* @LastEditTime: 2021-04-13 13:58:40 * @LastEditTime: 2021-05-25 10:07:03
* @Description: 助学工具接口 * @Description: 助学工具接口
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import Service from "@/common/js/service"; import Service from '@/common/js/service'
export function queryExternalCategoryTree(params: object) {
return Service.Hades('public/externalHades/queryCategoryTree', params)
}
export function queryCategoryTree(params: object) { export function queryCategoryTree(params: object) {
return Service.Hades("public/hades/queryCategoryTree", params); return Service.Hades('public/hades/queryCategoryTree', params)
} }
export function addCategory(params: object) { export function addCategory(params: object) {
return Service.Hades("public/hades/addCategory", params); return Service.Hades('public/hades/addCategory', params)
} }
export function delCategory(params: object) { export function delCategory(params: object) {
return Service.Hades("public/hades/delCategory", params); return Service.Hades('public/hades/delCategory', params)
} }
export function editCategory(params: object) { export function editCategory(params: object) {
return Service.Hades("public/hades/editCategory", params); return Service.Hades('public/hades/editCategory', params)
} }
export function editCategoryTree(params: object) { export function editCategoryTree(params: object) {
return Service.Hades("public/hades/editCategoryTree", params); return Service.Hades('public/hades/editCategoryTree', params)
} }
export function queryQuestionPageList(params: object) { export function queryQuestionPageList(params: object) {
return Service.Hades("public/hades/queryQuestionPageList", params); return Service.Hades('public/hades/queryQuestionPageList', params)
} }
export function addQuestion(params: object) { export function addQuestion(params: object) {
return Service.Hades("public/hades/addQuestion", params); return Service.Hades('public/hades/addQuestion', params)
} }
export function deleteQuestion(params: object) { export function deleteQuestion(params: object) {
return Service.Hades("public/hades/deleteQuestion", params); return Service.Hades('public/hades/deleteQuestion', params)
} }
export function queryQuestionDetails(params: object) { export function queryQuestionDetails(params: object) {
return Service.Hades("public/hades/queryQuestionDetails", params); return Service.Hades('public/hades/queryQuestionDetails', params)
} }
export function editQuestion(params: object) { export function editQuestion(params: object) {
return Service.Hades("public/hades/editQuestion", params); return Service.Hades('public/hades/editQuestion', params)
} }
export function batchImport(params: object) { export function batchImport(params: object) {
return Service.Hades("public/hades/batchImport", params); return Service.Hades('public/hades/batchImport', params)
} }
export function createPaper(params: object) { export function createPaper(params: object) {
return Service.Hades("public/hades/createPaper", params); return Service.Hades('public/hades/createPaper', params)
} }
export function queryPaperPageList(params: object) { export function queryPaperPageList(params: object) {
return Service.Hades("public/hades/queryPaperPageList", params); return Service.Hades('public/hades/queryPaperPageList', params)
} }
export function deletePaper(params: object) { export function deletePaper(params: object) {
return Service.Hades("public/hades/deletePaper", params); return Service.Hades('public/hades/deletePaper', params)
} }
export function queryPaperDetail(params: object) { export function queryPaperDetail(params: object) {
return Service.Hades("public/hades/queryPaperDetail", params); return Service.Hades('public/hades/queryPaperDetail', params)
} }
export function viewPaper(params: object) { export function viewPaper(params: object) {
return Service.Hades("public/hades/viewPaper", params); return Service.Hades('public/hades/viewPaper', params)
} }
export function editPaper(params: object) { export function editPaper(params: object) {
return Service.Hades("public/hades/editPaper", params); return Service.Hades('public/hades/editPaper', params)
} }
export function batchQueryQuestionDetails(params: object) { export function batchQueryQuestionDetails(params: object) {
return Service.Hades("public/hades/batchQueryQuestionDetails", params); return Service.Hades('public/hades/batchQueryQuestionDetails', params)
} }
export function queryQuestionPageListWithContent(params: object) { export function queryQuestionPageListWithContent(params: object) {
return Service.Hades("public/hades/queryQuestionPageListWithContent", params); return Service.Hades('public/hades/queryQuestionPageListWithContent', params)
} }
/* /*
* @Author: yuananting * @Author: yuananting
* @Date: 2021-03-11 11:34:37 * @Date: 2021-03-11 11:34:37
* @LastEditors: yuananting * @LastEditors: fusanqiasng
* @LastEditTime: 2021-04-13 13:58:11 * @LastEditTime: 2021-05-24 23:44:39
* @Description: 助学工具接口 * @Description: 助学工具接口
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import { import {
queryExternalCategoryTree,
queryCategoryTree, queryCategoryTree,
addCategory, addCategory,
delCategory, delCategory,
...@@ -25,101 +26,110 @@ import { ...@@ -25,101 +26,110 @@ import {
viewPaper, viewPaper,
editPaper, editPaper,
batchQueryQuestionDetails, batchQueryQuestionDetails,
queryQuestionPageListWithContent, queryQuestionPageListWithContent
} from '@/data-source/aidTool/request-apis'; } from '@/data-source/aidTool/request-apis'
export default class AidToolService { export default class AidToolService {
/**
* 查询运营端分类书
* @param parmas
* @returns
*/
static queryExternalCategoryTree(parmas: any) {
return queryExternalCategoryTree(parmas)
}
// 获取题目分类树 // 获取题目分类树
static queryCategoryTree(params: any) { static queryCategoryTree(params: any) {
return queryCategoryTree(params); return queryCategoryTree(params)
} }
// 新增题目分类 // 新增题目分类
static addCategory(params: any) { static addCategory(params: any) {
return addCategory(params); return addCategory(params)
} }
// 删除分类 // 删除分类
static delCategory(params: any) { static delCategory(params: any) {
return delCategory(params); return delCategory(params)
} }
// 编辑分类 // 编辑分类
static editCategory(params: any) { static editCategory(params: any) {
return editCategory(params); return editCategory(params)
} }
// 编辑分类树(拖拽) // 编辑分类树(拖拽)
static editCategoryTree(params: any) { static editCategoryTree(params: any) {
return editCategoryTree(params); return editCategoryTree(params)
} }
// 查询题目列表 // 查询题目列表
static queryQuestionPageList(params: any) { static queryQuestionPageList(params: any) {
return queryQuestionPageList(params); return queryQuestionPageList(params)
} }
// 添加题目 // 添加题目
static addQuestion(params: any) { static addQuestion(params: any) {
return addQuestion(params); return addQuestion(params)
} }
// 删除题目 // 删除题目
static deleteQuestion(params: any) { static deleteQuestion(params: any) {
return deleteQuestion(params); return deleteQuestion(params)
} }
// 预览题目 // 预览题目
static queryQuestionDetails(params: any) { static queryQuestionDetails(params: any) {
return queryQuestionDetails(params); return queryQuestionDetails(params)
} }
// 编辑题目 // 编辑题目
static editQuestion(params: any) { static editQuestion(params: any) {
return editQuestion(params); return editQuestion(params)
} }
// 批量导入 // 批量导入
static batchImport(params: any) { static batchImport(params: any) {
return batchImport(params); return batchImport(params)
} }
// 创建试卷 // 创建试卷
static createPaper(params: any) { static createPaper(params: any) {
return createPaper(params); return createPaper(params)
} }
// 查询试卷列表 // 查询试卷列表
static queryPaperPageList(params: any) { static queryPaperPageList(params: any) {
return queryPaperPageList(params); return queryPaperPageList(params)
} }
// 删除试卷 // 删除试卷
static deletePaper(params: any) { static deletePaper(params: any) {
return deletePaper(params); return deletePaper(params)
} }
// 编辑前查询试卷信息 // 编辑前查询试卷信息
static queryPaperDetail(params: any) { static queryPaperDetail(params: any) {
return queryPaperDetail(params); return queryPaperDetail(params)
} }
// 预览试卷 // 预览试卷
static viewPaper(params: any) { static viewPaper(params: any) {
return viewPaper(params); return viewPaper(params)
} }
// 编辑试卷 // 编辑试卷
static editPaper(params: any) { static editPaper(params: any) {
return editPaper(params); return editPaper(params)
} }
// 操作试卷-预览查询多题目信息 // 操作试卷-预览查询多题目信息
static batchQueryQuestionDetails(params: any) { static batchQueryQuestionDetails(params: any) {
return batchQueryQuestionDetails(params); return batchQueryQuestionDetails(params)
} }
// 操作试卷-选择题目列表带题目详情 // 操作试卷-选择题目列表带题目详情
static queryQuestionPageListWithContent(params: any) { static queryQuestionPageListWithContent(params: any) {
return queryQuestionPageListWithContent(params); return queryQuestionPageListWithContent(params)
} }
} }
/* /*
* @Author: 陈剑宇 * @Author: 陈剑宇
* @Date: 2020-05-07 14:43:01 * @Date: 2020-05-07 14:43:01
* @LastEditTime: 2021-04-19 16:42:24 * @LastEditTime: 2021-05-28 15:20:40
* @LastEditors: zhangleyuan * @LastEditors: fusanqiasng
* @Description: * @Description:
* @FilePath: /wheat-web-demo/src/domains/basic-domain/constants.ts * @FilePath: /wheat-web-demo/src/domains/basic-domain/constants.ts
*/ */
import { MapInterface } from '@/domains/basic-domain/interface' import { MapInterface } from '@/domains/basic-domain/interface'
// 默认是 dev 环境 // 默认是 dev 环境
const ENV: string = process.env.DEPLOY_ENV || 'dev'; const ENV: string = process.env.DEPLOY_ENV || 'dev'
console.log("process.env.DEPLOY_ENV",process) console.log('process.env.DEPLOY_ENV', process)
const BASIC_HOST_MAP: MapInterface = { const BASIC_HOST_MAP: MapInterface = {
dev: 'https://dev-heimdall.xiaomai5.com/', dev: 'https://dev-heimdall.xiaomai5.com/',
dev1: 'https://dev1-heimdall.xiaomai5.com/', dev1: 'https://dev1-heimdall.xiaomai5.com/',
rc: 'https://rc-heimdall.xiaomai5.com/', rc: 'https://rc-heimdall.xiaomai5.com/',
gray: 'https://gray-heimdall.xiaomai5.com/', gray: 'https://gray-heimdall.xiaomai5.com/',
prod: 'https://gateway.xiaomai5.com/' prod: 'https://gateway.xiaomai5.com/',
}; }
const PATH_MAP: MapInterface = { const PATH_MAP: MapInterface = {
dev: 'https://dev.xiaomai5.com/xiaomai-cloud-class-web/h5.html', dev: 'https://dev.xiaomai5.com/xiaomai-cloud-class-web/h5.html',
dev1: 'https://dev.xiaomai5.com/xiaomai-cloud-class-web/h5.html', dev1: 'https://dev.xiaomai5.com/xiaomai-cloud-class-web/h5.html',
rc: 'https://rc.xiaomai5.com/xiaomai-cloud-class-web/h5.html', rc: 'https://rc.xiaomai5.com/xiaomai-cloud-class-web/h5.html',
gray: 'https://res.xiaomai0.com/xiaomai-cloud-class-web/gray/h5.html', gray: 'https://res.xiaomai0.com/xiaomai-cloud-class-web/gray/h5.html',
prod: 'https://res.xiaomai0.com/xiaomai-cloud-class-web/h5.html' prod: 'https://res.xiaomai0.com/xiaomai-cloud-class-web/h5.html',
}; }
// axios headers config // axios headers config
export const TIME_OUT: number = 20000; export const TIME_OUT: number = 20000
export const USER_TYPE: string = 'B'; export const USER_TYPE: string = 'B'
export const PROJECT = 'xmzj-web-b'; export const PROJECT = 'xmzj-web-b'
export const VERSION = '5.4.8'; export const VERSION = '5.4.8'
export const PREFIX = 'cloud-class'; export const PREFIX = 'cloud-class'
export const USER_PREFIX = 'store-live'; export const USER_PREFIX = 'store-live'
// host // host
export const BASIC_HOST: string = BASIC_HOST_MAP[ENV]; export const BASIC_HOST: string = BASIC_HOST_MAP[ENV]
export const PATH: string = PATH_MAP[ENV]; export const PATH: string = PATH_MAP[ENV]
...@@ -6,127 +6,141 @@ ...@@ -6,127 +6,141 @@
* @Description: 大班直播、互动班课列表的筛选组件 * @Description: 大班直播、互动班课列表的筛选组件
*/ */
import React from 'react'; import React from 'react'
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom'
import { Row,Col, Input, Select ,Tooltip} from 'antd'; import { Row, Col, Input, Select, Tooltip } from 'antd'
import RangePicker from "@/modules/common/DateRangePicker"; import RangePicker from '@/modules/common/DateRangePicker'
import moment from 'moment'; import moment from 'moment'
import StoreService from "@/domains/store-domain/storeService"; import StoreService from '@/domains/store-domain/storeService'
import User from '@/common/js/user'; import User from '@/common/js/user'
import './LiveCourseFilter.less'; import './LiveCourseFilter.less'
const { Search } = Input; const { Search } = Input
const { Option } = Select; const { Option } = Select
const defaultQuery = { const defaultQuery = {
courseName: null, courseName: null,
startTime: null, startTime: null,
teacherId: null, teacherId: null,
courseState: null, courseState: null,
shelfState:null, shelfState: null,
} }
const defaultTeacherQuery = { const defaultTeacherQuery = {
size: 10, size: 10,
current: 1, current: 1,
nickName:null nickName: null,
} }
class LiveCourseFilter extends React.Component { class LiveCourseFilter extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
query: {...defaultQuery}, query: { ...defaultQuery },
teacherQuery: defaultTeacherQuery, teacherQuery: defaultTeacherQuery,
teacherList:[], teacherList: [],
expandFilter:false expandFilter: false,
} }
} }
componentDidMount() { componentDidMount() {
this.getTeacherList(); this.getTeacherList()
} }
getTeacherList(current = 1, selectList){ getTeacherList(current = 1, selectList) {
const { teacherQuery,teacherList} = this.state; const { teacherQuery, teacherList } = this.state
const _query = { const _query = {
...teacherQuery, ...teacherQuery,
current, current,
size:10 size: 10,
}; }
StoreService.getStoreUserBasicPage( _query).then((res) => { StoreService.getStoreUserBasicPage(_query).then((res) => {
const { result = {} } = res; const { result = {} } = res
const { records = [], total = 0, hasNext } = result; const { records = [], total = 0, 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,
}) })
}); })
} }
// 滑动加载更多讲师列表 // 滑动加载更多讲师列表
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)
}
)
} }
} }
// 改变搜索条件 // 改变搜索条件
handleChangeQuery = (field, value) => { handleChangeQuery = (field, value) => {
this.setState({ this.setState(
{
query: { query: {
...this.state.query, ...this.state.query,
[field]: value, [field]: value,
current: 1, current: 1,
} },
}, () => { },
if (field === 'courseName') return; () => {
if (field === 'courseName') return
this.props.onChange(this.state.query) this.props.onChange(this.state.query)
}); }
)
} }
handleChangeDates = (dates) => { handleChangeDates = (dates) => {
const { query } = this.state; const { query } = this.state
if (_.isEmpty(dates)) { if (_.isEmpty(dates)) {
delete query.startTime; delete query.startTime
delete query.endTime; delete query.endTime
} else { } else {
query.startTime = dates[0].valueOf(); query.startTime = dates[0]?.startOf('day').valueOf()
query.endTime = dates[1].valueOf(); query.endTime = dates[0]?.endOf('day').valueOf()
} }
this.setState({ this.setState(
query:{ {
query: {
...query, ...query,
current: 1, current: 1,
},
},
() => {
this.props.onChange(this.state.query)
} }
}, () => { )
this.props.onChange(this.state.query);
})
} }
// 选择老师 // 选择老师
handleSelectTeacher = (teacher) => { handleSelectTeacher = (teacher) => {
const { name: teacherName, teacherId } = teacher; const { name: teacherName, teacherId } = teacher
this.setState({ this.setState(
{
query: { query: {
...this.state.query, ...this.state.query,
teacherId, teacherId,
teacherName, teacherName,
current: 1, current: 1,
},
},
() => {
this.props.onChange(this.state.query)
} }
}, () => { )
this.props.onChange(this.state.query);
})
} }
// 清空搜索条件 // 清空搜索条件
handleReset = () => { handleReset = () => {
this.setState({ this.setState(
{
query: { query: {
courseName: null, courseName: null,
startTime: null, startTime: null,
...@@ -135,136 +149,177 @@ class LiveCourseFilter extends React.Component { ...@@ -135,136 +149,177 @@ class LiveCourseFilter extends React.Component {
teacherName: null, teacherName: null,
courseState: undefined, courseState: undefined,
current: 1, current: 1,
shelfState:null, shelfState: null,
}, },
}, () => { },
this.props.onChange(this.state.query); () => {
}) this.props.onChange(this.state.query)
}
)
} }
render() { render() {
const { const { courseName, startTime, endTime, courseState, teacherName, teacherId, shelfState } = this.state.query
courseName, startTime, endTime, const { expandFilter, teacherList, teacherQuery } = this.state
courseState, teacherName, teacherId, const { teacherId: _teahcerId } = {}
shelfState const isTeacher = !!_teahcerId // 判断是否是老师身份
} = this.state.query;
const {expandFilter,teacherList,teacherQuery} = this.state;
const { teacherId: _teahcerId } = {};
const isTeacher = !!_teahcerId; // 判断是否是老师身份
return ( return (
<div className="live-course-filter"> <div className='live-course-filter'>
<Row type="flex" justify="space-between" align="top"> <Row type='flex' justify='space-between' align='top'>
<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={courseName} value={courseName}
placeholder="搜索直播名称" placeholder='搜索直播名称'
onChange={(e) => { this.handleChangeQuery('courseName', e.target.value)}} onChange={(e) => {
onSearch={ () => { this.props.onChange(this.state.query) } } this.handleChangeQuery('courseName', e.target.value)
style={{ width: "calc(100% - 70px)" }} }}
className="search-input" onSearch={() => {
enterButton={<span className="icon iconfont">&#xe832;</span>} this.props.onChange(this.state.query)
}}
style={{ width: 'calc(100% - 70px)' }}
className='search-input'
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/> />
</div> </div>
<div className="search-condition__item"> <div className='search-condition__item'>
<span className="search-date">上课日期:</span> <span className='search-date'>上课日期:</span>
<RangePicker <RangePicker
id="course_date_picker" id='course_date_picker'
allowClear={false} allowClear={false}
value={ startTime ? [moment(startTime), moment(endTime)] : null } value={startTime ? [moment(startTime), moment(endTime)] : null}
format={"YYYY-MM-DD"} format={'YYYY-MM-DD'}
onChange={(dates) => { this.handleChangeDates(dates) }} onChange={(dates) => {
style={{ width: "calc(100% - 70px)" }} this.handleChangeDates(dates)
}}
style={{ width: 'calc(100% - 70px)' }}
/> />
</div> </div>
{ User.getUserRole()!=="CloudLecturer" && {User.getUserRole() !== 'CloudLecturer' && (
<div className="search-condition__item"> <div className='search-condition__item'>
<span>讲师:</span> <span>讲师:</span>
<Select <Select
placeholder="请选择讲师" placeholder='请选择讲师'
style={{ width: "calc(100% - 70px)"}} style={{ width: 'calc(100% - 70px)' }}
showSearch showSearch
allowClear allowClear
filterOption={(input, option) => option} filterOption={(input, option) => option}
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>
}
value={teacherId} value={teacherId}
onChange={(value) => { onChange={(value) => {
this.handleChangeQuery('teacherId', value); this.handleChangeQuery('teacherId', value)
}} }}
onSearch={(value) => { onSearch={(value) => {
teacherQuery.nickName = value teacherQuery.nickName = value
this.setState({ this.setState(
teacherQuery {
}, () => { teacherQuery,
},
() => {
this.getTeacherList() this.getTeacherList()
}) }
)
}} }}
onClear ={(value)=>{ onClear={(value) => {
this.setState({ this.setState(
teacherQuery:{ {
teacherQuery: {
size: 10, size: 10,
current: 1, current: 1,
nickName:null nickName: null,
} },
}, () => { },
() => {
this.getTeacherList() this.getTeacherList()
})
}
} }
> )
}}>
{_.map(teacherList, (item, index) => { {_.map(teacherList, (item, index) => {
return ( return (
<Select.Option value={item.userId} key={item.userId}>{item.nickName}</Select.Option> <Select.Option value={item.userId} key={item.userId}>
); {item.nickName}
</Select.Option>
)
})} })}
</Select> </Select>
</div> </div>
} )}
{ ((expandFilter && User.getUserRole()!=="CloudLecturer") || User.getUserRole()==="CloudLecturer") && {((expandFilter && User.getUserRole() !== 'CloudLecturer') || User.getUserRole() === 'CloudLecturer') && (
<div className="search-condition__item"> <div className='search-condition__item'>
<span className="select-status">上课状态:</span> <span className='select-status'>上课状态:</span>
<Select <Select
style={{ width: "calc(100% - 70px)" }} style={{ width: 'calc(100% - 70px)' }}
placeholder="请选择" placeholder='请选择'
allowClear={true} allowClear={true}
value={courseState} value={courseState}
onChange={(value) => { this.handleChangeQuery('courseState', value) }} onChange={(value) => {
suffixIcon={<span className="icon iconfont" style={{fontSize:'12px',color:'#BFBFBF'}}>&#xe835;</span>} this.handleChangeQuery('courseState', value)
> }}
<Option value="UN_START">待开课</Option> suffixIcon={
<Option value="STARTING">上课中</Option> <span className='icon iconfont' style={{ fontSize: '12px', color: '#BFBFBF' }}>
<Option value="FINISH">已完成</Option> &#xe835;
<Option value="EXPIRED">未成功开课</Option> </span>
}>
<Option value='UN_START'>待开课</Option>
<Option value='STARTING'>上课中</Option>
<Option value='FINISH'>已完成</Option>
<Option value='EXPIRED'>未成功开课</Option>
</Select> </Select>
</div> </div>
} )}
{ expandFilter && {expandFilter && (
<div className="search-condition__item"> <div className='search-condition__item'>
<span className="shelf-status">学院展示:</span> <span className='shelf-status'>学院展示:</span>
<Select <Select
style={{ width: "calc(100% - 70px)" }} style={{ width: 'calc(100% - 70px)' }}
placeholder="请选择" placeholder='请选择'
allowClear={true} allowClear={true}
value={shelfState} value={shelfState}
onChange={(value) => { this.handleChangeQuery('shelfState', value) }} onChange={(value) => {
suffixIcon={<span className="icon iconfont" style={{fontSize:'12px',color:'#BFBFBF'}}>&#xe835;</span>} this.handleChangeQuery('shelfState', value)
> }}
<Option value="YES">开启</Option> suffixIcon={
<Option value="NO">关闭</Option> <span className='icon iconfont' style={{ fontSize: '12px', color: '#BFBFBF' }}>
&#xe835;
</span>
}>
<Option value='YES'>开启</Option>
<Option value='NO'>关闭</Option>
</Select> </Select>
</div> </div>
} )}
</div> </div>
<div className="reset-fold-area"> <div className='reset-fold-area'>
<Tooltip title="清空筛选"><span className="resetBtn iconfont icon" onClick={this.handleReset}>&#xe61b; </span></Tooltip> <Tooltip title='清空筛选'>
<span style={{ cursor: 'pointer' }} className="fold-btn" onClick={() => { <span className='resetBtn iconfont icon' onClick={this.handleReset}>
this.setState({expandFilter:!expandFilter}); &#xe61b;{' '}
}}>{this.state.expandFilter ? <span><span>收起</span><span className="iconfont icon fold-icon" >&#xe82d; </span> </span> : <span>展开<span className="iconfont icon fold-icon" >&#xe835; </span></span>}</span> </span>
</Tooltip>
<span
style={{ cursor: 'pointer' }}
className='fold-btn'
onClick={() => {
this.setState({ expandFilter: !expandFilter })
}}>
{this.state.expandFilter ? (
<span>
<span>收起</span>
<span className='iconfont icon fold-icon'>&#xe82d; </span>{' '}
</span>
) : (
<span>
展开<span className='iconfont icon fold-icon'>&#xe835; </span>
</span>
)}
</span>
</div> </div>
</Row> </Row>
</div> </div>
...@@ -272,4 +327,4 @@ class LiveCourseFilter extends React.Component { ...@@ -272,4 +327,4 @@ class LiveCourseFilter extends React.Component {
} }
} }
export default withRouter(LiveCourseFilter); export default withRouter(LiveCourseFilter)
\ No newline at end of file
import React from 'react'; import React from 'react'
import { Row, Input, Select,Tooltip } from 'antd'; import { Row, Input, Select, Tooltip } from 'antd'
import RangePicker from "@/modules/common/DateRangePicker"; import RangePicker from '@/modules/common/DateRangePicker'
import './GraphicsCourseFilter.less'; import './GraphicsCourseFilter.less'
import moment from 'moment'; import moment from 'moment'
import StoreService from "@/domains/store-domain/storeService"; import StoreService from '@/domains/store-domain/storeService'
const { Search } = Input; const { Search } = Input
const { Option } = Select; const { Option } = Select
const DEFAULT_QUERY = { const DEFAULT_QUERY = {
courseName: null, // 课程名称 courseName: null, // 课程名称
operatorId: null, // 创建人 operatorId: null, // 创建人
beginTime: null, // 开始日期 beginTime: null, // 开始日期
endTime: null, // 结束日期 endTime: null, // 结束日期
shelfState:null, shelfState: null,
} }
const defaultTeacherQuery = { const defaultTeacherQuery = {
size: 10, size: 10,
current: 1, current: 1,
nickName:null nickName: null,
} }
class GraphicsCourseFilter extends React.Component { class GraphicsCourseFilter extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
query: { ...DEFAULT_QUERY }, // 使用扩展运算符,避免浅拷贝 query: { ...DEFAULT_QUERY }, // 使用扩展运算符,避免浅拷贝
teacherQuery: defaultTeacherQuery, teacherQuery: defaultTeacherQuery,
teacherList:[], teacherList: [],
expandFilter:false expandFilter: false,
} }
} }
componentDidMount() { componentDidMount() {
this.getTeacherList(); this.getTeacherList()
} }
getTeacherList(current = 1, selectList){ getTeacherList(current = 1, selectList) {
const { teacherQuery,teacherList} = this.state; const { teacherQuery, teacherList } = this.state
const _query = { const _query = {
...teacherQuery, ...teacherQuery,
current, current,
size:10 size: 10,
}; }
StoreService.getStoreUserBasicPage( _query).then((res) => { StoreService.getStoreUserBasicPage(_query).then((res) => {
const { result = {} } = res; const { result = {} } = res
const { records = [], total = 0, hasNext } = result; const { records = [], total = 0, 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,
}) })
}); })
} }
// 滑动加载更多讲师列表 // 滑动加载更多讲师列表
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)
}
)
} }
} }
// 改变搜索条件 // 改变搜索条件
handleChangeQuery = (field, value) => { handleChangeQuery = (field, value) => {
this.setState({ this.setState(
{
query: { query: {
...this.state.query, ...this.state.query,
[field]: value, [field]: value,
current: 1, current: 1,
} },
}, () => { },
if (field === 'courseName') return; () => {
if (field === 'courseName') return
this.props.onChange(this.state.query) this.props.onChange(this.state.query)
}); }
)
} }
handleChangeDates = (dates) => { handleChangeDates = (dates) => {
const query = _.clone(this.state.query); const query = _.clone(this.state.query)
if (_.isEmpty(dates)) { if (_.isEmpty(dates)) {
delete query.beginTime; delete query.beginTime
delete query.endTime; delete query.endTime
} else { } else {
query.beginTime = dates[0].valueOf(); query.beginTime = dates[0]?.startOf('day').valueOf()
query.endTime = dates[1].valueOf(); query.endTime = dates[0]?.endOf('day').valueOf()
} }
this.setState({ this.setState(
query:{ {
query: {
...query, ...query,
current: 1, current: 1,
},
},
() => {
this.props.onChange(this.state.query)
} }
}, () => { )
this.props.onChange(this.state.query);
})
} }
// 重置搜索条件 // 重置搜索条件
handleReset = () => { handleReset = () => {
this.setState({ this.setState(
{
query: DEFAULT_QUERY, query: DEFAULT_QUERY,
}, () => { },
this.props.onChange(this.state.query); () => {
}) this.props.onChange(this.state.query)
}
)
} }
render() { render() {
const { const {
query: { query: { courseName, operator, beginTime, endTime, operatorId, shelfState },
courseName,
operator,
beginTime,
endTime,
operatorId,
shelfState
},
expandFilter, expandFilter,
teacherList, teacherList,
teacherQuery teacherQuery,
} = this.state; } = this.state
return ( return (
<div className="video-course-filter"> <div className='video-course-filter'>
<Row type="flex" justify="space-between" align="top"> <Row type='flex' justify='space-between' align='top'>
<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={courseName} value={courseName}
placeholder="搜索图文课名称" placeholder='搜索图文课名称'
onChange={(e) => { this.handleChangeQuery('courseName', e.target.value)}} onChange={(e) => {
onSearch={ () => { this.props.onChange(this.state.query) } } this.handleChangeQuery('courseName', e.target.value)
style={{ width: "calc(100% - 84px)" }} }}
enterButton={<span className="icon iconfont">&#xe832;</span>} onSearch={() => {
this.props.onChange(this.state.query)
}}
style={{ width: 'calc(100% - 84px)' }}
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/> />
</div> </div>
<div className="search-condition__item"> <div className='search-condition__item'>
<span>创建人:</span> <span>创建人:</span>
<Select <Select
placeholder="请选择创建人" placeholder='请选择创建人'
style={{width:"calc(100% - 70px)"}} style={{ width: 'calc(100% - 70px)' }}
showSearch showSearch
allowClear allowClear
filterOption={(input, option) => option} filterOption={(input, option) => option}
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>
}
value={operatorId} value={operatorId}
onChange={(value) => { onChange={(value) => {
this.handleChangeQuery('operatorId', value) this.handleChangeQuery('operatorId', value)
}} }}
onSearch={(value) => { onSearch={(value) => {
teacherQuery.nickName = value teacherQuery.nickName = value
this.setState({ this.setState(
teacherQuery {
}, () => { teacherQuery,
},
() => {
this.getTeacherList() this.getTeacherList()
}) }
)
}} }}
onClear ={(value)=>{ onClear={(value) => {
this.setState({ this.setState(
teacherQuery:{ {
teacherQuery: {
size: 10, size: 10,
current: 1, current: 1,
nickName:null nickName: null,
} },
}, () => { },
() => {
this.getTeacherList() this.getTeacherList()
})
}
} }
> )
}}>
{_.map(teacherList, (item, index) => { {_.map(teacherList, (item, index) => {
return ( return (
<Select.Option value={item.id} key={item.id}>{item.nickName}</Select.Option> <Select.Option value={item.id} key={item.id}>
); {item.nickName}
</Select.Option>
)
})} })}
</Select> </Select>
</div> </div>
<div className="search-condition__item"> <div className='search-condition__item'>
<span className="search-date">创建日期:</span> <span className='search-date'>创建日期:</span>
<RangePicker <RangePicker
id="course_date_picker" id='course_date_picker'
allowClear={false} allowClear={false}
value={ beginTime ? [moment(beginTime), moment(endTime)] : null } value={beginTime ? [moment(beginTime), moment(endTime)] : null}
format={"YYYY-MM-DD"} format={'YYYY-MM-DD'}
onChange={(dates) => { this.handleChangeDates(dates) }} onChange={(dates) => {
style={{ width: "calc(100% - 70px)" }} this.handleChangeDates(dates)
}}
style={{ width: 'calc(100% - 70px)' }}
/> />
</div> </div>
{ expandFilter && {expandFilter && (
<div className="search-condition__item"> <div className='search-condition__item'>
<span className="shelf-status">学院展示:</span> <span className='shelf-status'>学院展示:</span>
<Select <Select
style={{ width: "calc(100% - 84px)" }} style={{ width: 'calc(100% - 84px)' }}
placeholder="请选择" placeholder='请选择'
allowClear={true} allowClear={true}
value={shelfState} value={shelfState}
onChange={(value) => { this.handleChangeQuery('shelfState', value) }} onChange={(value) => {
suffixIcon={<span className="icon iconfont" style={{fontSize:'12px',color:'#BFBFBF'}}>&#xe835;</span>} this.handleChangeQuery('shelfState', value)
> }}
<Option value="YES">开启</Option> suffixIcon={
<Option value="NO">关闭</Option> <span className='icon iconfont' style={{ fontSize: '12px', color: '#BFBFBF' }}>
&#xe835;
</span>
}>
<Option value='YES'>开启</Option>
<Option value='NO'>关闭</Option>
</Select> </Select>
</div> </div>
} )}
</div> </div>
<div className="reset-fold-area"> <div className='reset-fold-area'>
<Tooltip title="清空筛选"><span className="resetBtn iconfont icon" onClick={this.handleReset}>&#xe61b; </span></Tooltip> <Tooltip title='清空筛选'>
<span style={{ cursor: 'pointer' }} className="fold-btn" onClick={() => { <span className='resetBtn iconfont icon' onClick={this.handleReset}>
this.setState({expandFilter:!expandFilter}); &#xe61b;{' '}
}}>{this.state.expandFilter ? <span><span>收起</span><span className="iconfont icon fold-icon" >&#xe82d; </span> </span> : <span>展开<span className="iconfont icon fold-icon" >&#xe835; </span></span>}</span> </span>
</Tooltip>
<span
style={{ cursor: 'pointer' }}
className='fold-btn'
onClick={() => {
this.setState({ expandFilter: !expandFilter })
}}>
{this.state.expandFilter ? (
<span>
<span>收起</span>
<span className='iconfont icon fold-icon'>&#xe82d; </span>{' '}
</span>
) : (
<span>
展开<span className='iconfont icon fold-icon'>&#xe835; </span>
</span>
)}
</span>
</div> </div>
</Row> </Row>
</div> </div>
...@@ -226,4 +271,4 @@ class GraphicsCourseFilter extends React.Component { ...@@ -226,4 +271,4 @@ class GraphicsCourseFilter extends React.Component {
} }
} }
export default GraphicsCourseFilter; export default GraphicsCourseFilter
.video-course-list { .video-course-list {
margin-top: 12px; margin-top: 12px;
.video-list-table{ .video-list-table {
tbody { tbody {
tr{ tr {
&:nth-child(even){ &:nth-child(even) {
background: transparent !important; background: transparent !important;
td{ td {
background:#FFF !important; background: #fff !important;
} }
} }
&:nth-child(odd){ &:nth-child(odd) {
background: #FAFAFA !important; background: #fafafa !important;
td{ td {
background: #FAFAFA !important; background: #fafafa !important;
} }
} }
&:hover{ &:hover {
td{ td {
background:#F3f6fa !important; background: #f3f6fa !important;
} }
} }
} }
} }
} }
.watchUserCount{ .watchUserCount {
text-align:right; text-align: right;
padding:16px; padding: 16px;
} }
.operate-text { .operate-text {
color: #5289FA; color: #5289fa;
cursor: pointer; cursor: pointer;
} }
.operate { .operate {
display: flex; display: flex;
&__item { &__item {
color: #5289FA; color: #5289fa;
cursor: pointer; cursor: pointer;
&.split { &.split {
margin: 0 8px; margin: 0 8px;
color: #BFBFBF; color: #bfbfbf;
} }
} }
} }
.more-operate{ .more-operate {
line-height:20px; line-height: 20px;
} }
.record__item { .record__item {
display: flex; display: flex;
...@@ -56,21 +56,20 @@ ...@@ -56,21 +56,20 @@
height: 50px; height: 50px;
border-radius: 2px; border-radius: 2px;
margin-right: 8px; margin-right: 8px;
background-color: #666; // background-color: #666;
} }
.course-name { .course-name {
color: #666; color: #666;
width:188px; width: 188px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
height:48px; height: 48px;
} }
} }
} }
.video-course-more-menu { .video-course-more-menu {
......
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-07-23 14:54:16 * @Date: 2020-07-23 14:54:16
* @LastEditors: wufan * @LastEditors: fusanqiasng
* @LastEditTime: 2021-02-01 16:33:18 * @LastEditTime: 2021-05-24 21:02:21
* @Description: 大班直播课预览弹窗 * @Description: 大班直播课预览弹窗
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
*/ */
import React from 'react'; import React from 'react'
import { Modal } from 'antd'; import { Modal } from 'antd'
import moment from 'moment'; import moment from 'moment'
import './PreviewCourseModal.less'; import './PreviewCourseModal.less'
const courseStateShow = { const courseStateShow = {
UN_START: { UN_START: {
title: "待开课", title: '待开课'
}, },
STARTING: { STARTING: {
title: "上课中", title: '上课中'
}, },
FINISH: { FINISH: {
title: "已完成", title: '已完成'
}, },
EXPIRED: { EXPIRED: {
code: 4, code: 4,
title: "未成功开课", title: '未成功开课',
color: "#CCCCCC", color: '#CCCCCC'
}, }
}; }
class PreviewCourseModal extends React.Component { class PreviewCourseModal extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {}
}
} }
dealTimeDuration = (time) => { dealTimeDuration = (time) => {
const diff = Math.floor(time % 3600); const diff = Math.floor(time % 3600)
let hours = Math.floor(time / 3600); let hours = Math.floor(time / 3600)
let mins = Math.floor(diff / 60); let mins = Math.floor(diff / 60)
let seconds = Math.floor(time % 60); let seconds = Math.floor(time % 60)
hours = hours < 10 ? ("0" + hours) : hours; hours = hours < 10 ? '0' + hours : hours
mins = mins < 10 ? ("0" + mins) : mins; mins = mins < 10 ? '0' + mins : mins
seconds = seconds < 10 ? ("0" + seconds) : seconds; seconds = seconds < 10 ? '0' + seconds : seconds
return hours + ":" + mins + ":" + seconds; return hours + ':' + mins + ':' + seconds
} }
dealWithTime = (startTime, endTime) => { dealWithTime = (startTime, endTime) => {
const startDate = new Date(Number(startTime)); const startDate = new Date(Number(startTime))
const endDate = new Date(Number(endTime)); const endDate = new Date(Number(endTime))
const year = startDate.getFullYear(); const year = startDate.getFullYear()
const month = (startDate.getMonth() + 1) < 10 ? `0${startDate.getMonth() + 1}` : startDate.getMonth() + 1; const month = startDate.getMonth() + 1 < 10 ? `0${startDate.getMonth() + 1}` : startDate.getMonth() + 1
const day = startDate.getDate() < 10 ? `0${startDate.getDate()}` : startDate.getDate(); const day = startDate.getDate() < 10 ? `0${startDate.getDate()}` : startDate.getDate()
const startHour = startDate.getHours() < 10 ? `0${startDate.getHours()}` : startDate.getHours(); const startHour = startDate.getHours() < 10 ? `0${startDate.getHours()}` : startDate.getHours()
const startMinute = startDate.getMinutes() < 10 ? `0${startDate.getMinutes()}` : startDate.getMinutes(); const startMinute = startDate.getMinutes() < 10 ? `0${startDate.getMinutes()}` : startDate.getMinutes()
const endHour = endDate.getHours() < 10 ? `0${endDate.getHours()}` : endDate.getHours(); const endHour = endDate.getHours() < 10 ? `0${endDate.getHours()}` : endDate.getHours()
const endMinute = endDate.getMinutes() < 10 ? `0${endDate.getMinutes()}` : endDate.getMinutes(); const endMinute = endDate.getMinutes() < 10 ? `0${endDate.getMinutes()}` : endDate.getMinutes()
const liveDateStr = `${year}-${month}-${day}`; const liveDateStr = `${year}-${month}-${day}`
const startTimeStr = `${startHour}:${startMinute}`; const startTimeStr = `${startHour}:${startMinute}`
const endTimeStr = `${endHour}:${endMinute}`; const endTimeStr = `${endHour}:${endMinute}`
return { return {
liveDateStr, liveDateStr,
startTimeStr, startTimeStr,
endTimeStr, endTimeStr
}; }
} }
render() { render() {
const { courseBasicInfo, courseClassInfo = {}, courseIntroInfo, type,courseState,origin} = this.props; const { courseBasicInfo, courseClassInfo = {}, courseIntroInfo, type, courseState, origin } = this.props
const { coverUrl, courseName, scheduleVideoUrl,videoDuration} = courseBasicInfo; const { coverUrl, courseName, scheduleVideoUrl, videoDuration } = courseBasicInfo
const { liveDate, calendarTime,startTime,endTime,timeHorizonStart, timeHorizonEnd, teacherName } = courseClassInfo; const { liveDate, calendarTime, startTime, endTime, timeHorizonStart, timeHorizonEnd, teacherName } = courseClassInfo
const { introduce } = courseIntroInfo; const { introduce } = courseIntroInfo
let liveDateStr, startTimeStr, endTimeStr; let liveDateStr, startTimeStr, endTimeStr
if (type === 'add') { if (type === 'add') {
const _liveDate = moment(calendarTime[0]).format("YYYY-MM-DD"); const _liveDate = moment(calendarTime[0]).format('YYYY-MM-DD')
console.log("_liveDate",_liveDate); console.log('_liveDate', _liveDate)
const _timeHorizonStart = moment(startTime).format('HH:mm'); const _timeHorizonStart = moment(startTime).format('HH:mm')
const _timeHorizonEnd = moment(endTime).format('HH:mm'); const _timeHorizonEnd = moment(endTime).format('HH:mm')
const _startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x'); const _startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x')
const _endTime = moment(_liveDate + ' ' + _timeHorizonEnd).format('x'); const _endTime = moment(_liveDate + ' ' + _timeHorizonEnd).format('x')
const { const { liveDateStr: _liveDateStr, startTimeStr: _startTimeStr, endTimeStr: _endTimeStr } = this.dealWithTime(_startTime, _endTime)
liveDateStr: _liveDateStr,
startTimeStr: _startTimeStr, liveDateStr = _liveDateStr
endTimeStr: _endTimeStr startTimeStr = _startTimeStr
} = this.dealWithTime(_startTime, _endTime); endTimeStr = _endTimeStr
} else {
liveDateStr = _liveDateStr; const _liveDate = moment(liveDate).format('YYYY-MM-DD')
startTimeStr = _startTimeStr; const _timeHorizonStart = moment(timeHorizonStart).format('HH:mm')
endTimeStr = _endTimeStr; const _timeHorizonEnd = moment(timeHorizonEnd).format('HH:mm')
}else{ const startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x')
const _liveDate = moment(liveDate).format("YYYY-MM-DD"); const endTime = moment(_liveDate + ' ' + _timeHorizonEnd).format('x')
const _timeHorizonStart = moment(timeHorizonStart).format('HH:mm'); const { liveDateStr: _liveDateStr, startTimeStr: _startTimeStr, endTimeStr: _endTimeStr } = this.dealWithTime(startTime, endTime)
const _timeHorizonEnd = moment(timeHorizonEnd).format('HH:mm');
const startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x'); liveDateStr = _liveDateStr
const endTime = moment(_liveDate + ' ' + _timeHorizonEnd).format('x'); startTimeStr = _startTimeStr
const { endTimeStr = _endTimeStr
liveDateStr: _liveDateStr,
startTimeStr: _startTimeStr,
endTimeStr: _endTimeStr
} = this.dealWithTime(startTime, endTime);
liveDateStr = _liveDateStr;
startTimeStr = _startTimeStr;
endTimeStr = _endTimeStr;
} }
return ( return (
<Modal <Modal
title="预览" title='预览'
visible={true} visible={true}
width={680} width={680}
onCancel={this.props.close} onCancel={this.props.close}
footer={null} footer={null}
maskClosable={false} maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>} closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
className="preview-live-course-modal" className='preview-live-course-modal'>
> <div className='container__wrap'>
<div className="container__wrap"> <div className='container'>
<div className="container"> <div className='container__header'>
<div className="container__header"> {type === 'videoCourse' ? (
{
type === 'videoCourse' ?
<video <video
controls controls
src={scheduleVideoUrl} src={scheduleVideoUrl}
poster={coverUrl ? coverUrl : `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`} poster={coverUrl ? coverUrl : `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`}
className="course-url" className='course-url'
/> : />
<img src={coverUrl} className="course-cover" /> ) : (
} <img src={coverUrl} className='course-cover' />
)}
</div> </div>
{ {type === 'videoCourse' ? (
type === 'videoCourse' ? <div className='container__body'>
<div className="container__body"> <div className='title__name'>{courseName}</div>
<div className="title__name">{courseName}</div> {videoDuration && <div>视频时长:{this.dealTimeDuration(videoDuration)}</div>}
{videoDuration &&
<div>视频时长:{this.dealTimeDuration(videoDuration)}</div>
}
</div> </div>
: ) : (
<div className="container__body"> <div className='container__body'>
<div className="container__body__title"> <div className='container__body__title'>
<div className="title__name">{courseName}</div> <div className='title__name'>{courseName}</div>
<div className="title__state">{courseStateShow[courseState].title}</div> <div className='title__state'>{courseStateShow[courseState].title}</div>
</div> </div>
<div className="container__body__time"> <div className='container__body__time'>
<span className="time__label">上课时间:</span> <span className='time__label'>上课时间:</span>
<span className="time__value"> <span className='time__value'>
{ {[
[
<span>{liveDateStr}&nbsp;</span>, <span>{liveDateStr}&nbsp;</span>,
<span>{startTimeStr}~{endTimeStr }</span> <span>
] {startTimeStr}~{endTimeStr}
} </span>
]}
</span> </span>
</div> </div>
<div className="container__body__teacher"> <div className='container__body__teacher'>
<span className="teacher__label">上课老师:</span> <span className='teacher__label'>上课老师:</span>
<span className="teacher__value">{teacherName}</span> <span className='teacher__value'>{teacherName}</span>
</div> </div>
</div> </div>
} )}
<div className="container__introduction"> <div className='container__introduction'>
{ type === 'videoCourse' ? {type === 'videoCourse' ? (
<div className="container__introduction__title">视频课简介</div> <div className='container__introduction__title'>视频课简介</div>
: ) : (
<div className="container__introduction__title">直播课简介</div> <div className='container__introduction__title'>直播课简介</div>
} )}
<div className="container__introduction__list editor-box"> <div className='container__introduction__list editor-box'>
<div <div
className="intro-item text" className='intro-item text'
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: introduce __html: introduce
}} }}
...@@ -192,4 +176,4 @@ class PreviewCourseModal extends React.Component { ...@@ -192,4 +176,4 @@ class PreviewCourseModal extends React.Component {
} }
} }
export default PreviewCourseModal; export default PreviewCourseModal
import React from 'react'; import React from 'react'
import {Table, Modal,Input} from 'antd'; import { Table, Modal, Input } from 'antd'
import { PageControl } from "@/components"; import { PageControl } from '@/components'
import CourseService from "@/domains/course-domain/CourseService"; import CourseService from '@/domains/course-domain/CourseService'
import PlanService from "@/domains/plan-domain/planService"; import PlanService from '@/domains/plan-domain/planService'
import User from '@/common/js/user' import User from '@/common/js/user'
import './RelatedPlanModal.less'; import './RelatedPlanModal.less'
import _ from "underscore"; import _ from 'underscore'
const { Search } = Input; const { Search } = Input
class RelatedPlanModal extends React.Component { class RelatedPlanModal extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
dataSource:[], dataSource: [],
size:10, size: 10,
query: { query: {
current: 1, current: 1
}, },
totalCount:0, totalCount: 0,
selectPlanList:{}, selectPlanList: {}
}; }
} }
componentDidMount() { componentDidMount() {
this.handleFetchDataList(); this.handleFetchDataList()
} }
// 获取培训计划列表 // 获取培训计划列表
handleFetchDataList = () => { handleFetchDataList = () => {
const {query,size,totalCount} = this.state const { query, size } = this.state
const params ={ const params = {
...query, ...query,
size, size,
storeId:User.getStoreId() storeId: User.getStoreId()
} }
PlanService.getStorePlanAll(params).then((res) => { PlanService.getStorePlanAll(params).then((res) => {
const { result = {} } = res ; const { result = {} } = res
const { records = [], total = 0 } = result; const { records = [], total = 0 } = result
this.setState({ this.setState({
dataSource: records, dataSource: records,
totalCount: Number(total) totalCount: Number(total)
}); })
}); })
} }
handleChangePlanName = (value)=>{ handleChangePlanName = (value) => {
const {query} = this.state; const { query } = this.state
query.planName = value; query.planName = value
query.current = 1; query.current = 1
this.setState({ this.setState({
query query
}) })
...@@ -54,9 +54,14 @@ class RelatedPlanModal extends React.Component { ...@@ -54,9 +54,14 @@ class RelatedPlanModal extends React.Component {
if (current == size) { if (current == size) {
return return
} }
this.setState({ this.setState(
{
size size
},()=>{this.handleFetchDataList()}) },
() => {
this.handleFetchDataList()
}
)
} }
// 请求表头 // 请求表头
...@@ -66,14 +71,12 @@ class RelatedPlanModal extends React.Component { ...@@ -66,14 +71,12 @@ class RelatedPlanModal extends React.Component {
title: '培训计划', title: '培训计划',
key: 'planName', key: 'planName',
dataIndex: 'planName', dataIndex: 'planName',
render:(val,record)=>{ render: (val, record) => {
return ( return <span>{val}</span>
<span>{val}</span>
)
} }
} }
]; ]
return columns; return columns
} }
parseTaskColumns = (parentIndex) => { parseTaskColumns = (parentIndex) => {
...@@ -82,173 +85,189 @@ class RelatedPlanModal extends React.Component { ...@@ -82,173 +85,189 @@ class RelatedPlanModal extends React.Component {
title: '任务名称', title: '任务名称',
key: 'taskName', key: 'taskName',
dataIndex: 'taskName', dataIndex: 'taskName',
render:(val,record)=>{ render: (val, record) => {
return ( return <span>{val}</span>
<span>{val}</span>
)
} }
} }
]; ]
return columns; return columns
} }
selectPlanList = (record,selected,planId) =>{ selectPlanList = (record, selected, planId) => {
const { selectPlanList } = this.props; const { selectPlanList } = this.props
let _selectPlanList = {...selectPlanList}; let _selectPlanList = { ...selectPlanList }
if (selected) { if (selected) {
if(!_selectPlanList[planId]){ if (!_selectPlanList[planId]) {
_selectPlanList[planId] = {} _selectPlanList[planId] = {}
} }
_selectPlanList[planId].taskBaseVOList = []; _selectPlanList[planId].taskBaseVOList = []
_selectPlanList[planId].planId = planId; _selectPlanList[planId].planId = planId
_selectPlanList[planId].taskBaseVOList.push(record); _selectPlanList[planId].taskBaseVOList.push(record)
} else { } else {
if(!_selectPlanList[planId]){ if (!_selectPlanList[planId]) {
_selectPlanList[planId] = {} _selectPlanList[planId] = {}
} }
_selectPlanList[planId].taskBaseVOList = []; _selectPlanList[planId].taskBaseVOList = []
_selectPlanList[planId].planId = planId; _selectPlanList[planId].planId = planId
} }
this.props.onChange(_selectPlanList); this.props.onChange(_selectPlanList)
// this.setState({selectPlanList:_selectPlanList}); // this.setState({selectPlanList:_selectPlanList});
} }
handleSelectPlanListData(selectPlanList){ handleSelectPlanListData(selectPlanList) {
let _selectPlanList = []; let _selectPlanList = []
for(let key in selectPlanList ){ for (let key in selectPlanList) {
let item = {}; let item = {}
if(selectPlanList[key].taskBaseVOList){ if (selectPlanList[key].taskBaseVOList) {
item.planId = selectPlanList[key].planId; item.planId = selectPlanList[key].planId
if(selectPlanList[key].taskBaseVOList[0]){ if (selectPlanList[key].taskBaseVOList[0]) {
item.taskId = selectPlanList[key].taskBaseVOList[0].taskId; item.taskId = selectPlanList[key].taskBaseVOList[0].taskId
} }
} }
if(item.taskId){ if (item.taskId) {
_selectPlanList.push(item) _selectPlanList.push(item)
} }
} }
return _selectPlanList; return _selectPlanList
} }
confirmRelatedPlan =()=>{ confirmRelatedPlan = () => {
const {selectPlanList } = this.props; const { selectPlanList } = this.props
const params = { const params = {
courseId:this.props.selectCourseId, courseId: this.props.selectCourseId,
relatedPlanList:this.handleSelectPlanListData(selectPlanList), relatedPlanList: this.handleSelectPlanListData(selectPlanList),
storeId:User.getStoreId(), storeId: User.getStoreId()
} }
CourseService.relatedCourseToPlan(params).then((res) => { CourseService.relatedCourseToPlan(params).then((res) => {
this.props.onConfirm(); this.props.onConfirm()
}); })
} }
getSelectLength = (selectList)=>{ getSelectLength = (selectList) => {
let num = 0; let num = 0
for(let key in selectList ){ for (let key in selectList) {
if(selectList[key].taskBaseVOList.length > 0){ if (selectList[key].taskBaseVOList.length > 0) {
num = num + 1 num = num + 1
} }
} }
return num; return num
} }
clearSelect = ()=>{ clearSelect = () => {
const _selectPlanList = {}; const _selectPlanList = {}
this.props.onChange(_selectPlanList); this.props.onChange(_selectPlanList)
} }
render() { render() {
const { size,dataSource,totalCount,query} = this.state; const { size, dataSource, totalCount, query } = this.state
const { visible,selectPlanList} = this.props; const { visible, selectPlanList } = this.props
return ( return (
<Modal <Modal
title="关联培训计划" title='关联培训计划'
onCancel={this.props.onClose} onCancel={this.props.onClose}
maskClosable={false} maskClosable={false}
visible={visible} visible={visible}
className="related-plan-modal" className='related-plan-modal'
closable={true} closable={true}
width={800} width={800}
onOk={() => this.confirmRelatedPlan() } onOk={() => this.confirmRelatedPlan()}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>} closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}>
> <div className='search-container'>
<div className="search-container"> <Search
<Search placeholder="搜索培训计划名称" placeholder='搜索培训计划名称'
style={{ width: 207 }} style={{ width: 207 }}
onChange={(e) => { this.handleChangePlanName(e.target.value)}} onChange={(e) => {
onSearch={ () => { this.handleFetchDataList()}} this.handleChangePlanName(e.target.value)
enterButton={<span className="icon iconfont">&#xe832;</span>} }}
onSearch={() => {
this.handleFetchDataList()
}}
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/> />
</div> </div>
<div className="select-container"> <div className='select-container'>
<span className="con"> <span className='con'>
<div> <div>
<span className="icon iconfont tip">&#xe6f2;</span> <span className='icon iconfont tip'>&#xe6f2;</span>
<span className="text">已选择{this.getSelectLength(selectPlanList)}个任务</span> <span className='text'>已选择{this.getSelectLength(selectPlanList)}个任务</span>
</div> </div>
<div> <div>
<span className="clear" onClick={this.clearSelect}>清空</span> <span className='clear' onClick={this.clearSelect}>
清空
</span>
</div> </div>
</span> </span>
</div> </div>
<div> <div>
<Table <Table
rowKey={record => record.planId} rowKey={(record) => record.planId}
className="plan-table" className='plan-table'
dataSource={dataSource} dataSource={dataSource}
columns={this.parsePlanColumns()} columns={this.parsePlanColumns()}
pagination={false} pagination={false}
expandedRowRender={(_record,index) => { expandedRowRender={(_record, index) => {
if(!_record.taskBaseVOList){ if (!_record.taskBaseVOList) {
return return
} }
if (_record.taskBaseVOList.length !== 0 ){ if (_record.taskBaseVOList.length !== 0) {
const selectPlan = selectPlanList[_record.planId] const selectPlan = selectPlanList[_record.planId]
let taskBaseVOList = []; let taskBaseVOList = []
if(selectPlan){ if (selectPlan) {
taskBaseVOList = selectPlan.taskBaseVOList; taskBaseVOList = selectPlan.taskBaseVOList
} }
console.log('taskBaseVOList',taskBaseVOList); console.log('taskBaseVOList', taskBaseVOList)
return <div> return (
<div>
<Table <Table
rowKey={record => record.taskId} rowKey={(record) => record.taskId}
pagination={false} pagination={false}
dataSource={_record.taskBaseVOList} dataSource={_record.taskBaseVOList}
columns={this.parseTaskColumns(index)} columns={this.parseTaskColumns(index)}
className="child-table" className='child-table'
rowSelection={{ rowSelection={{
type: 'checkbox', type: 'checkbox',
selectedRowKeys: _.pluck(taskBaseVOList, 'taskId'), selectedRowKeys: _.pluck(taskBaseVOList, 'taskId'),
onSelect: (record, selected) => { onSelect: (record, selected) => {
this.selectPlanList(record,selected,_record.planId); this.selectPlanList(record, selected, _record.planId)
},
onSelectAll: (selected, _selectedRows, changeRows) => {
}, },
onSelectAll: (selected, _selectedRows, changeRows) => {}
}} }}
/> />
</div> </div>
)
}
}}
rowClassName={(record, index) => {
if (index % 2 === 0) {
return 'odd-row'
} else {
return 'even-row'
} }
}} }}
rowClassName={(record,index)=>{if(index%2===0){return 'odd-row'}else{ return 'even-row'}}}
/> />
{dataSource.length >0 && {dataSource.length > 0 && (
<div className="box-footer"> <div className='box-footer'>
<PageControl <PageControl
current={query.current - 1} current={query.current - 1}
pageSize={size} pageSize={size}
total={totalCount} total={totalCount}
size="small" size='small'
toPage={(page) => { toPage={(page) => {
const _query = {...query, current: page + 1}; const _query = { ...query, current: page + 1 }
this.setState({ this.setState(
query:_query {
},()=>{ this.handleFetchDataList()}) query: _query
},
() => {
this.handleFetchDataList()
}
)
}} }}
onShowSizeChange={this.onShowSizeChange} onShowSizeChange={this.onShowSizeChange}
/> />
</div> </div>
} )}
</div> </div>
</Modal> </Modal>
) )
} }
} }
export default RelatedPlanModal; export default RelatedPlanModal
\ No newline at end of file
...@@ -6,219 +6,179 @@ ...@@ -6,219 +6,179 @@
* @Description: 大班直播分享弹窗 * @Description: 大班直播分享弹窗
*/ */
import React from 'react'; import React from 'react'
import { Modal, Input, Button, message } from 'antd'; import { Modal, Button, message } from 'antd'
import domtoimage from 'dom-to-image'; import domtoimage from 'dom-to-image'
import html2canvas from 'html2canvas';
import qrcode from "@/libs/qrcode/qrcode.js";
import User from '@/common/js/user';
import $ from 'jquery';
import CourseService from "@/domains/course-domain/CourseService";
import './ShareLiveModal.less'; import qrcode from '@/libs/qrcode/qrcode.js'
import User from '@/common/js/user'
import $ from 'jquery'
import _ from 'underscore'
import CourseService from '@/domains/course-domain/CourseService'
const storeName = User.getStoreName(); import './ShareLiveModal.less'
const DEFAULT_COVER = 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png';
class ShareLiveModal extends React.Component { const DEFAULT_COVER = 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'
class ShareLiveModal extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
shareUrl: 'https://xiaomai5.com/liveShare?courseId=12' shareUrl: 'https://xiaomai5.com/liveShare?courseId=12',
} }
} }
componentDidMount() { componentDidMount() {
// 获取短链接 // 获取短链接
this.handleConvertShortUrl(); this.handleConvertShortUrl()
// const { needStr, data, type } = this.props;
// const { courseName, coverUrl = DEFAULT_COVER, scheduleVideoUrl } = data;
// const { shareUrl ,time} = this.state;
// // 判断是否是默认图, 默认图不需要在URL后面增加字符串
// const isDefaultCover = coverUrl === DEFAULT_COVER;
// let coverImgSrc = coverUrl;
// if(type === 'videoClass'){
// if(!coverUrl || isDefaultCover){
// coverImgSrc = `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast&anystring=anystring`
// }
// }else{
// data.courseMediaVOS.map((item,index)=>{
// if( item.contentType === "COVER"){
// coverImgSrc = item.mediaUrl;
// }
// })
// }
// let image = new Image()
// image.src =coverImgSrc + '?v=' + Math.random()
// image.crossOrigin = "*"
// image.onload = ()=>{
// let imgData = getBase64Image(image)
// this.setState({imgData})
// }
// function getBase64Image (img) {
// let canvas = document.createElement("canvas")
// canvas.width = img.width
// canvas.height = img.height
// let ctx = canvas.getContext("2d")
// ctx.drawImage(img, 0, 0, img.width, img.height)
// let dataURL = canvas.toDataURL("image/png")
// return dataURL
// }
} }
handleConvertShortUrl = () => { handleConvertShortUrl = () => {
const { longUrl } = this.props.data; const { longUrl } = this.props.data
// 发请求 // 发请求
CourseService.getQrcode({ CourseService.getQrcode({
urls: [longUrl] urls: [longUrl],
}).then((res) => { }).then((res) => {
const { result = [] } = res; const { result = [] } = res
this.setState({ this.setState(
shareUrl: result[0].shortUrl {
}, () => { shareUrl: result[0].shortUrl,
const qrcodeWrapDom = document.querySelector('#qrcodeWrap'); },
() => {
const qrcodeWrapDom = document.querySelector('#qrcodeWrap')
const qrcodeNode = new qrcode({ const qrcodeNode = new qrcode({
text: this.state.shareUrl, text: this.state.shareUrl,
size: 98, size: 98,
}) })
qrcodeWrapDom.appendChild(qrcodeNode); qrcodeWrapDom.appendChild(qrcodeNode)
}); }
)
}) })
} }
componentWillUnmount() { componentWillUnmount() {
// 页面销毁之前清空定时器 // 页面销毁之前清空定时器
clearTimeout(this.timer); clearTimeout(this.timer)
} }
// 下载海报 // 下载海报
handleDownloadPoster = () => { handleDownloadPoster = () => {
this.setState({ this.setState(
showImg:true, {
time:new Date().valueOf() showImg: true,
},()=>{ time: new Date().valueOf(),
},
() => {
this.setState({ time: new Date().valueOf() }, () => { this.setState({ time: new Date().valueOf() }, () => {
setTimeout(() => { setTimeout(() => {
let node = document.getElementById('poster'); let node = document.getElementById('poster')
domtoimage.toPng(node) domtoimage.toPng(node).then((imgData) => {
.then((imgData) => {
console.log(imgData) console.log(imgData)
const download = document.createElement('a'); const download = document.createElement('a')
const { courseName } = this.props.data; const { courseName } = this.props.data
$(download).attr('href', imgData).attr('download', `${courseName}.png`).get(0).click(); $(download).attr('href', imgData).attr('download', `${courseName}.png`).get(0).click()
// this.props.close() // this.props.close()
}) })
}, 1000) }, 1000)
}) })
}) }
)
} }
// 复制分享链接 // 复制分享链接
handleCopy = () => { handleCopy = () => {
const textContent = document.getElementById('shareUrl').innerText; const textContent = document.getElementById('shareUrl').innerText
window.copyText(textContent); window.copyText(textContent)
message.success('复制成功!'); message.success('复制成功!')
} }
render() { render() {
const { needStr, data, type, title } = this.props; const { courseDivision, data, type, title } = this.props
const { courseName, coverUrl = DEFAULT_COVER, scheduleVideoUrl } = data; const { courseName, coverUrl = DEFAULT_COVER, scheduleVideoUrl } = data
const { shareUrl ,imgData,showImg,time} = this.state; const { shareUrl, showImg, time } = this.state
// 判断是否是默认图, 默认图不需要在URL后面增加字符串 // 判断是否是默认图, 默认图不需要在URL后面增加字符串
const isDefaultCover = coverUrl === DEFAULT_COVER; const isDefaultCover = coverUrl === DEFAULT_COVER
let coverImgSrc = coverUrl; let coverImgSrc = coverUrl
if(type === 'videoClass'){ if (type === 'videoClass') {
if((!coverUrl || isDefaultCover) && title !== '图文课' && title != '线下课'){ if ((!coverUrl || isDefaultCover) && title !== '图文课' && title !== '线下课') {
if (courseDivision === 'external') {
coverImgSrc = 'https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png'
} else {
coverImgSrc = `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast&anystring=anystring` coverImgSrc = `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast&anystring=anystring`
} }
}else{ }
data.courseMediaVOS.map((item,index)=>{ } else {
if( item.contentType === "COVER"){ data.courseMediaVOS.map((item, index) => {
coverImgSrc = item.mediaUrl; if (item.contentType === 'COVER') {
coverImgSrc = item.mediaUrl
} }
}) })
} }
return ( return (
<Modal <Modal
title={`分享${title}`} title={`分享${title}`}
width={680} width={680}
visible={true} visible={true}
footer={null} footer={null}
maskClosable={false} maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>} closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
className="share-live-modal" className='share-live-modal'
onCancel={this.props.close} onCancel={this.props.close}>
> <div className='left'>
<div className="left"> <div id='poster'>
<div id="poster"> <div className='store-name'>
<div className="store-name"> <span className='text'>{User.getStoreName()}</span>
<span className="text">{storeName}</span>
</div> </div>
<div className="course-name-title">{type === 'videoClass' ? `${courseName}开课啦`: `邀请你观看直播:`}</div> <div className='course-name-title'>{type === 'videoClass' ? `${courseName}开课啦` : `邀请你观看直播:`}</div>
{ {type === 'liveClass' && <div class='live-couse-name'>{courseName}</div>}
type === "liveClass" && <Choose>
<div class="live-couse-name">{courseName}</div> <When condition={showImg}>
} <img crossOrigin='*' src={coverImgSrc + `?=${time}`} className='course-cover' alt='' />
{ </When>
showImg ? <img <Otherwise>
crossOrigin='*' <img src={coverImgSrc + `?=${time}`} className='course-cover' alt='' />
src={coverImgSrc+`?=${time}`} </Otherwise>
className="course-cover" </Choose>
/>: <img
src={coverImgSrc+`?=${time}`} <div className='qrcode-wrap'>
className="course-cover" <div className='qrcode-wrap__left'>
/> <div className='text'>长按识别二维码进入观看</div>
} <img className='finger' src='https://image.xiaomaiketang.com/xm/thpkWDwJsC.png' alt='' />
<div className="qrcode-wrap">
<div className="qrcode-wrap__left">
<div className="text">长按识别二维码进入观看</div>
<img className="finger" src="https://image.xiaomaiketang.com/xm/thpkWDwJsC.png"/>
</div> </div>
<div className="qrcode-wrap__right" id="qrcodeWrap"> <div className='qrcode-wrap__right' id='qrcodeWrap'></div>
</div> </div>
</div> </div>
</div> </div>
<div className='right'>
<div className='share-poster right__item'>
<div className='title'>① 海报分享</div>
{type === 'liveClass' && <div className='sub-title'>学员可通过微信扫描海报二维码,观看{title}</div>}
{type === 'videoClass' && <div className='sub-title'>学员可通过微信识别二维码,报名观看{title}</div>}
<div className='content' onClick={_.debounce(this.handleDownloadPoster, 1000, true)}>
下载海报
</div> </div>
<div className="right">
<div className="share-poster right__item">
<div className="title">① 海报分享</div>
{ type === "liveClass" &&
<div className="sub-title">学员可通过微信扫描海报二维码,观看{title}</div>
}
{ type === "videoClass" &&
<div className="sub-title">学员可通过微信识别二维码,报名观看{title}</div>
}
<div className="content" onClick={_.debounce(this.handleDownloadPoster, 1000, true)}>下载海报</div>
</div> </div>
<div className="share-url right__item"> <div className='share-url right__item'>
<div className="title">② 链接分享</div> <div className='title'>② 链接分享</div>
{ type === "liveClass" && {type === 'liveClass' && <div className='sub-title'>学员可通过微信打开以下链接,观看{title}</div>}
<div className="sub-title">学员可通过微信打开以下链接,观看{title}</div> {type === 'videoClass' && <div className='sub-title'>学员可通过打开链接,报名观看{title}</div>}
} <div className='content url-content'>
{ type === "videoClass" && <div className='share-url' id='shareUrl'>
<div className="sub-title">学员可通过打开链接,报名观看{title}</div> {shareUrl}
} </div>
<div className="content url-content"> <Button type='primary' onClick={this.handleCopy}>
<div className="share-url" id="shareUrl">{shareUrl}</div> 复制
<Button type="primary" onClick={this.handleCopy}>复制</Button> </Button>
</div> </div>
</div> </div>
</div> </div>
...@@ -227,4 +187,4 @@ class ShareLiveModal extends React.Component { ...@@ -227,4 +187,4 @@ class ShareLiveModal extends React.Component {
} }
} }
export default ShareLiveModal; export default ShareLiveModal
import React from 'react'; import React from 'react'
import { Row, Input, Select,Tooltip } from 'antd'; import { Row, Input, Select, Tooltip } from 'antd'
import RangePicker from "@/modules/common/DateRangePicker"; import RangePicker from '@/modules/common/DateRangePicker'
import './OfflineCourseFilter.less'; import './OfflineCourseFilter.less'
import moment from 'moment'; import moment from 'moment'
import StoreService from "@/domains/store-domain/storeService"; import StoreService from '@/domains/store-domain/storeService'
const { Search } = Input; const { Search } = Input
const { Option } = Select; const { Option } = Select
const DEFAULT_QUERY = { const DEFAULT_QUERY = {
courseName: null, // 课程名称 courseName: null, // 课程名称
operatorId: null, // 创建人 operatorId: null, // 创建人
beginTime: null, // 开始日期 beginTime: null, // 开始日期
endTime: null, // 结束日期 endTime: null, // 结束日期
shelfState:null, shelfState: null,
} }
const defaultTeacherQuery = { const defaultTeacherQuery = {
size: 10, size: 10,
current: 1, current: 1,
nickName:null nickName: null,
} }
class OfflineCourseFilter extends React.Component { class OfflineCourseFilter extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
query: { ...DEFAULT_QUERY }, // 使用扩展运算符,避免浅拷贝 query: { ...DEFAULT_QUERY }, // 使用扩展运算符,避免浅拷贝
teacherQuery: defaultTeacherQuery, teacherQuery: defaultTeacherQuery,
teacherList:[], teacherList: [],
expandFilter:false expandFilter: false,
} }
} }
componentDidMount() { componentDidMount() {
this.getTeacherList(); this.getTeacherList()
} }
getTeacherList(current = 1, selectList){ getTeacherList(current = 1, selectList) {
const { teacherQuery,teacherList} = this.state; const { teacherQuery, teacherList } = this.state
const _query = { const _query = {
...teacherQuery, ...teacherQuery,
current, current,
size:10 size: 10,
}; }
StoreService.getStoreUserBasicPage( _query).then((res) => { StoreService.getStoreUserBasicPage(_query).then((res) => {
const { result = {} } = res; const { result = {} } = res
const { records = [], total = 0, hasNext } = result; const { records = [], total = 0, 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,
}) })
}); })
} }
// 滑动加载更多讲师列表 // 滑动加载更多讲师列表
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)
}
)
} }
} }
// 改变搜索条件 // 改变搜索条件
handleChangeQuery = (field, value) => { handleChangeQuery = (field, value) => {
this.setState({ this.setState(
{
query: { query: {
...this.state.query, ...this.state.query,
[field]: value, [field]: value,
current: 1, current: 1,
} },
}, () => { },
if (field === 'courseName') return; () => {
if (field === 'courseName') return
this.props.onChange(this.state.query) this.props.onChange(this.state.query)
}); }
)
} }
handleChangeDates = (dates) => { handleChangeDates = (dates) => {
const query = _.clone(this.state.query); const query = _.clone(this.state.query)
if (_.isEmpty(dates)) { if (_.isEmpty(dates)) {
delete query.beginTime; delete query.beginTime
delete query.endTime; delete query.endTime
} else { } else {
query.beginTime = dates[0].valueOf(); query.beginTime = dates[0]?.startOf('day').valueOf()
query.endTime = dates[1].valueOf(); query.endTime = dates[0]?.endOf('day').valueOf()
} }
this.setState({ this.setState(
query:{ {
query: {
...query, ...query,
current: 1, current: 1,
},
},
() => {
this.props.onChange(this.state.query)
} }
}, () => { )
this.props.onChange(this.state.query);
})
} }
// 重置搜索条件 // 重置搜索条件
handleReset = () => { handleReset = () => {
this.setState({ this.setState(
{
query: DEFAULT_QUERY, query: DEFAULT_QUERY,
}, () => { },
this.props.onChange(this.state.query); () => {
}) this.props.onChange(this.state.query)
}
)
} }
render() { render() {
const { const {
query: { query: { courseName, courseState, shelfState },
courseName,
courseState,
shelfState,
},
expandFilter, expandFilter,
} = this.state; } = this.state
return ( return (
<div className="video-course-filter"> <div className='video-course-filter'>
<Row type="flex" justify="space-between" align="top"> <Row type='flex' justify='space-between' align='top'>
<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={courseName} value={courseName}
placeholder="搜索线下课名称" placeholder='搜索线下课名称'
onChange={(e) => { this.handleChangeQuery('courseName', e.target.value)}} onChange={(e) => {
onSearch={ () => { this.props.onChange(this.state.query) } } this.handleChangeQuery('courseName', e.target.value)
style={{ width: "calc(100% - 84px)" }} }}
enterButton={<span className="icon iconfont" style={{fontSize: '16px'}}>&#xe832;</span>} onSearch={() => {
this.props.onChange(this.state.query)
}}
style={{ width: 'calc(100% - 84px)' }}
enterButton={
<span className='icon iconfont' style={{ fontSize: '16px' }}>
&#xe832;
</span>
}
/> />
</div> </div>
<div className="search-condition__item"> <div className='search-condition__item'>
<span className="shelf-status">学院展示:</span> <span className='shelf-status'>学院展示:</span>
<Select <Select
style={{ width: "calc(100% - 84px)" }} style={{ width: 'calc(100% - 84px)' }}
placeholder="请选择" placeholder='请选择'
allowClear={true} allowClear={true}
value={shelfState} value={shelfState}
onChange={(value) => { this.handleChangeQuery('shelfState', value) }} onChange={(value) => {
suffixIcon={<span className="icon iconfont" style={{fontSize:'12px',color:'#BFBFBF'}}>&#xe835;</span>} this.handleChangeQuery('shelfState', value)
> }}
<Option value="YES">开启</Option> suffixIcon={
<Option value="NO">关闭</Option> <span className='icon iconfont' style={{ fontSize: '12px', color: '#BFBFBF' }}>
&#xe835;
</span>
}>
<Option value='YES'>开启</Option>
<Option value='NO'>关闭</Option>
</Select> </Select>
</div> </div>
</div> </div>
<div className="reset-fold-area"> <div className='reset-fold-area'>
<Tooltip title="清空筛选"><span className="resetBtn iconfont icon" onClick={this.handleReset}>&#xe61b; </span></Tooltip> <Tooltip title='清空筛选'>
<span className='resetBtn iconfont icon' onClick={this.handleReset}>
&#xe61b;{' '}
</span>
</Tooltip>
</div> </div>
</Row> </Row>
</div> </div>
...@@ -161,4 +187,4 @@ class OfflineCourseFilter extends React.Component { ...@@ -161,4 +187,4 @@ class OfflineCourseFilter extends React.Component {
} }
} }
export default OfflineCourseFilter; export default OfflineCourseFilter
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-05 10:07:47 * @Date: 2020-08-05 10:07:47
* @LastEditors: zhangleyuan * @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-10 10:15:46 * @LastEditTime: 2021-05-24 21:04:03
* @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 { ImgCutModalNew } from '@/components'; import { ImgCutModalNew } from '@/components'
import ShowTips from "@/components/ShowTips"; import ShowTips from '@/components/ShowTips'
import Breadcrumbs from "@/components/Breadcrumbs"; import Breadcrumbs from '@/components/Breadcrumbs'
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 $ from 'jquery'; import $ from 'jquery'
// import PhotoClip from 'photoclip'; // import PhotoClip from 'photoclip';
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' }
//添加课程时课程默认的一些值 //添加课程时课程默认的一些值
const defaultShelfState = 'YES'; const defaultShelfState = 'YES'
const defaultScheduleMedia = [{ const defaultScheduleMedia = [
contentType:'INTRO', {
contentType: 'INTRO',
mediaType: 'TEXT', mediaType: 'TEXT',
mediaContent: '', mediaContent: '',
key: EDIT_BOX_KEY key: EDIT_BOX_KEY
}] }
]
const whetherVisitorsJoin = 'NO' const whetherVisitorsJoin = 'NO'
let cutFlag = false; let cutFlag = false
class AddVideoCourse extends React.Component { class AddVideoCourse extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
const id = getParameterByName("id"); const id = getParameterByName('id')
const pageType = getParameterByName("type"); const pageType = getParameterByName('type')
this.state = { this.state = {
id, // 视频课ID,编辑的时候从URL上带过来 id, // 视频课ID,编辑的时候从URL上带过来
...@@ -61,57 +62,60 @@ class AddVideoCourse extends React.Component { ...@@ -61,57 +62,60 @@ class AddVideoCourse extends React.Component {
coverId: null, // 视频封面的recourceId coverId: null, // 视频封面的recourceId
coverUrl: null, // 视频课封面 coverUrl: null, // 视频课封面
studentList: [], // 上课学员列表 studentList: [], // 上课学员列表
shelfState:'YES', //是否开启学院展示 shelfState: 'YES', //是否开启学院展示
scheduleMedia: [{ // 视频课媒体资源 scheduleMedia: [
contentType:"INTRO", {
// 视频课媒体资源
contentType: 'INTRO',
mediaType: 'TEXT', mediaType: 'TEXT',
mediaContent: '', mediaContent: '',
key: EDIT_BOX_KEY key: EDIT_BOX_KEY
}], }
],
diskList: [], // 机构可见磁盘目录 diskList: [], // 机构可见磁盘目录
selectedFileList: [], // 已经从资料云盘中勾选的文件 selectedFileList: [], // 已经从资料云盘中勾选的文件
showCutModal: false, // 是否显示截图弹窗 showCutModal: false, // 是否显示截图弹窗
showSelectFileModal: false, showSelectFileModal: false,
studentModal: false, studentModal: false,
categoryName:null, //分类名称 categoryName: null, //分类名称
courseCatalogList:[], //分类列表 courseCatalogList: [], //分类列表
categoryId:null, //分类的Id值 categoryId: null, //分类的Id值
whetherVisitorsJoin:'NO', // 是否允许游客加入 whetherVisitorsJoin: 'NO', // 是否允许游客加入
showSelectCoverModal:false, showSelectCoverModal: false,
cutImageBlob: null, cutImageBlob: null,
introduce: '', introduce: ''
} }
} }
componentWillMount() { componentWillMount() {
const { id, pageType } = this.state; const { id, pageType } = this.state
this.getCourseCatalogList(); this.getCourseCatalogList()
if (pageType === 'edit') { if (pageType === 'edit') {
this.handleFetchScheudleDetail(id); this.handleFetchScheudleDetail(id)
} }
} }
//获取分类列表 //获取分类列表
getCourseCatalogList = ()=>{ getCourseCatalogList = () => {
StoreService.getCourseCatalogList({current:1,size:1000}).then((res) => { StoreService.getCourseCatalogList({ current: 1, size: 1000 }).then((res) => {
this.setState({ this.setState({
courseCatalogList:res.result.records courseCatalogList: res.result.records
})
}) })
});
} }
catalogChange= (value) => { catalogChange = (value) => {
const changeValueLength = value.length; const changeValueLength = value.length
switch (changeValueLength){ switch (changeValueLength) {
case 1: case 1:
this.setState({categoryId:value[0]}); this.setState({ categoryId: value[0] })
break; break
case 2: case 2:
this.setState({categoryId:value[1]}); this.setState({ categoryId: value[1] })
break; break
default: default:
this.setState({categoryId:null}); this.setState({ categoryId: null })
break; break
} }
} }
// 获取视频课详情 // 获取视频课详情
...@@ -119,54 +123,46 @@ class AddVideoCourse extends React.Component { ...@@ -119,54 +123,46 @@ class AddVideoCourse extends React.Component {
CourseService.videoScheduleDetail({ CourseService.videoScheduleDetail({
courseId courseId
}).then((res) => { }).then((res) => {
const { result = {} } = res || {}; const { result = {} } = res || {}
const { const { courseName, shelfState, whetherVisitorsJoin, courseMediaVOS, categoryOneName, categoryTwoName, categoryId } = result
courseName, let coverId
shelfState, let coverUrl
whetherVisitorsJoin, let videoType
courseMediaVOS, let videoDuration
categoryOneName, let videoName
categoryTwoName, let scheduleMedia = []
categoryId let scheduleVideoId
} = result; let scheduleVideoUrl
let coverId; let hasIntro
let coverUrl;
let videoType;
let videoDuration;
let videoName;
let scheduleMedia = [];
let scheduleVideoId;
let scheduleVideoUrl;
let hasIntro;
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':
videoDuration = item.videoDuration; videoDuration = item.videoDuration
videoName = item.mediaName; videoName = item.mediaName
scheduleVideoId = item.mediaContent; scheduleVideoId = item.mediaContent
scheduleVideoUrl = item.mediaUrl; scheduleVideoUrl = item.mediaUrl
videoType = item.mediaType; videoType = item.mediaType
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
}) })
let categoryName; let categoryName
if( categoryTwoName ){ if (categoryTwoName) {
categoryName = `${categoryOneName}-${categoryTwoName}`; categoryName = `${categoryOneName}-${categoryTwoName}`
}else{ } else {
categoryName = `${categoryOneName}`; categoryName = `${categoryOneName}`
} }
this.setState({ this.setState({
loadintroduce: !hasIntro, loadintroduce: !hasIntro,
...@@ -183,7 +179,7 @@ class AddVideoCourse extends React.Component { ...@@ -183,7 +179,7 @@ class AddVideoCourse extends React.Component {
whetherVisitorsJoin, whetherVisitorsJoin,
categoryName, categoryName,
categoryId categoryId
}); })
}) })
} }
...@@ -192,43 +188,42 @@ class AddVideoCourse extends React.Component { ...@@ -192,43 +188,42 @@ class AddVideoCourse extends React.Component {
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, scheduleMedia, scheduleVideoId, categoryId, shelfState, whetherVisitorsJoin } = this.state
coverId, if (
videoName, videoName ||
videoDuration, videoDuration ||
courseName, scheduleVideoId ||
scheduleMedia, !_.isEqual(scheduleMedia, defaultScheduleMedia) ||
scheduleVideoId, categoryId ||
categoryId, courseName ||
shelfState, coverId ||
whetherVisitorsJoin shelfState !== defaultShelfState ||
} = this.state; whetherVisitorsJoin !== whetherVisitorsJoin
if(videoName || videoDuration || scheduleVideoId || !_.isEqual(scheduleMedia, defaultScheduleMedia) || categoryId || courseName || coverId || shelfState !== defaultShelfState || 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: `/video-course`, pathname: `/video-course`
}); })
} }
}); })
}else{ } else {
window.RCHistory.push({ window.RCHistory.push({
pathname: `/video-course`, pathname: `/video-course`
}); })
} }
} }
...@@ -237,51 +232,44 @@ class AddVideoCourse extends React.Component { ...@@ -237,51 +232,44 @@ class AddVideoCourse extends React.Component {
this.setState({ this.setState({
[field]: value, [field]: value,
coverUrl: coverUrl ? coverUrl : this.state.coverUrl coverUrl: coverUrl ? coverUrl : this.state.coverUrl
}); })
} }
// 显示选择学员弹窗 // 显示选择学员弹窗
handleShowSelectStuModal = () => { handleShowSelectStuModal = () => {
this.setState({ studentModal : true }); this.setState({ studentModal: true })
const { studentList, selectedStuList } = this.state; const { studentList, selectedStuList } = this.state
const studentModal = ( const studentModal = (
<SelectStudent <SelectStudent
showTabs={true} showTabs={true}
type="videoCourse" type='videoCourse'
onSelect={this.handleSelectStudent} onSelect={this.handleSelectStudent}
after={true} //表明是不是上课后的状态 after={true} //表明是不是上课后的状态
studentList={studentList} studentList={studentList}
close={() => { close={() => {
this.setState({ this.setState({
studentModal: null, studentModal: null
}); })
}} }}
/> />
) )
this.setState({ studentModal }); this.setState({ studentModal })
} }
handleSelectStudent = (studentIds) => { handleSelectStudent = (studentIds) => {
let studentList = []; let studentList = []
_.each(studentIds, (item) => { _.each(studentIds, (item) => {
studentList.push({ studentId: item }); studentList.push({ studentId: item })
}); })
// this.setState({ studentModal: null }); // this.setState({ studentModal: null });
this.setState({ studentList }); this.setState({ studentList })
this.setState({ studentModal : false }); this.setState({ studentModal: false })
} }
// 显示预览弹窗 // 显示预览弹窗
handleShowPreviewModal = () => { handleShowPreviewModal = () => {
const { const { coverUrl, scheduleVideoUrl, courseName, scheduleMedia, videoDuration, introduce } = this.state
coverUrl,
scheduleVideoUrl,
courseName,
scheduleMedia,
videoDuration,
introduce,
} = this.state;
const courseBasinInfo = { const courseBasinInfo = {
coverUrl, coverUrl,
...@@ -291,12 +279,12 @@ class AddVideoCourse extends React.Component { ...@@ -291,12 +279,12 @@ class AddVideoCourse extends React.Component {
} }
const courseIntroInfo = { const courseIntroInfo = {
liveCourseMediaRequests: scheduleMedia, liveCourseMediaRequests: scheduleMedia,
introduce, introduce
} }
const previewCourseModal = ( const previewCourseModal = (
<PreviewCourseModal <PreviewCourseModal
type="videoCourse" type='videoCourse'
courseBasicInfo={courseBasinInfo} courseBasicInfo={courseBasinInfo}
courseIntroInfo={courseIntroInfo} courseIntroInfo={courseIntroInfo}
close={() => { close={() => {
...@@ -305,9 +293,9 @@ class AddVideoCourse extends React.Component { ...@@ -305,9 +293,9 @@ class AddVideoCourse extends React.Component {
}) })
}} }}
/> />
); )
this.setState({ previewCourseModal }); this.setState({ previewCourseModal })
} }
// 选择视频 // 选择视频
...@@ -315,25 +303,25 @@ class AddVideoCourse extends React.Component { ...@@ -315,25 +303,25 @@ class AddVideoCourse extends React.Component {
this.setState({ this.setState({
showSelectFileModal: false showSelectFileModal: false
}) })
const { ossUrl, resourceId, folderName, folderFormat, folderSize } = file; const { ossUrl, resourceId, folderName, folderFormat, folderSize } = file
const videoDom = document.createElement('video'); const videoDom = document.createElement('video')
videoDom.src = ossUrl; videoDom.src = ossUrl
videoDom.onloadedmetadata = () => { videoDom.onloadedmetadata = () => {
this.setState({ this.setState({
size: folderSize, size: folderSize,
videoName: folderName, videoName: folderName,
videoType: folderFormat, videoType: folderFormat,
scheduleVideoUrl: ossUrl, scheduleVideoUrl: ossUrl,
scheduleVideoId : resourceId, scheduleVideoId: resourceId,
videoDuration: videoDom.duration videoDuration: videoDom.duration
}); })
} }
} }
// 保存 // 保存
handleSubmit = () => { handleSubmit = () => {
const { instId, adminId } = window.currentUserInstInfo; const { instId, adminId } = window.currentUserInstInfo
const { const {
id, id,
...@@ -352,113 +340,113 @@ class AddVideoCourse extends React.Component { ...@@ -352,113 +340,113 @@ class AddVideoCourse extends React.Component {
categoryId, categoryId,
shelfState, shelfState,
whetherVisitorsJoin, whetherVisitorsJoin,
introduce, introduce
} = this.state; } = this.state
const commonParams = { const commonParams = {
videoName, videoName,
videoDuration, videoDuration,
courseMediaId: scheduleVideoId, courseMediaId: scheduleVideoId,
scheduleMedia: scheduleMedia.filter(item => !!item.mediaContent), scheduleMedia: scheduleMedia.filter((item) => !!item.mediaContent),
categoryId, categoryId,
courseName, courseName,
coverId, coverId,
operatorId:User.getStoreUserId(), operatorId: User.getStoreUserId(),
storeId:User.getStoreId(), storeId: User.getStoreId(),
shelfState, shelfState,
whetherVisitorsJoin, whetherVisitorsJoin,
courseType: 'VOICE', courseType: 'VOICE'
}; }
// 校验必填字段:课程名称, 课程视频 // 校验必填字段:课程名称, 课程视频
this.handleValidate(courseName, scheduleVideoId,categoryId, scheduleMedia).then((res) => { this.handleValidate(courseName, scheduleVideoId, categoryId, scheduleMedia).then((res) => {
if (!res) return; if (!res) return
Upload.uploadTextToOSS(introduce, `${randomString()}.txt`, (introduceId) => { Upload.uploadTextToOSS(introduce, `${randomString()}.txt`, (introduceId) => {
this.submitRemote({ id, pageType, commonParams: { ...commonParams, introduceId } }) this.submitRemote({ id, pageType, commonParams: { ...commonParams, introduceId } })
}); })
}); })
} }
submitRemote = ({ id, pageType, commonParams }) => { submitRemote = ({ id, pageType, commonParams }) => {
if (pageType === 'add') { if (pageType === 'add') {
Service.Hades('public/hades/createMediaCourse', commonParams).then((res) => { Service.Hades('public/hades/createMediaCourse', commonParams).then((res) => {
if (!res) return; if (!res) return
message.success("新建成功"); message.success('新建成功')
window.RCHistory.push({ window.RCHistory.push({
pathname: `/video-course`, pathname: `/video-course`
}); })
}) })
} else { } else {
const editParams = { const editParams = {
courseId:id, courseId: id,
...commonParams, ...commonParams
} }
Service.Hades('public/hades/editMediaCourse', editParams).then((res) => { Service.Hades('public/hades/editMediaCourse', editParams).then((res) => {
if (!res) return; if (!res) return
message.success("保存成功"); message.success('保存成功')
window.RCHistory.push({ window.RCHistory.push({
pathname: `/video-course`, pathname: `/video-course`
}); })
}); })
} }
} }
handleValidate = (courseName, scheduleVideoId,categoryId,scheduleMedia) => { handleValidate = (courseName, scheduleVideoId, categoryId, scheduleMedia) => {
return new Promise((resolve) => { return new Promise((resolve) => {
if (!courseName) { if (!courseName) {
message.warning('请输入课程名称'); message.warning('请输入课程名称')
resolve(false); resolve(false)
return false return false
} }
if (!scheduleVideoId) { if (!scheduleVideoId) {
message.warning('请上传视频'); message.warning('请上传视频')
resolve(false); resolve(false)
return false return false
} }
if(!categoryId){ if (!categoryId) {
message.warning('请选择课程分类'); message.warning('请选择课程分类')
resolve(false); resolve(false)
return false return false
} }
const textMedia = scheduleMedia.filter((item) => item.mediaType === 'TEXT'); const textMedia = scheduleMedia.filter((item) => item.mediaType === 'TEXT')
for (let i = 0, len = textMedia.length; i < len; i++) { for (let i = 0, len = textMedia.length; i < len; i++) {
if (textMedia[i].mediaContentLength && textMedia[i].mediaContentLength.length > 1000) { if (textMedia[i].mediaContentLength && textMedia[i].mediaContentLength.length > 1000) {
message.warning(`第${i+1}个文字简介的字数超过了1000个字`); message.warning(`第${i + 1}个文字简介的字数超过了1000个字`)
resolve(false); resolve(false)
return false return false
} }
} }
resolve(true); resolve(true)
}); })
} }
handleSelectCover = (file)=> { handleSelectCover = (file) => {
this.uploadImage(file); this.uploadImage(file)
} }
//上传图片 //上传图片
uploadImage = (imageFile) => { uploadImage = (imageFile) => {
const { folderName } = imageFile; const { folderName } = imageFile
const fileName = window.random_string(16) + folderName.slice(folderName.lastIndexOf(".")); const fileName = window.random_string(16) + folderName.slice(folderName.lastIndexOf('.'))
const self = this; const self = this
this.setState( this.setState(
{ {
visible: true, visible: true
}, },
() => { () => {
setTimeout(() => { setTimeout(() => {
const okBtnDom = document.querySelector("#headPicModal"); const okBtnDom = document.querySelector('#headPicModal')
const options = { const options = {
size: [500, 282], size: [500, 282],
ok: okBtnDom, ok: okBtnDom,
maxZoom: 3, maxZoom: 3,
style: { style: {
jpgFillColor: "transparent", jpgFillColor: 'transparent'
}, },
done: function (dataUrl) { done: function (dataUrl) {
clearTimeout(self.timer); clearTimeout(self.timer)
self.timer = setTimeout(() => { self.timer = setTimeout(() => {
if ((self.state.rotate != this.rotate()) || (self.state.scale != this.scale())) { if (self.state.rotate != this.rotate() || self.state.scale != this.scale()) {
const _dataUrl = this.clip() const _dataUrl = this.clip()
const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl); const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl)
self.setState({ self.setState({
cutImageBlob, cutImageBlob,
dataUrl: _dataUrl, dataUrl: _dataUrl,
...@@ -466,20 +454,19 @@ class AddVideoCourse extends React.Component { ...@@ -466,20 +454,19 @@ class AddVideoCourse extends React.Component {
scale: this.scale() scale: this.scale()
}) })
} }
}, 500) }, 500)
const cutImageBlob = self.convertBase64UrlToBlob(dataUrl); const cutImageBlob = self.convertBase64UrlToBlob(dataUrl)
self.setState({ self.setState({
cutImageBlob, cutImageBlob,
dataUrl dataUrl
}) })
setTimeout(() => { setTimeout(() => {
cutFlag = false; cutFlag = false
}, 2000); }, 2000)
}, },
fail: (failInfo) => { fail: (failInfo) => {
message.error("图片上传失败了,请重新上传"); message.error('图片上传失败了,请重新上传')
}, },
loadComplete: function (img) { loadComplete: function (img) {
setTimeout(() => { setTimeout(() => {
...@@ -489,152 +476,199 @@ class AddVideoCourse extends React.Component { ...@@ -489,152 +476,199 @@ class AddVideoCourse extends React.Component {
hasImgReady: true hasImgReady: true
}) })
}, 100) }, 100)
}
}, }
};
const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}` const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}`
if (!this.state.photoclip) { if (!this.state.photoclip) {
const _photoclip = new PhotoClip("#headPicModal", options); const _photoclip = new PhotoClip('#headPicModal', options)
_photoclip.load(imgUrl); _photoclip.load(imgUrl)
this.setState({ this.setState({
photoclip: _photoclip, photoclip: _photoclip
}); })
} else { } else {
this.state.photoclip.clear(); this.state.photoclip.clear()
this.state.photoclip.load(imgUrl); this.state.photoclip.load(imgUrl)
} }
}, 200)
}, 200); }
)
} }
);
};
//获取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,
coverId: signInfo.resourceId,
visible: false visible: false
},()=>this.updateCover()) },
() => this.updateCover()
}); )
}; })
updateCover = () =>{ }
const {coverClicpPath,coverId} = this.state updateCover = () => {
const { coverClicpPath, coverId } = this.state
this.setState({ this.setState({
showSelectCoverModal: false, showSelectCoverModal: false,
coverUrl:coverClicpPath, coverUrl: coverClicpPath,
coverId:coverId coverId: coverId
}) })
} }
// base64转换成blob // base64转换成blob
convertBase64UrlToBlob = (urlData) => { convertBase64UrlToBlob = (urlData) => {
const bytes = window.atob(urlData.split(",")[1]); const bytes = window.atob(urlData.split(',')[1])
const ab = new ArrayBuffer(bytes.length); const ab = new ArrayBuffer(bytes.length)
const ia = new Uint8Array(ab); const ia = new Uint8Array(ab)
for (let i = 0; i < bytes.length; i++) { for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i); ia[i] = bytes.charCodeAt(i)
}
return new Blob([ab], { type: 'image/png' })
} }
return new Blob([ab], { type: "image/png" });
};
render() { render() {
const { const {
pageType,courseName, scheduleVideoId, coverId, pageType,
coverUrl, scheduleVideoUrl, studentList, scheduleMedia, courseName,
showCutModal, showSelectFileModal, diskList, scheduleVideoId,
imageFile, joinType, videoName, videoType,shelfState, coverId,
categoryName,courseCatalogList,whetherVisitorsJoin, coverUrl,
visible,showSelectCoverModal,hasImgReady,cutImageBlob, scheduleVideoUrl,
studentList,
scheduleMedia,
showCutModal,
showSelectFileModal,
diskList,
imageFile,
joinType,
videoName,
videoType,
shelfState,
categoryName,
courseCatalogList,
whetherVisitorsJoin,
visible,
showSelectCoverModal,
hasImgReady,
cutImageBlob,
introduce, introduce,
loadintroduce, loadintroduce,
id, id
} = this.state; } = this.state
// 已选择的上课学员数量 // 已选择的上课学员数量
const hasSelectedStu = studentList.length; const hasSelectedStu = studentList.length
const courseWareIcon = FileVerifyMap[videoType] ? FileTypeIcon[FileVerifyMap[videoType].type] : FileTypeIcon[videoType]; const courseWareIcon = FileVerifyMap[videoType] ? FileTypeIcon[FileVerifyMap[videoType].type] : FileTypeIcon[videoType]
return ( return (
<div className="page add-video-course-page"> <div className='page add-video-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="course-name required"> <div className='course-name required'>
<span className="label">课程名称:</span> <span className='label'>课程名称:</span>
<Input <Input
value={courseName} value={courseName}
placeholder="请输入视频课的名称(40字以内)" placeholder='请输入视频课的名称(40字以内)'
maxLength={40} maxLength={40}
style={{ width: 240 }} style={{ width: 240 }}
onChange={(e) => { this.handleChangeForm('courseName', e.target.value)}} onChange={(e) => {
this.handleChangeForm('courseName', e.target.value)
}}
/> />
</div> </div>
<div className="upload-video mt16"> <div className='upload-video mt16'>
<div className="content flex"> <div className='content flex'>
<span className="label required">视频上传:</span> <span className='label required'>视频上传:</span>
<div className="value"> <div className='value'>
{ {scheduleVideoId ? (
scheduleVideoId ? <div className='course-ware'>
<div className="course-ware"> <img className='course-ware__img' src={courseWareIcon} />
<img className="course-ware__img" src={courseWareIcon} /> <span className='course-ware__name'>{videoName}</span>
<span className="course-ware__name">{videoName}</span> </div>
</div> : ) : (
<div className="course-ware--empty">从资料云盘中选择视频</div> <div className='course-ware--empty'>从资料云盘中选择视频</div>
} )}
</div> </div>
</div> </div>
<div className="sub-content"> <div className='sub-content'>
<Button <Button
onClick={() => { onClick={() => {
this.setState({ this.setState({
showSelectFileModal: true showSelectFileModal: true
}) })
}} }}>{`${pageType === 'add' && !scheduleVideoId ? '选择' : '更换'}视频`}</Button>
>{`${(pageType === 'add' && !scheduleVideoId) ? '选择' : '更换'}视频`}</Button>
<span className="tips">视频数量限制1个,大小不超过2G</span> <span className='tips'>视频数量限制1个,大小不超过2G</span>
</div> </div>
</div> </div>
<div className="cover-url flex mt16"> <div className='cover-url flex mt16'>
<div className="label">视频封面:</div> <div className='label'>视频封面:</div>
<div className="cover-url__wrap"> <div className='cover-url__wrap'>
<div className="img-content"> <div className='img-content'>
{/* 如果视频和封面都没有上传的话, 那么就显示缺省, 如果上传了视频, 那么封面图就默认显示视频的第一帧, 如果上传了封面图, 那么就显示上传的封面图 */} {/* 如果视频和封面都没有上传的话, 那么就显示缺省, 如果上传了视频, 那么封面图就默认显示视频的第一帧, 如果上传了封面图, 那么就显示上传的封面图 */}
{ {scheduleVideoUrl || coverUrl ? (
(scheduleVideoUrl || coverUrl )? <img src={coverUrl || `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`} />
<img src={coverUrl || `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`} /> : ) : (
<div className="empty-img">若不上传<br />系统默认将视频首帧作为封面图</div> <div className='empty-img'>
} 若不上传
<br />
系统默认将视频首帧作为封面图
</div>
)}
</div> </div>
<div className="opt-btns"> <div className='opt-btns'>
<Button onClick={() => {this.setState({ showSelectCoverModal:true })}}>{`${(pageType === 'add' && (!scheduleVideoId && !coverUrl)) ? '上传' : '修改'}封面`}</Button> <Button
<div className="tips">建议尺寸1280*720px或16:9。封面图最大5M,支持jpg、jpeg和png。</div> onClick={() => {
this.setState({ showSelectCoverModal: true })
}}>{`${pageType === 'add' && !scheduleVideoId && !coverUrl ? '上传' : '修改'}封面`}</Button>
<div className='tips'>建议尺寸1280*720px或16:9。封面图最大5M,支持jpg、jpeg和png。</div>
</div> </div>
</div> </div>
</div> </div>
<div className="course-catalog required"> <div className='course-catalog required'>
<span className="label">课程分类:</span> <span className='label'>课程分类:</span>
{ (pageType === 'add') && {pageType === 'add' && (
<Cascader defaultValue={[categoryName]} options={courseCatalogList} displayRender={ label => label.join('-')} fieldNames={fieldNames} onChange={this.catalogChange} style={{ width: 240 }} placeholder="请选择课程分类" suffixIcon={<span className="icon iconfont" style={{fontSize:'12px',color:'#BFBFBF'}}>&#xe835;</span>}/> <Cascader
defaultValue={[categoryName]}
options={courseCatalogList}
displayRender={(label) => label.join('-')}
fieldNames={fieldNames}
onChange={this.catalogChange}
style={{ width: 240 }}
placeholder='请选择课程分类'
suffixIcon={
<span className='icon iconfont' style={{ fontSize: '12px', color: '#BFBFBF' }}>
&#xe835;
</span>
} }
{ (pageType === 'edit' && categoryName ) && />
<Cascader defaultValue={[categoryName]} options={courseCatalogList} displayRender={ label => label.join('-')} fieldNames={fieldNames} onChange={this.catalogChange} style={{ width: 240 }} placeholder="请选择课程分类" suffixIcon={<span className="icon iconfont" style={{fontSize:'12px',color:'#BFBFBF'}}>&#xe835;</span>}/> )}
{pageType === 'edit' && categoryName && (
<Cascader
defaultValue={[categoryName]}
options={courseCatalogList}
displayRender={(label) => label.join('-')}
fieldNames={fieldNames}
onChange={this.catalogChange}
style={{ width: 240 }}
placeholder='请选择课程分类'
suffixIcon={
<span className='icon iconfont' style={{ fontSize: '12px', color: '#BFBFBF' }}>
&#xe835;
</span>
} }
/>
)}
</div> </div>
<div className="intro-info mt16"> <div className='intro-info mt16'>
<AddVideoIntro <AddVideoIntro
data={{ data={{
id, id,
...@@ -642,7 +676,7 @@ class AddVideoCourse extends React.Component { ...@@ -642,7 +676,7 @@ class AddVideoCourse extends React.Component {
shelfState, shelfState,
whetherVisitorsJoin, whetherVisitorsJoin,
introduce, introduce,
loadintroduce, loadintroduce
}} }}
onChange={this.handleChangeForm} onChange={this.handleChangeForm}
/> />
...@@ -650,21 +684,23 @@ class AddVideoCourse extends React.Component { ...@@ -650,21 +684,23 @@ class AddVideoCourse extends React.Component {
</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.handleSubmit(), 3000, true)}>保存</Button> <Button type='primary' onClick={_.debounce(() => this.handleSubmit(), 3000, true)}>
保存
</Button>
</div> </div>
{/* 选择备课文件弹窗 */} {/* 选择备课文件弹窗 */}
{ showSelectFileModal && {showSelectFileModal && (
<SelectPrepareFileModal <SelectPrepareFileModal
operateType="select" operateType='select'
selectTypeList={['MP4']} selectTypeList={['MP4']}
accept="video/mp4" accept='video/mp4'
confirm={{ confirm={{
title: '文件过大,无法上传', title: '文件过大,无法上传',
content: '为保障学员的观看体验,上传的视频大小不能超过2G', content: '为保障学员的观看体验,上传的视频大小不能超过2G'
}} }}
tooltip={'格式支持mp4,大小不超过2G'} tooltip={'格式支持mp4,大小不超过2G'}
isOpen={showSelectFileModal} isOpen={showSelectFileModal}
...@@ -675,13 +711,13 @@ class AddVideoCourse extends React.Component { ...@@ -675,13 +711,13 @@ class AddVideoCourse extends React.Component {
}} }}
onSelect={this.handleSelectVideo} onSelect={this.handleSelectVideo}
/> />
} )}
{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}
...@@ -690,70 +726,66 @@ class AddVideoCourse extends React.Component { ...@@ -690,70 +726,66 @@ class AddVideoCourse extends React.Component {
}} }}
onSelect={this.handleSelectCover} onSelect={this.handleSelectCover}
/> />
} )}
<Modal <Modal
title="设置图片" title='设置图片'
width={1080} width={1080}
visible={visible} visible={visible}
maskClosable={false} maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>} closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
onCancel={() => { onCancel={() => {
this.setState({ visible: false }); this.setState({ visible: false })
}} }}
zIndex={10001} zIndex={10001}
footer={[ footer={[
<Button <Button
key="back" key='back'
onClick={() => { onClick={() => {
this.setState({ visible: false }); this.setState({ visible: false })
}} }}>
>
重新上传 重新上传
</Button>, </Button>,
<Button <Button
key="submit" key='submit'
type="primary" type='primary'
disabled={!hasImgReady} disabled={!hasImgReady}
onClick={() => { onClick={() => {
if (!cutFlag) { if (!cutFlag) {
cutFlag = true; cutFlag = true
this.refs.hiddenBtn.click(); this.refs.hiddenBtn.click()
} }
this.getSignature(cutImageBlob); this.getSignature(cutImageBlob)
}} }}>
>
确定 确定
</Button>, </Button>
]} ]}>
> <div className='clip-box'>
<div className="clip-box">
<div <div
id="headPicModal" id='headPicModal'
ref="headPicModal" ref='headPicModal'
style={{ style={{
width: "500px", width: '500px',
height: "430px", height: '430px',
marginBottom: 0, marginBottom: 0
}} }}></div>
></div> <div id='clipBtn' style={{ display: 'none' }} ref='hiddenBtn'></div>
<div id="clipBtn" style={{ display: "none" }} ref="hiddenBtn"></div> <div className='preview-img'>
<div className="preview-img"> <div className='title'>效果预览</div>
<div className="title">效果预览</div> <div id='preview-url-box' style={{ width: 500, height: 282 }}>
<div id="preview-url-box" style={{width:500,height:282}}> <img src={this.state.dataUrl} style={{ width: '100%' }} alt='' />
<img src={this.state.dataUrl} style={{ width: '100%' }} alt="" />
</div> </div>
<div className="tip-box"> <div className='tip-box'>
<div className="tip">温馨提示</div> <div className='tip'>温馨提示</div>
<div className="tip">①预览效果图时可能存在延迟,单击左侧图片刷新即可</div> <div className='tip'>①预览效果图时可能存在延迟,单击左侧图片刷新即可</div>
<div className="tip">②设置图片时双击可旋转图片,滚动可放大或缩小图片</div> <div className='tip'>②设置图片时双击可旋转图片,滚动可放大或缩小图片</div>
</div> </div>
</div> </div>
</div> </div>
</Modal> </Modal>
{ this.state.previewCourseModal } {this.state.previewCourseModal}
</div> </div>
) )
} }
} }
export default AddVideoCourse; export default AddVideoCourse
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-05 10:11:57 * @Date: 2020-08-05 10:11:57
* @LastEditors: zhangleyuan * @LastEditors: fusanqiasng
* @LastEditTime: 2021-02-23 16:35:37 * @LastEditTime: 2021-05-28 20:14:37
* @Description: 视频课-搜索模块 * @Description: 视频课-搜索模块
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
*/ */
import React from 'react'; import React from 'react'
import { Row, Input, Select,Tooltip } from 'antd'; import { Row, Input, Select, Tooltip, TreeSelect } from 'antd'
import RangePicker from "@/modules/common/DateRangePicker"; import RangePicker from '@/modules/common/DateRangePicker'
import _ from 'underscore'
import './VideoCourseFilter.less'
import moment from 'moment'
import StoreService from '@/domains/store-domain/storeService'
import User from '@/common/js/user'
import AidToolService from '@/domains/aid-tool-domain/AidToolService'
import './VideoCourseFilter.less'; const { Search } = Input
import moment from 'moment'; const { Option } = Select
import StoreService from "@/domains/store-domain/storeService";
const { Search } = Input;
const { Option } = Select;
const DEFAULT_QUERY = { const DEFAULT_QUERY = {
courseName: null, // 课程名称 courseName: null, // 课程名称
operatorId: null, // 创建人 operatorId: null, // 创建人
beginTime: null, // 开始日期 beginTime: null, // 开始日期
endTime: null, // 结束日期 endTime: null, // 结束日期
shelfState:null, shelfState: null,
categoryId: null,
} }
const defaultTeacherQuery = { const defaultTeacherQuery = {
size: 10, size: 10,
current: 1, current: 1,
nickName:null nickName: null,
} }
class VideoCourseFilter extends React.Component { class VideoCourseFilter extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
query: { ...DEFAULT_QUERY }, // 使用扩展运算符,避免浅拷贝 query: { ...DEFAULT_QUERY }, // 使用扩展运算符,避免浅拷贝
teacherQuery: defaultTeacherQuery, teacherQuery: defaultTeacherQuery,
teacherList:[], teacherList: [],
expandFilter:false expandFilter: false,
categoryList: [],
}
} }
shouldComponentUpdate(nextProps) {
if (nextProps.currentTabKey !== this.props.currentTabKey) {
this.handleReset()
return false
}
return true
} }
componentDidMount() { componentDidMount() {
this.getTeacherList(); this.getTeacherList()
this.queryCategoryTree()
}
// 查询分类树
queryCategoryTree = (categoryName = '') => {
let query = {
bizType: 'QUESTION',
source: 2,
categoryName,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
count: false,
}
AidToolService.queryExternalCategoryTree(query).then((res) => {
const { categoryList = [] } = res.result
console.log(this.renderTreeNodes(categoryList))
this.setState({
categoryList: this.renderTreeNodes(categoryList),
categoryName,
})
})
} }
getTeacherList(current = 1, selectList){
const { teacherQuery,teacherList} = this.state; renderTreeNodes = (data) => {
let newTreeData = data.map((item) => {
item.title = <span>{item.categoryName}</span>
item.key = item.id
if (item.sonCategoryList) {
item.children = this.renderTreeNodes(item.sonCategoryList)
}
return item
})
return newTreeData
}
getTeacherList(current = 1, selectList) {
const { teacherQuery, teacherList } = this.state
const _query = { const _query = {
...teacherQuery, ...teacherQuery,
current, current,
size:10 size: 10,
}; }
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,
}) })
}); })
} }
// 滑动加载更多讲师列表 // 滑动加载更多讲师列表
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)
}
)
} }
} }
// 改变搜索条件 // 改变搜索条件
handleChangeQuery = (field, value) => { handleChangeQuery = (field, value) => {
this.setState({ this.setState(
{
query: { query: {
...this.state.query, ...this.state.query,
[field]: value, [field]: value,
current: 1, current: 1,
} },
}, () => { },
if (field === 'courseName') return; () => {
if (field === 'courseName') return
this.props.onChange(this.state.query) this.props.onChange(this.state.query)
}); }
)
} }
handleChangeDates = (dates) => { handleChangeDates = (dates) => {
const query = _.clone(this.state.query); console.log(dates)
const query = _.clone(this.state.query)
if (_.isEmpty(dates)) { if (_.isEmpty(dates)) {
delete query.beginTime; delete query.beginTime
delete query.endTime; delete query.endTime
} else { } else {
query.beginTime = dates[0].valueOf(); query.beginTime = dates[0]?.startOf('day').valueOf()
query.endTime = dates[1].valueOf(); query.endTime = dates[1]?.endOf('day').valueOf()
} }
this.setState({ this.setState(
query:{ {
query: {
...query, ...query,
current: 1, current: 1,
},
},
() => {
this.props.onChange(this.state.query)
} }
}, () => { )
this.props.onChange(this.state.query);
})
} }
// 重置搜索条件 // 重置搜索条件
handleReset = () => { handleReset = () => {
this.setState({ this.setState(
{
query: DEFAULT_QUERY, query: DEFAULT_QUERY,
}, () => { },
this.props.onChange(this.state.query); () => {
}) this.props.onChange(this.state.query)
}
)
} }
render() { render() {
const { currentTabKey } = this.props
const { const {
query: { query: { courseName, beginTime, endTime, operatorId, shelfState, categoryId },
courseName,
operator,
beginTime,
endTime,
operatorId,
shelfState
},
expandFilter, expandFilter,
teacherList, teacherList,
teacherQuery teacherQuery,
} = this.state; categoryList,
} = this.state
return ( return (
<div className="video-course-filter"> <div className='video-course-filter'>
<Row type="flex" justify="space-between" align="top"> <Row type='flex' justify='space-between' align='top'>
<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={courseName} value={courseName}
placeholder="搜索视频课名称" placeholder='搜索视频课名称'
onChange={(e) => { this.handleChangeQuery('courseName', e.target.value)}} onChange={(e) => {
onSearch={ () => { this.props.onChange(this.state.query) } } this.handleChangeQuery('courseName', e.target.value)
style={{ width: "calc(100% - 84px)" }} }}
enterButton={<span className="icon iconfont">&#xe832;</span>} onSearch={() => {
this.props.onChange(this.state.query)
}}
style={{ width: 'calc(100% - 84px)' }}
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/> />
</div> </div>
<Choose>
<div className="search-condition__item"> <When condition={currentTabKey === 'internal'}>
<div className='search-condition__item'>
<span>创建人:</span> <span>创建人:</span>
<Select <Select
placeholder="请选择创建人" placeholder='请选择创建人'
style={{width:"calc(100% - 70px)"}} style={{ width: 'calc(100% - 70px)' }}
showSearch showSearch
allowClear allowClear
filterOption={(input, option) => option} filterOption={(input, option) => option}
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>
}
value={operatorId} value={operatorId}
onChange={(value) => { onChange={(value) => {
this.handleChangeQuery('operatorId', value) this.handleChangeQuery('operatorId', value)
}} }}
onSearch={(value) => { onSearch={(value) => {
teacherQuery.nickName = value teacherQuery.nickName = value
this.setState({ this.setState(
teacherQuery {
}, () => { teacherQuery,
},
() => {
this.getTeacherList() this.getTeacherList()
}) }
)
}} }}
onClear ={(value)=>{ onClear={(value) => {
this.setState({ this.setState(
teacherQuery:{ {
teacherQuery: {
size: 10, size: 10,
current: 1, current: 1,
nickName:null nickName: null,
} },
}, () => { },
() => {
this.getTeacherList() this.getTeacherList()
})
}
} }
> )
}}>
{_.map(teacherList, (item, index) => { {_.map(teacherList, (item, index) => {
return ( return (
<Select.Option value={item.id} key={item.id}>{item.nickName}</Select.Option> <Select.Option value={item.id} key={item.id}>
); {item.nickName}
</Select.Option>
)
})} })}
</Select> </Select>
</div> </div>
</When>
<Otherwise>
<div className='search-condition__item'>
<span className='shelf-status'>课程分类:</span>
<TreeSelect
treeNodeFilterProp='categoryName'
showSearch
value={categoryId || null}
style={{ minWidth: 'calc(100% - 75px)' }}
dropdownMatchSelectWidth={false}
treeData={categoryList}
placeholder='请选择课程分类'
allowClear
onChange={(value) => {
this.handleChangeQuery('categoryId', value)
}}
/>
</div>
</Otherwise>
</Choose>
<div className="search-condition__item"> <div className='search-condition__item'>
<span className="search-date">创建日期:</span> <span className='search-date'>创建日期:</span>
<RangePicker <RangePicker
id="course_date_picker" id='course_date_picker'
allowClear={false} allowClear={false}
value={ beginTime ? [moment(beginTime), moment(endTime)] : null } value={beginTime ? [moment(beginTime), moment(endTime)] : null}
format={"YYYY-MM-DD"} format={'YYYY-MM-DD'}
onChange={(dates) => { this.handleChangeDates(dates) }} onChange={(dates) => {
style={{ width: "calc(100% - 70px)" }} this.handleChangeDates(dates)
}}
style={{ width: 'calc(100% - 70px)' }}
/> />
</div> </div>
{ expandFilter &&
<div className="search-condition__item"> <If condition={expandFilter}>
<span className="shelf-status">学院展示:</span> <div className='search-condition__item'>
<span className='shelf-status'>学院展示:</span>
<Select <Select
style={{ width: "calc(100% - 84px)" }} style={{ width: 'calc(100% - 84px)' }}
placeholder="请选择" placeholder='请选择'
allowClear={true} allowClear={true}
value={shelfState} value={shelfState}
onChange={(value) => { this.handleChangeQuery('shelfState', value) }} onChange={(value) => {
suffixIcon={<span className="icon iconfont" style={{fontSize:'12px',color:'#BFBFBF'}}>&#xe835;</span>} this.handleChangeQuery('shelfState', value)
> }}
<Option value="YES">开启</Option> suffixIcon={
<Option value="NO">关闭</Option> <span className='icon iconfont' style={{ fontSize: '12px', color: '#BFBFBF' }}>
&#xe835;
</span>
}>
<Option value='YES'>开启</Option>
<Option value='NO'>关闭</Option>
</Select> </Select>
</div> </div>
} </If>
</div> </div>
<div className="reset-fold-area"> <div className='reset-fold-area'>
<Tooltip title="清空筛选"><span className="resetBtn iconfont icon" onClick={this.handleReset}>&#xe61b; </span></Tooltip> <Tooltip title='清空筛选'>
<span style={{ cursor: 'pointer' }} className="fold-btn" onClick={() => { <span className='resetBtn iconfont icon' onClick={this.handleReset}>
this.setState({expandFilter:!expandFilter}); &#xe61b;{' '}
}}>{this.state.expandFilter ? <span><span>收起</span><span className="iconfont icon fold-icon" >&#xe82d; </span> </span> : <span>展开<span className="iconfont icon fold-icon" >&#xe835; </span></span>}</span> </span>
</Tooltip>
<span
style={{ cursor: 'pointer' }}
className='fold-btn'
onClick={() => {
this.setState({ expandFilter: !expandFilter })
}}>
<Choose>
<When condition={this.state.expandFilter}>
<span>
<span>收起</span>
<span className='iconfont icon fold-icon'>&#xe82d; </span>{' '}
</span>
</When>
<Otherwise>
<span>
展开<span className='iconfont icon fold-icon'>&#xe835; </span>
</span>
</Otherwise>
</Choose>
</span>
</div> </div>
</Row> </Row>
</div> </div>
...@@ -232,4 +350,4 @@ class VideoCourseFilter extends React.Component { ...@@ -232,4 +350,4 @@ class VideoCourseFilter extends React.Component {
} }
} }
export default VideoCourseFilter; export default VideoCourseFilter
/* import React from "react"
* @Author: 吴文洁 import { Table, Modal, message, Tooltip, Switch, Dropdown } from "antd"
* @Date: 2020-08-05 10:12:45 import _ from "underscore"
* @LastEditors: wufan import { PageControl } from "@/components"
* @LastEditTime: 2021-05-13 16:34:11 import { LIVE_SHARE } from "@/domains/course-domain/constants"
* @Description: 视频课-列表模块
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
import { Table, Modal, message , Tooltip,Switch,Dropdown} from 'antd';
import { PageControl } from "@/components"; import ShareLiveModal from "@/modules/course-manage/modal/ShareLiveModal"
import { LIVE_SHARE_MAP } from '@/common/constants/academic/cloudClass'; import WatchDataModal from "../modal/WatchDataModal"
import { appId, shareUrl, LIVE_SHARE } from '@/domains/course-domain/constants'; import CourseService from "@/domains/course-domain/CourseService"
import RelatedPlanModal from "../../modal/RelatedPlanModal"
import User from "@/common/js/user"
import "./VideoCourseList.less"
import ShareLiveModal from '@/modules/course-manage/modal/ShareLiveModal';
import WatchDataModal from '../modal/WatchDataModal'
import CourseService from "@/domains/course-domain/CourseService";
import RelatedPlanModal from '../../modal/RelatedPlanModal';
import User from '@/common/js/user'
import './VideoCourseList.less';
const ENV = process.env.DEPLOY_ENV || 'dev';
const userRole = User.getUserRole();
class VideoCourseList extends React.Component { class VideoCourseList extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
id: '', // 视频课ID id: "", // 视频课ID
studentIds:[], studentIds: [],
RelatedPlanModalVisible:false, RelatedPlanModalVisible: false,
selectPlanList:{} selectPlanList: {},
ShelfLoading: false
// dataSource: [],
} }
} }
componentDidMount() { componentDidMount() {
const videoCourseItem = localStorage.getItem('videoCourseItem'); const videoCourseItem = localStorage.getItem("videoCourseItem")
if (videoCourseItem) { if (videoCourseItem) {
const _videoCourseItem = JSON.parse(videoCourseItem); const _videoCourseItem = JSON.parse(videoCourseItem)
this.handleShowShareModal(_videoCourseItem, true); this.handleShowShareModal(_videoCourseItem, true)
} }
} }
// 观看数据弹窗 // 观看数据弹窗
handleShowWatchDataModal = (record) => { handleShowWatchDataModal = (record) => {
console.log('111');
console.log("record",record);
const watchDataModal = ( const watchDataModal = (
<WatchDataModal <WatchDataModal
type='videoCourseList' type='videoCourseList'
...@@ -56,85 +42,133 @@ class VideoCourseList extends React.Component { ...@@ -56,85 +42,133 @@ class VideoCourseList extends React.Component {
close={() => { close={() => {
this.setState({ this.setState({
watchDataModal: null watchDataModal: null
}); })
}} }}
/> />
); )
this.setState({ watchDataModal }); this.setState({ watchDataModal })
} }
// 请求表头 // 请求表头
parseColumns = () => { parseColumns = () => {
const { type } = this.props
const { ShelfLoading } = this.state
const columns = [ const columns = [
{ {
title: '视频课', title: "视频课",
key: 'scheduleName', key: "scheduleName",
dataIndex: 'scheduleName', dataIndex: "scheduleName",
width:321, width: 321,
fixed: 'left', fixed: "left",
render: (val, record) => { render: (val, record) => {
const { coverUrl, scheduleVideoUrl } = record; const { coverUrl, scheduleVideoUrl } = record
return ( return (
<div className="record__item"> <div className='record__item'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */} {/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
<img className="course-cover" src={coverUrl || `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`} /> <img
{ record.courseName.length > 25? className='course-cover'
src={
coverUrl ||
(type === "internal" ? `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast` : "https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png")
}
alt=''
/>
<Choose>
<When condition={record.courseName.length > 25}>
<Tooltip title={record.courseName}> <Tooltip title={record.courseName}>
<div className="course-name">{record.courseName}</div> <div className='course-name'>{record.courseName}</div>
</Tooltip> </Tooltip>
: </When>
<div className="course-name">{record.courseName}</div> <Otherwise>
} <div className='course-name'>{record.courseName}</div>
</Otherwise>
</Choose>
</div> </div>
) )
} }
}, },
{ {
title: '课程分类', title: (
key: 'categoryName', <span>
dataIndex: 'categoryName', <span>课程分类</span>
<If condition={type !== "internal"}>
<Tooltip title={<div>外部课程的分类由系统提供,不影响企业课程分类。</div>}>
<i className='icon iconfont' style={{ marginLeft: "5px", cursor: "pointer", color: "#bfbfbf", fontSize: "14px", fontWeight: "normal" }}>
&#xe61d;
</i>
</Tooltip>
</If>
</span>
),
key: "categoryName",
dataIndex: "categoryName",
width: 200, width: 200,
render: (val, record) => { render: (val, record) => {
return ( return (
<div className="record__item"> <Choose>
{record.categoryOneName}{ record.categoryTwoName?`-${record.categoryTwoName}`:''} <When condition={type === "internal"}>
<div className='record__item'>
{record.categoryOneName}
{record.categoryTwoName ? `-${record.categoryTwoName}` : ""}
</div> </div>
</When>
<Otherwise>
<div className='record__item'>{record.categorySonName}</div>
</Otherwise>
</Choose>
) )
} }
}, },
{ {
title: '创建人', title: "创建人",
key: 'createName', key: "createName",
dataIndex: 'createName', dataIndex: "createName",
width: 100, width: 100,
render: (val) => { render: (val) => {
return ( return (
<div> <div>
{ val && {val && (
<Tooltip title={val}> <Tooltip title={val}>
<div> <div>{val.length > 4 ? `${val.slice(0, 4)}...` : val}</div>
{val.length > 4 ? `${val.slice(0,4)}...` : val}
</div>
</Tooltip> </Tooltip>
} )}
</div> </div>
) )
} }
}, },
{ {
title: <span> title: (
<span>
<span>学院展示</span> <span>学院展示</span>
<Tooltip title={<div>开启后,学员可在学院内查看到此课程。<br/>关闭后,学院内不再展示此课程,但学员仍可通过分享的海报/链接查看此课程。</div>}><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px'}}>&#xe61d;</i></Tooltip> <Tooltip
</span>, title={
<div>
开启后,学员可在学院内查看到此课程。
<br />
关闭后,学院内不再展示此课程,但学员仍可通过分享的海报/链接查看此课程。
</div>
}>
<i className='icon iconfont' style={{ marginLeft: "5px", cursor: "pointer", color: "#bfbfbf", fontSize: "14px" }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
width: 120, width: 120,
dataIndex: "courseware", key: "shelfState",
dataIndex: "shelfState",
render: (val, item, index) => { render: (val, item, index) => {
return ( return (
<Switch defaultChecked={item.shelfState==="YES"?true:false} onChange={()=>this.changeShelfState(item)}/> <Switch
loading={ShelfLoading}
checked={item.shelfState === "YES"}
defaultChecked={item.shelfState}
onClick={(checked) => {
this.changeShelfState(checked, item, index)
}}
/>
) )
}, }
}, },
{ {
title: "观看学员数", title: "观看学员数",
...@@ -142,73 +176,81 @@ class VideoCourseList extends React.Component { ...@@ -142,73 +176,81 @@ class VideoCourseList extends React.Component {
key: "watchUserCount", key: "watchUserCount",
dataIndex: "watchUserCount", dataIndex: "watchUserCount",
render: (val, item) => { render: (val, item) => {
return ( return <div className='watchUserCount'>{val || 0}</div>
<div className="watchUserCount">{val}</div> }
)
},
}, },
{ {
title: '创建时间', title: "创建时间",
width: 181, width: 181,
key: 'created', key: "created",
dataIndex: 'created', dataIndex: "created",
sorter: true, sorter: true,
render: (val) => { render: (val) => {
return formatDate('YYYY-MM-DD H:i', val) return formatDate("YYYY-MM-DD H:i", val)
} }
}, },
{ {
title: '更新时间', title: "更新时间",
width: 181, width: 181,
key: 'updated', key: "updated",
dataIndex: 'updated', dataIndex: "updated",
sorter: true, sorter: true,
render: (val) => { render: (val) => {
return formatDate('YYYY-MM-DD H:i', val) return formatDate("YYYY-MM-DD H:i", val)
} }
}, },
{ {
title: '关联项', title: "关联项",
width: 200, width: 200,
key: "planList", key: "planList",
dataIndex: "planList", dataIndex: "planList",
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 <span>{item.planName} { (index < record.relatedPlanList.length-1)&&(<span> 、</span>)} </span> {record.relatedPlanList.map((item, index) => {
}) return (
} <span>
{item.planName} {index < record.relatedPlanList.length - 1 && <span> 、</span>}
</span>
)
})}
</Tooltip> </Tooltip>
: </When>
<Otherwise>
<span></span> <span></span>
} </Otherwise>
</Choose>
</div> </div>
) )
} }
}, },
{ {
title: '操作', title: "操作",
key: 'operate', key: "operate",
dataIndex: 'operate', dataIndex: "operate",
width: 210, width: 210,
fixed: 'right', fixed: "right",
render: (val, record) => { render: (val, record) => {
return ( return (
<div className="operate"> <div className='operate'>
<div className="operate__item" onClick={()=>this.handleShowWatchDataModal(record)}>观看数据</div> <div className='operate__item' onClick={() => this.handleShowWatchDataModal(record)}>
<span className="operate__item split"> | </span> 观看数据
<div className="operate__item" onClick={() => this.handleShowShareModal(record)}>分享</div> </div>
<span className="operate__item split"> | </span>
<If condition={type === "internal"}>
<span className='operate__item split'> | </span>
<div className='operate__item' onClick={() => this.handleShowShareModal(record)}>
分享
</div>
</If>
<span className='operate__item split'> | </span>
<Dropdown overlay={this.renderMoreOperate(record)}> <Dropdown overlay={this.renderMoreOperate(record)}>
<span className="more-operate"> <span className='more-operate'>
<span className="operate-text">更多</span> <span className='operate-text'>更多</span>
<span <span className='iconfont icon' style={{ color: "#2966FF" }}>
className="iconfont icon"
style={{ color: "#5289FA" }}
>
&#xe824; &#xe824;
</span> </span>
</span> </span>
...@@ -217,208 +259,222 @@ class VideoCourseList extends React.Component { ...@@ -217,208 +259,222 @@ class VideoCourseList extends React.Component {
) )
} }
} }
]; ]
return columns;
type !== "internal" && columns.splice(2, 1)
return columns
} }
renderMoreOperate = (item) => { renderMoreOperate = (item) => {
const { type } = this.props
return ( return (
<div className="live-course-more-menu"> <div className='live-course-more-menu'>
{ (User.getUserRole() === "CloudManager" || User.getUserRole() === "StoreManager") && <If condition={type !== "internal"}>
<div <div className='operate__item' onClick={() => this.handleShowShareModal(item)}>
className="operate__item" 分享
onClick={()=>this.handleRelatedModalShow(item)} </div>
>关联培训计划</div> </If>
} {(User.getUserRole() === "CloudManager" || User.getUserRole() === "StoreManager") && (
<div className='operate__item' onClick={() => this.handleRelatedModalShow(item)}>
关联培训计划
</div>
)}
<If condition={type === "internal"}>
<div <div
className="operate__item" className='operate__item'
onClick={() => { onClick={() => {
RCHistory.push(`/create-video-course?type=edit&id=${item.id}`); window.RCHistory.push(`/create-video-course?type=edit&id=${item.id}`)
}} }}>
>编辑</div> 编辑
<div </div>
className="operate__item" <div className='operate__item' onClick={() => this.handleDeleteVideoCourse(item.id)}>
onClick={() => this.handleDeleteVideoCourse(item.id)} 删除
>删除</div> </div>
</If>
</div> </div>
) )
} }
handlePlanName = (planArray)=>{ handlePlanName = (planArray) => {
let planStr = ""; let planStr = ""
planArray.map((item,index)=>{ planArray.map((item, index) => {
if(index < planArray.length-1){ if (index < planArray.length - 1) {
planStr = planStr + item.planName + '、'; planStr = planStr + item.planName + "、"
}else{ } else {
planStr = planStr + item.planName planStr = planStr + item.planName
} }
}) })
return planStr return planStr
} }
//改变上架状态 //改变上架状态
changeShelfState = (item) =>{ changeShelfState = (checked, item, index) => {
let _shelfState = item.shelfState let _shelfState = checked ? "YES" : "NO"
if(_shelfState==='NO'){ if (checked) {
_shelfState = "YES"; _shelfState = "YES"
item.shelfState = "YES" } else {
}else{
_shelfState = "NO" _shelfState = "NO"
item.shelfState = "NO"
} }
const params={ const params = {
courseId: item.id, courseId: item.id,
shelfState:_shelfState shelfState: _shelfState
}
CourseService.changeVideoShelfState(params).then((res)=>{
if(res.success){
if(_shelfState === "YES"){
message.success("已开启展示");
}else{
message.success("已取消展示");
} }
CourseService.changeVideoShelfState(params).then(() => {
if (_shelfState === "YES") {
message.success("已开启展示")
} else {
message.success("已取消展示")
} }
this.props.changeShelfState(index, _shelfState)
}) })
} }
// 删除视频课 // 删除视频课
handleDeleteVideoCourse = (scheduleId) => { handleDeleteVideoCourse = (scheduleId) => {
Modal.confirm({ Modal.confirm({
title: '你确定要删除此视频课吗?', title: "你确定要删除此视频课吗?",
content: '删除后,学员将不能进行观看。', content: "删除后,学员将不能进行观看。",
icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>, icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
okText: '确定', okText: "确定",
okType: 'danger', okType: "danger",
cancelText: '取消', cancelText: "取消",
onOk: () => { onOk: () => {
const param ={ const param = {
courseId:scheduleId, courseId: scheduleId,
storeId:User.getStoreId() storeId: User.getStoreId()
} }
CourseService.delVideoSchedule( CourseService.delVideoSchedule(param).then(() => {
param message.success("删除成功")
).then(() => { this.props.onChange()
message.success('删除成功');
this.props.onChange();
}) })
} }
}); })
} }
// 显示分享弹窗 // 显示分享弹窗
handleShowShareModal = (record, needStr = false) => { handleShowShareModal = (record, needStr = false) => {
const { id, scheduleVideoUrl } = record; const { type } = this.props
const { id, scheduleVideoUrl } = record
const _appId = appId; const htmlUrl = `${LIVE_SHARE}video_detail/${id}?id=${User.getStoreId()}`
const htmlUrl = `${LIVE_SHARE}video_detail/${id}?id=${User.getStoreId()}`; const longUrl = htmlUrl
const longUrl = htmlUrl; const { coverUrl, courseName } = record
const { coverUrl, courseName } = record;
const shareData = { const shareData = {
longUrl, longUrl,
coverUrl, coverUrl,
scheduleVideoUrl, scheduleVideoUrl,
courseName, courseName
}; }
const shareLiveModal = ( const shareLiveModal = (
<ShareLiveModal <ShareLiveModal
needStr={needStr} needStr={needStr}
data={shareData} data={shareData}
type="videoClass" type='videoClass'
title="视频课" courseDivision={type}
title='视频课'
close={() => { close={() => {
this.setState({ this.setState({
shareLiveModal: null shareLiveModal: null
}); })
localStorage.setItem('videoCourseItem', ''); localStorage.setItem("videoCourseItem", "")
}} }}
/> />
); )
this.setState({ shareLiveModal }); this.setState({ shareLiveModal })
} }
handleChangeTable = (pagination, filters, sorter) => { handleChangeTable = (pagination, filters, sorter) => {
const { columnKey, order } = sorter; const { columnKey, order } = sorter
const { query } = this.props; const { query } = this.props
let { order: _order } =query; let { order: _order } = query
// 按创建时间升序排序 // 按创建时间升序排序
if (columnKey === 'created' && order === 'ascend') { _order = 'CREATED_ASC'; } if (columnKey === "created" && order === "ascend") {
_order = "CREATED_ASC"
}
// 按创建时间降序排序 // 按创建时间降序排序
if (columnKey === 'created' && order === 'descend') { _order = 'CREATED_DESC'; } if (columnKey === "created" && order === "descend") {
_order = "CREATED_DESC"
}
// 按更新时间升序排序 // 按更新时间升序排序
if (columnKey === 'updated' && order === 'ascend') { _order = 'UPDATED_ASC'; } if (columnKey === "updated" && order === "ascend") {
_order = "UPDATED_ASC"
}
// 按更新时间降序排序 // 按更新时间降序排序
if (columnKey === 'updated' && order === 'descend') { _order = 'UPDATED_DESC'; } if (columnKey === "updated" && order === "descend") {
_order = "UPDATED_DESC"
}
const _query = { const _query = {
...query, ...query,
orderEnum: _order orderEnum: _order
};
this.props.onChange(_query);
} }
handleRelatedModalShow = (item)=>{ this.props.onChange(_query)
const selectPlanList = {}; }
if(item.relatedPlanList){ handleRelatedModalShow = (item) => {
item.relatedPlanList.map((item,index)=>{ const selectPlanList = {}
if (item.relatedPlanList) {
item.relatedPlanList.map((item, index) => {
selectPlanList[item.planId] = {} selectPlanList[item.planId] = {}
selectPlanList[item.planId].planId = item.planId; selectPlanList[item.planId].planId = item.planId
selectPlanList[item.planId].taskBaseVOList = [{taskId:item.taskId}]; selectPlanList[item.planId].taskBaseVOList = [{ taskId: item.taskId }]
return item return item
}) })
} }
this.setState({ this.setState({
RelatedPlanModalVisible:true, RelatedPlanModalVisible: true,
selectCourseId:item.id, selectCourseId: item.id,
selectPlanList:selectPlanList selectPlanList: selectPlanList
}) })
} }
closeRelatedPlanModalVisible = ()=>{ closeRelatedPlanModalVisible = () => {
this.setState({ this.setState({
RelatedPlanModalVisible:false RelatedPlanModalVisible: false
}) })
} }
onChangeSelectPlanList = (selectPlanList)=>{ onChangeSelectPlanList = (selectPlanList) => {
this.setState({ this.setState({
selectPlanList:selectPlanList selectPlanList: selectPlanList
}) })
} }
onConfirmSelectPlanList = ()=>{ onConfirmSelectPlanList = () => {
this.setState({ this.setState(
RelatedPlanModalVisible:false {
},()=>{this.props.onChange();}) RelatedPlanModalVisible: false
},
() => {
this.props.onChange()
}
)
} }
render() { render() {
const { dataSource = [], totalCount, query, type } = this.props
const { dataSource = [], totalCount, query } = this.props; const { current, size } = query
const { current, size } = query; const { RelatedPlanModalVisible, selectPlanList, selectCourseId } = this.state
const {RelatedPlanModalVisible,selectPlanList,selectCourseId} = this.state;
return ( return (
<div className="video-course-list"> <div className={`video-course-list ${type !== "internal" ? "video-course-list-mt" : ""}`}>
<Table <Table
rowKey={record => record.id} rowKey={(record) => record.id}
dataSource={dataSource} dataSource={dataSource}
columns={this.parseColumns()} columns={this.parseColumns()}
onChange={this.handleChangeTable} onChange={this.handleChangeTable}
pagination={false} pagination={false}
scroll={{ x: 1500}} scroll={{ x: 1500 }}
bordered bordered
className="video-list-table" className='video-list-table'
/> />
<div className="box-footer"> <div className='box-footer'>
<PageControl <PageControl
current={current - 1} current={current - 1}
pageSize={size} pageSize={size}
total={totalCount} total={totalCount}
toPage={(page) => { toPage={(page) => {
const _query = {...query, current: page + 1}; const _query = { ...query, current: page + 1 }
this.props.onChange(_query) this.props.onChange(_query)
}} }}
/> />
</div> </div>
{ RelatedPlanModalVisible && {RelatedPlanModalVisible && (
<RelatedPlanModal <RelatedPlanModal
onClose={this.closeRelatedPlanModalVisible} onClose={this.closeRelatedPlanModalVisible}
visible={RelatedPlanModalVisible} visible={RelatedPlanModalVisible}
...@@ -427,12 +483,12 @@ class VideoCourseList extends React.Component { ...@@ -427,12 +483,12 @@ class VideoCourseList extends React.Component {
onChange={this.onChangeSelectPlanList} onChange={this.onChangeSelectPlanList}
onConfirm={this.onConfirmSelectPlanList} onConfirm={this.onConfirmSelectPlanList}
/> />
} )}
{ this.state.shareLiveModal } {this.state.shareLiveModal}
{ this.state.watchDataModal } {this.state.watchDataModal}
</div> </div>
) )
} }
} }
export default VideoCourseList; export default VideoCourseList
.video-course-list { .video-course-list {
margin-top: 12px; margin-top: 12px;
.video-list-table{ &.video-course-list-mt {
margin-top: 4px;
}
.video-list-table {
tbody { tbody {
tr{ tr {
&:nth-child(even){ &:nth-child(even) {
background: transparent !important; background: transparent !important;
td{ td {
background:#FFF !important; background: #fff !important;
} }
} }
&:nth-child(odd){ &:nth-child(odd) {
background: #FAFAFA !important; background: #fafafa !important;
td{ td {
background: #FAFAFA !important; background: #fafafa !important;
} }
} }
&:hover{ &:hover {
td{ td {
background:#F3f6fa !important; background: #f3f6fa !important;
} }
} }
} }
} }
} }
.watchUserCount{ .watchUserCount {
text-align:right; text-align: right;
padding:16px; padding: 16px;
} }
.operate-text { .operate-text {
color: #5289FA; color: #5289fa;
cursor: pointer; cursor: pointer;
} }
.operate { .operate {
display: flex; display: flex;
&__item { &__item {
color: #5289FA; color: #5289fa;
cursor: pointer; cursor: pointer;
&.split { &.split {
margin: 0 8px; margin: 0 8px;
color: #BFBFBF; color: #bfbfbf;
} }
} }
} }
.more-operate{ .more-operate {
line-height:20px; line-height: 20px;
} }
.record__item { .record__item {
display: flex; display: flex;
...@@ -56,21 +59,21 @@ ...@@ -56,21 +59,21 @@
height: 50px; height: 50px;
border-radius: 2px; border-radius: 2px;
margin-right: 8px; margin-right: 8px;
background-color: #666; // background-color: #666;
} }
.course-name { .course-name {
color: #666; color: #666;
width:188px; width: 188px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
height:48px; height: 48px;
} }
} }
.related-task{ .related-task {
text-overflow: -o-ellipsis-lastline; text-overflow: -o-ellipsis-lastline;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
......
/* import React from 'react'
* @Author: 吴文洁 import { Tabs } from 'antd'
* @Date: 2020-08-05 10:08:06 import VideoCourseFilter from './components/VideoCourseFilter'
* @LastEditors: zhangleyuan import VideoCourseOpt from './components/VieoCourseOpt'
* @LastEditTime: 2020-12-26 15:56:49 import VideoCourseList from './components/VideoCourseList'
* @Description: 云课堂-视频课入口页面 import CourseService from '@/domains/course-domain/CourseService'
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
import VideoCourseFilter from './components/VideoCourseFilter';
import VideoCourseOpt from './components/VieoCourseOpt';
import VideoCourseList from './components/VideoCourseList';
import CourseService from "@/domains/course-domain/CourseService";
import User from '@/common/js/user' import User from '@/common/js/user'
const { TabPane } = Tabs
class VideoCourse extends React.Component { class VideoCourse extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
query: { query: {
size: 10, size: 10,
current: 1, current: 1,
storeId:User.getStoreId() storeId: User.getStoreId(),
}, },
dataSource: [], // 视频课列表 dataSource: [], // 视频课列表
totalCount: 0, // 视频课数据总条数 totalCount: 0, // 视频课数据总条数
currentTabKey: 'internal',
} }
} }
componentWillMount() { componentWillMount() {
// 获取视频课列表 // 获取视频课列表
this.handleFetchScheduleList(); this.handleFetchScheduleList()
} }
// 获取视频课列表 // 获取视频课列表
handleFetchScheduleList = (_query = {}) => { handleFetchScheduleList = (_query = {}) => {
const { currentTabKey } = this.state
const query = { const query = {
...this.state.query, ...this.state.query,
..._query ..._query,
};
courseDivision: currentTabKey === 'external' ? 1 : null,
}
// 更新请求参数 // 更新请求参数
this.setState({ query }); this.setState({ query })
CourseService.videoSchedulePage(query).then((res) => { CourseService.videoSchedulePage(query).then((res) => {
const { result = {} } = res || {}; const { result = {} } = res || {}
const { records = [], total = 0 } = result; const { records = [], total = 0 } = result
if (Number(total) && query.size * (query.current - 1) >= Number(total)) {
this.handleFetchScheduleList({
...query,
current: 1,
})
return
}
this.setState({ this.setState({
dataSource: records, dataSource: records,
totalCount: Number(total) totalCount: Number(total),
}); })
}); })
}
currenTabChange = (currentTabKey) => {
const { query } = this.state
this.setState(
{
currentTabKey,
query: {
...query,
current: 1,
},
},
() => {
this.handleFetchScheduleList()
}
)
}
changeShelfState = (index, shelfState) => {
const { dataSource } = this.state
dataSource[index].shelfState = shelfState
this.setState({
dataSource,
})
} }
render() { render() {
const { dataSource, totalCount, query } = this.state; const { dataSource, totalCount, query, currentTabKey } = this.state
return ( return (
<div className="page video-course-page"> <div className='page video-course-page'>
<div className="content-header">视频课</div> <div className='content-header'>视频课</div>
<div className="box"> <div className='box'>
{/* 搜索模块 */} <Tabs onChange={this.currenTabChange} activeKey={currentTabKey}>
<VideoCourseFilter <TabPane key='internal' tab='内部课程'></TabPane>
onChange={this.handleFetchScheduleList}
/>
<TabPane key='external' tab='外部课程'></TabPane>
</Tabs>
{/* 搜索模块 */}
<VideoCourseFilter currentTabKey={currentTabKey} onChange={this.handleFetchScheduleList} />
{/* 操作模块 */} {/* 操作模块 */}
<If condition={currentTabKey === 'internal'}>
<VideoCourseOpt /> <VideoCourseOpt />
</If>
{/* 视频课列表模块 */} {/* 视频课列表模块 */}
<VideoCourseList <VideoCourseList
type={currentTabKey}
query={query} query={query}
dataSource={dataSource} dataSource={dataSource}
totalCount={totalCount} totalCount={totalCount}
onChange={this.handleFetchScheduleList} onChange={this.handleFetchScheduleList}
changeShelfState={this.changeShelfState}
/> />
</div> </div>
</div> </div>
...@@ -82,4 +111,4 @@ class VideoCourse extends React.Component { ...@@ -82,4 +111,4 @@ class VideoCourse extends React.Component {
} }
} }
export default VideoCourse; export default VideoCourse
...@@ -2,93 +2,93 @@ ...@@ -2,93 +2,93 @@
* @Description: * @Description:
* @Author: zangsuyun * @Author: zangsuyun
* @Date: 2021-03-12 14:49:40 * @Date: 2021-03-12 14:49:40
* @LastEditors: wufan * @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-13 16:35:41 * @LastEditTime: 2021-05-30 18:31:24
* @Copyright: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyright: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React from "react"; import React from "react"
import { Table, Modal, message, Tooltip, Switch, Dropdown, Button } from "antd"; import { Table, Modal, message, Tooltip, Switch, Dropdown, Button } from "antd"
import { Route, withRouter } from "react-router-dom"; import { Route, withRouter } from "react-router-dom"
import { PageControl } from "@/components"; import { PageControl } from "@/components"
import { LIVE_SHARE_MAP } from "@/common/constants/academic/cloudClass"; import { LIVE_SHARE_MAP } from "@/common/constants/academic/cloudClass"
import { appId, shareUrl, LIVE_SHARE } from "@/domains/course-domain/constants"; import { appId, shareUrl, LIVE_SHARE } from "@/domains/course-domain/constants"
import ScanFileModal from "../../resource-disk/modal/ScanFileModal"; import ScanFileModal from "../../resource-disk/modal/ScanFileModal"
import WatchData from "./WatchData"; import WatchData from "./WatchData"
import KnowledgeAPI from "@/data-source/knowledge/request-api"; import KnowledgeAPI from "@/data-source/knowledge/request-api"
import ENUM from "../ENUM.js"; import ENUM from "../ENUM.js"
import "./KnowledgeBaseList.less"; import "./KnowledgeBaseList.less"
const DEFAULT_SIZE_UNIT = 1000 * 1000; // 将B转换成M const DEFAULT_SIZE_UNIT = 1000 * 1000 // 将B转换成M
const { confirm } = Modal; const { confirm } = Modal
const ENV = process.env.DEPLOY_ENV || "dev"; const ENV = process.env.DEPLOY_ENV || "dev"
class KnowledgeBaseList extends React.Component { class KnowledgeBaseList extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
id: "", id: "",
scanFileModal:null scanFileModal: null
}; }
} }
handleAdminName = (adminArray) => { handleAdminName = (adminArray) => {
let adminStr = ""; let adminStr = ""
adminArray.map((item, index) => { adminArray.map((item, index) => {
if (index < adminArray.length - 1) { if (index < adminArray.length - 1) {
adminStr = adminStr + item.adminName + "、"; adminStr = adminStr + item.adminName + "、"
} else { } else {
adminStr = adminStr + item.adminName; adminStr = adminStr + item.adminName
}
})
return adminStr
} }
});
return adminStr;
};
handleUp = (index, record) => { handleUp = (index, record) => {
if (index === 0 && this.props.query.current === 1) { if (index === 0 && this.props.query.current === 1) {
return; return
} }
const params = { const params = {
direction: "UP", direction: "UP",
id: record.id, id: record.id,
storeId: record.storeId, storeId: record.storeId
}; }
KnowledgeAPI.moveKnowledge(params).then((res) => { KnowledgeAPI.moveKnowledge(params).then((res) => {
if (res.success) { if (res.success) {
message.success("更新成功"); message.success("更新成功")
this.props.onChange(); this.props.onChange()
}
})
} }
});
};
handleDown = (record, index) => { handleDown = (record, index) => {
const { query, totalCount } = this.props; const { query, totalCount } = this.props
const { current, size } = query; const { current, size } = query
if (totalCount === size * (current - 1) + index + 1) { if (totalCount === size * (current - 1) + index + 1) {
return; return
} }
const params = { const params = {
direction: "DOWN", direction: "DOWN",
id: record.id, id: record.id,
storeId: record.storeId, storeId: record.storeId
}; }
KnowledgeAPI.moveKnowledge(params).then((res) => { KnowledgeAPI.moveKnowledge(params).then((res) => {
if (res.success) { if (res.success) {
message.success("更新成功"); message.success("更新成功")
this.props.onChange(); this.props.onChange()
}
})
} }
});
};
getBlob = (url) => { getBlob = (url) => {
return new Promise((resolve) => { return new Promise((resolve) => {
const xhr = new XMLHttpRequest() const xhr = new XMLHttpRequest()
xhr.open('GET', url, true) xhr.open("GET", url, true)
xhr.responseType = 'blob' xhr.responseType = "blob"
xhr.onload = () => { xhr.onload = () => {
if (xhr.status === 200) { if (xhr.status === 200) {
resolve(xhr.response) resolve(xhr.response)
...@@ -102,8 +102,8 @@ class KnowledgeBaseList extends React.Component { ...@@ -102,8 +102,8 @@ class KnowledgeBaseList extends React.Component {
if (window.navigator.msSaveOrOpenBlob) { if (window.navigator.msSaveOrOpenBlob) {
navigator.msSaveBlob(blob, filename) navigator.msSaveBlob(blob, filename)
} else { } else {
const link = document.createElement('a') const link = document.createElement("a")
const body = document.querySelector('body') const body = document.querySelector("body")
// 创建对象url // 创建对象url
link.href = window.URL.createObjectURL(blob) link.href = window.URL.createObjectURL(blob)
...@@ -127,12 +127,12 @@ class KnowledgeBaseList extends React.Component { ...@@ -127,12 +127,12 @@ class KnowledgeBaseList extends React.Component {
// 预览文件 // 预览文件
handleScanFile = (folder) => { handleScanFile = (folder) => {
console.log(folder); console.log(folder)
const { folderFormat, folderSize, ossUrl } = folder; const { folderFormat, folderSize, ossUrl } = folder
switch (folderFormat) { switch (folderFormat) {
case "PDF": case "PDF":
window.open(ossUrl, "_blank"); window.open(ossUrl, "_blank")
break; break
case "WORD": case "WORD":
case "DOCX": case "DOCX":
case "DOC": case "DOC":
...@@ -141,11 +141,7 @@ class KnowledgeBaseList extends React.Component { ...@@ -141,11 +141,7 @@ class KnowledgeBaseList extends React.Component {
case "PPTX": case "PPTX":
case "PDF": case "PDF":
if ( if (
((folderFormat === "PPT" || ((folderFormat === "PPT" || folderFormat === "PPTX" || folderFormat === "DOCX" || folderFormat === "WORD" || folderFormat === "DOC") &&
folderFormat === "PPTX" ||
folderFormat === "DOCX" ||
folderFormat === "WORD" ||
folderFormat === "DOC") &&
folderSize > 10 * DEFAULT_SIZE_UNIT) || folderSize > 10 * DEFAULT_SIZE_UNIT) ||
(folderFormat === "EXCEL" && folderSize > 5 * DEFAULT_SIZE_UNIT) (folderFormat === "EXCEL" && folderSize > 5 * DEFAULT_SIZE_UNIT)
) { ) {
...@@ -157,33 +153,33 @@ class KnowledgeBaseList extends React.Component { ...@@ -157,33 +153,33 @@ class KnowledgeBaseList extends React.Component {
okText: "下载", okText: "下载",
onOk: () => { onOk: () => {
this.handleDownload(folder) this.handleDownload(folder)
},
});
break;
} }
const prefixUrl = "https://view.officeapps.live.com/op/view.aspx?src="; })
const scanUrl = `${prefixUrl}${encodeURIComponent(ossUrl)}`; break
window.open(scanUrl, "_blank"); }
break; const prefixUrl = "https://view.officeapps.live.com/op/view.aspx?src="
const scanUrl = `${prefixUrl}${encodeURIComponent(ossUrl)}`
window.open(scanUrl, "_blank")
break
default: default:
const scanFileModal = ( const scanFileModal = (
<ScanFileModal <ScanFileModal
fileType={folderFormat} fileType={folderFormat}
item={folder} item={folder}
close={() => { close={() => {
this.setState({ scanFileModal: null }); this.setState({ scanFileModal: null })
}} }}
/> />
); )
this.setState({ scanFileModal }); this.setState({ scanFileModal })
break; break
}
} }
};
// 请求表头 // 请求表头
parseColumns = () => { parseColumns = () => {
const { query, totalCount } = this.props; const { query, totalCount } = this.props
const { current, size } = query; const { current, size } = query
const columns = [ const columns = [
{ {
title: "课程名称", title: "课程名称",
...@@ -192,131 +188,90 @@ class KnowledgeBaseList extends React.Component { ...@@ -192,131 +188,90 @@ class KnowledgeBaseList extends React.Component {
width: 391, width: 391,
fixed: "left", fixed: "left",
render: (val, record) => { render: (val, record) => {
const { coverUrl, mediaCourseUrl } = record.source; const { coverUrl, mediaCourseUrl, courseDivision } = record.source
let hasCover = false; let hasCover = false
const type = record.type; const type = record.type
return ( return (
<div> <div>
{type === "LIVE" && ( {type === "LIVE" && (
<div className="record__item"> <div className='record__item'>
{record.source && {record.source &&
record.source.courseMediaVOS.map((item, index) => { record.source.courseMediaVOS.map((item, index) => {
if (item.contentType === "COVER") { if (item.contentType === "COVER") {
hasCover = true; hasCover = true
return ( return <img className='course-cover' key={index} src={item.mediaUrl} />
<img
className="course-cover"
key={index}
src={item.mediaUrl}
/>
);
} }
})} })}
{!hasCover && ( {!hasCover && <img className='course-cover' src={"https://image.xiaomaiketang.com/xm/YNfi45JwFA.png"} />}
<img
className="course-cover"
src={"https://image.xiaomaiketang.com/xm/YNfi45JwFA.png"}
/>
)}
<div> <div>
{val.length > 17 ? ( {val.length > 17 ? (
<Tooltip title={val}> <Tooltip title={val}>
<div className="course-name">{val}</div> <div className='course-name'>{val}</div>
</Tooltip> </Tooltip>
) : ( ) : (
<div className="course-name">{val}</div> <div className='course-name'>{val}</div>
)} )}
<div> <div>
<span className="course-time"> <span className='course-time'>
{formatDate( {formatDate("YYYY-MM-DD H:i", parseInt(record.source.startTime))}~{formatDate("H:i", parseInt(record.source.endTime))}
"YYYY-MM-DD H:i",
parseInt(record.source.startTime)
)}
~{formatDate("H:i", parseInt(record.source.endTime))}
</span> </span>
<span <span
className="course-status" className='course-status'
style={{ style={{
color: color: ENUM.courseStateShow[record.source.courseState].color,
ENUM.courseStateShow[record.source.courseState] border: `1px solid ${ENUM.courseStateShow[record.source.courseState].color}`
.color, }}>
border: `1px solid ${
ENUM.courseStateShow[record.source.courseState]
.color
}`,
}}
>
{ENUM.courseStateShow[record.source.courseState].title} {ENUM.courseStateShow[record.source.courseState].title}
</span> </span>
{record.hideToUser && ( {record.hideToUser && (
<Tooltip <Tooltip title={<div>课程未成功开课,已在学员知识列表中隐藏</div>}>
title={
<div>课程未成功开课,已在学员知识列表中隐藏</div>
}
>
<i <i
className="icon iconfont" className='icon iconfont'
style={{ style={{
marginLeft: "5px", marginLeft: "5px",
cursor: "pointer", cursor: "pointer",
color: "#FF4F4F", color: "#FF4F4F",
fontSize: "14px", fontSize: "14px"
}} }}>
>
&#xe61d; &#xe61d;
</i> </i>
</Tooltip> </Tooltip>
)} )}
</div> </div>
<div className="teacher-assistant"> <div className='teacher-assistant'>
{record.source.teacherName.length > 4 ? ( {record.source.teacherName.length > 4 ? (
<Tooltip title={record.source.teacherName}> <Tooltip title={record.source.teacherName}>
<span className="teacher"> <span className='teacher'>讲师:{record.source.teacherName}</span>
讲师:{record.source.teacherName}
</span>
</Tooltip> </Tooltip>
) : ( ) : (
<span className="teacher"> <span className='teacher'>讲师:{record.source.teacherName}</span>
讲师:{record.source.teacherName}
</span>
)} )}
{record.source.admins.length > 0 && ( {record.source.admins.length > 0 && (
<> <>
<span className="split"> | </span> <span className='split'> | </span>
{this.handleAdminName(record.source.admins).length > {this.handleAdminName(record.source.admins).length > 4 ? (
4 ? ( <Tooltip title={this.handleAdminName(record.source.admins)}>
<Tooltip <span className='assistant'>
title={this.handleAdminName(record.source.admins)}
>
<span className="assistant">
助教: 助教:
{record.source.admins.map((item, index) => { {record.source.admins.map((item, index) => {
return ( return (
<span> <span>
{item.adminName}{" "} {item.adminName} {index < record.source.admins.length - 1 && <span> 、</span>}{" "}
{index <
record.source.admins.length - 1 && (
<span> 、</span>
)}{" "}
</span> </span>
); )
})} })}
</span> </span>
</Tooltip> </Tooltip>
) : ( ) : (
<span className="assistant"> <span className='assistant'>
助教: 助教:
{record.source.admins.map((item, index) => { {record.source.admins.map((item, index) => {
return ( return (
<span key={index}> <span key={index}>
{item.adminName}{" "} {item.adminName} {index < record.source.admins.length - 1 && <span> 、</span>}{" "}
{index <
record.source.admins.length - 1 && (
<span> 、</span>
)}{" "}
</span> </span>
); )
})} })}
</span> </span>
)} )}
...@@ -328,88 +283,77 @@ class KnowledgeBaseList extends React.Component { ...@@ -328,88 +283,77 @@ class KnowledgeBaseList extends React.Component {
)} )}
{type === "VOICE" && ( {type === "VOICE" && (
<div className="record__item"> <div className='record__item'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */} {/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
<img <img
className="course-cover" className='course-cover'
src={ src={
coverUrl || coverUrl
`${mediaCourseUrl}?x-oss-process=video/snapshot,t_0,m_fast` ? coverUrl
: courseDivision !== "EXTERNAL"
? `${mediaCourseUrl}?x-oss-process=video/snapshot,t_0,m_fast`
: "https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png"
} }
alt=''
/> />
{val.length > 25 ? ( {val.length > 25 ? (
<Tooltip title={val}> <Tooltip title={val}>
<div className="course-name clamp">{val}</div> <div className='course-name clamp'>{val}</div>
</Tooltip> </Tooltip>
) : ( ) : (
<div className="course-name clamp">{val}</div> <div className='course-name clamp'>{val}</div>
)} )}
</div> </div>
)} )}
{type === "PICTURE" && ( {type === "PICTURE" && (
<div className="record__item"> <div className='record__item'>
<img <img className='course-cover' src={coverUrl || "https://image.xiaomaiketang.com/xm/YNfi45JwFA.png"} />
className="course-cover"
src={
coverUrl ||
"https://image.xiaomaiketang.com/xm/YNfi45JwFA.png"
}
/>
{val.length > 25 ? ( {val.length > 25 ? (
<Tooltip title={val}> <Tooltip title={val}>
<div className="course-name clamp">{val}</div> <div className='course-name clamp'>{val}</div>
</Tooltip> </Tooltip>
) : ( ) : (
<div className="course-name clamp">{val}</div> <div className='course-name clamp'>{val}</div>
)} )}
</div> </div>
)} )}
{type === "FOLDER" && ( {type === "FOLDER" && (
<div <div
className="record__item" className='record__item'
onClick={() => { onClick={() => {
this.handleScanFile(record.source); this.handleScanFile(record.source)
}} }}>
> <div className={`folder-type ${record.source && record.source.folderFormat}`} />
<div
className={`folder-type ${
record.source && record.source.folderFormat
}`}
/>
{val.length > 25 ? ( {val.length > 25 ? (
<Tooltip title={val}> <Tooltip title={val}>
<div className="course-name clamp">{val}</div> <div className='course-name clamp'>{val}</div>
</Tooltip> </Tooltip>
) : ( ) : (
<div className="course-name clamp">{val}</div> <div className='course-name clamp'>{val}</div>
)} )}
</div> </div>
)} )}
</div> </div>
); )
}, }
}, },
{ {
title: "课程类型", title: "课程类型",
key: "type", key: "type",
dataIndex: "type", dataIndex: "type",
align:'center', align: "center",
// width: 100, // width: 100,
render: (val, record) => { render: (val, record) => {
return ( return <div className=''>{val ? ENUM.CourseTypeEnum[val] : "-"}</div>
<div className=""> }
{val ? ENUM.CourseTypeEnum[val] : "-"}
</div>
);
},
}, },
{ {
title: "创建人", title: "创建人",
key: "createName", key: "createName",
dataIndex: "createName", dataIndex: "createName",
align:'center', align: "center",
render: (val) => { render: (val) => {
return ( return (
<div> <div>
...@@ -419,31 +363,27 @@ class KnowledgeBaseList extends React.Component { ...@@ -419,31 +363,27 @@ class KnowledgeBaseList extends React.Component {
</Tooltip> </Tooltip>
)} )}
</div> </div>
); )
}, }
}, },
{ {
title: "观看学员数", title: "观看学员数",
key: "watchUserCount", key: "watchUserCount",
dataIndex: "watchUserCount", dataIndex: "watchUserCount",
align:'right', align: "right",
render: (val, item) => { render: (val, item) => {
return val ? ( return val ? (
<div <div className='operate' style={{ display: "block" }} onClick={() => this.handleLinkToClassData(item)}>
className="operate" <span className='operate__item'>{val}</span>
style={{display:'block'}}
onClick={() => this.handleLinkToClassData(item)}
>
<span className="operate__item">{val}</span>
</div> </div>
) : ( ) : (
0 0
); )
}, }
}, },
{ {
title: "", title: "",
width: 48, width: 48
}, },
{ {
title: "操作", title: "操作",
...@@ -452,131 +392,111 @@ class KnowledgeBaseList extends React.Component { ...@@ -452,131 +392,111 @@ class KnowledgeBaseList extends React.Component {
width: 160, width: 160,
fixed: "right", fixed: "right",
render: (val, record, index) => { render: (val, record, index) => {
console.log(this.props.categoryId); console.log(this.props.categoryId)
return this.props.categoryId === "0" ? ( return this.props.categoryId === "0" ? (
<div className="operate"> <div className='operate'>
<div <div className='operate__item' onClick={() => this.handleDelete(record)}>
className="operate__item"
onClick={() => this.handleDelete(record)}
>
移出 移出
</div> </div>
</div> </div>
) : ( ) : (
<div className="operate"> <div className='operate'>
<div <div className={index === 0 && current === 1 ? "operate__item disable" : "operate__item"} onClick={() => this.handleUp(index, record)}>
className={
index === 0 && current === 1
? "operate__item disable"
: "operate__item"
}
onClick={() => this.handleUp(index, record)}
>
上移 上移
</div> </div>
<span className="operate__item split"> | </span> <span className='operate__item split'> | </span>
<div <div
className={ className={totalCount === size * (current - 1) + index + 1 ? "operate__item disable" : "operate__item"}
totalCount === size * (current - 1) + index + 1 onClick={() => this.handleDown(record, index)}>
? "operate__item disable"
: "operate__item"
}
onClick={() => this.handleDown(record, index)}
>
下移 下移
</div> </div>
<span className="operate__item split"> | </span> <span className='operate__item split'> | </span>
<div <div className='operate__item' onClick={() => this.handleDelete(record)}>
className="operate__item"
onClick={() => this.handleDelete(record)}
>
移出 移出
</div> </div>
</div> </div>
); )
}, }
}, }
]; ]
return columns; return columns
}; }
handleDelete = (record) => { handleDelete = (record) => {
return confirm({ return confirm({
title: "移出知识", title: "移出知识",
content: "确定将分类中此知识移出吗?", content: "确定将分类中此知识移出吗?",
icon: ( icon: <span className='icon iconfont default-confirm-icon'>&#xe839; </span>,
<span className="icon iconfont default-confirm-icon">&#xe839; </span>
),
okText: "删除", okText: "删除",
okType: "danger", okType: "danger",
cancelText: "取消", cancelText: "取消",
width: 440, width: 440,
height: 188, height: 188,
onOk: () => { onOk: () => {
this.deleteConfirm(record); this.deleteConfirm(record)
}, }
}); })
}; }
// 前往上课数据页面 // 前往上课数据页面
handleLinkToClassData = (item) => { handleLinkToClassData = (item) => {
const { match } = this.props; const { match } = this.props
console.log(item); console.log(item)
localStorage.setItem("WatchData_CourseName", item.name); localStorage.setItem("WatchData_CourseName", item.name)
window.RCHistory.push({ window.RCHistory.push({
// pathname: `${match.url}/course-data?type=${item.courseType}&id=${item.liveCourseId}`, // pathname: `${match.url}/course-data?type=${item.courseType}&id=${item.liveCourseId}`,
pathname: `${match.url}/course-data?type=${item.type}&id=${item.id}`, pathname: `${match.url}/course-data?type=${item.type}&id=${item.id}`
}); })
}; }
deleteConfirm = (item) => { deleteConfirm = (item) => {
const params = { const params = {
id: item.id, id: item.id,
storeId: item.storeId, storeId: item.storeId
}; }
KnowledgeAPI.delKnowledge(params).then((res) => { KnowledgeAPI.delKnowledge(params).then((res) => {
if (res.success) { if (res.success) {
message.success("移出成功"); message.success("移出成功")
this.props.onChange(); this.props.onChange()
this.props.updateCategoryTree(); this.props.updateCategoryTree()
}
})
} }
});
};
onShowSizeChange = (current, size) => { onShowSizeChange = (current, size) => {
if (current == size) { if (current == size) {
return; return
}
let _query = this.props.query
_query.size = size
this.props.onChange(_query)
} }
let _query = this.props.query;
_query.size = size;
this.props.onChange(_query);
};
render() { render() {
const { dataSource = [], totalCount, query, match } = this.props; const { dataSource = [], totalCount, query, match } = this.props
const { current, size } = query; const { current, size } = query
return ( return (
<div className="knowledge-base-list"> <div className='knowledge-base-list'>
<Table <Table
rowKey={(record) => record.id} rowKey={(record) => record.id}
size="middle" size='middle'
dataSource={dataSource} dataSource={dataSource}
columns={this.parseColumns()} columns={this.parseColumns()}
pagination={false} pagination={false}
scroll={{ x: 900 }} scroll={{ x: 900 }}
bordered bordered
className="knowledge-list-table" className='knowledge-list-table'
/> />
<div className="box-footer"> <div className='box-footer'>
{totalCount > 0 && ( {totalCount > 0 && (
<PageControl <PageControl
current={current - 1} current={current - 1}
pageSize={size} pageSize={size}
total={totalCount} total={totalCount}
toPage={(page) => { toPage={(page) => {
const _query = { ...query, current: page + 1 }; const _query = { ...query, current: page + 1 }
this.props.onChange(_query); this.props.onChange(_query)
}} }}
onShowSizeChange={this.onShowSizeChange} onShowSizeChange={this.onShowSizeChange}
/> />
...@@ -586,10 +506,9 @@ class KnowledgeBaseList extends React.Component { ...@@ -586,10 +506,9 @@ class KnowledgeBaseList extends React.Component {
<Route path={`${match.url}/course-data`} component={WatchData} /> <Route path={`${match.url}/course-data`} component={WatchData} />
{this.state.scanFileModal} {this.state.scanFileModal}
</div> </div>
); )
} }
} }
export default withRouter(KnowledgeBaseList); export default withRouter(KnowledgeBaseList)
...@@ -2,25 +2,25 @@ ...@@ -2,25 +2,25 @@
* @Description: * @Description:
* @Author: zangsuyun * @Author: zangsuyun
* @Date: 2021-03-16 10:18:31 * @Date: 2021-03-16 10:18:31
* @LastEditors: wufan * @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-13 16:36:00 * @LastEditTime: 2021-05-28 15:55:14
* @Copyright: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyright: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React from "react"; import React from 'react'
import { Table, message, Input, Button } from "antd"; import { Table, message, Input, Button } from 'antd'
import { PageControl } from "@/components"; import { PageControl } from '@/components'
import CourseService from "@/domains/course-domain/CourseService"; import CourseService from '@/domains/course-domain/CourseService'
import User from "@/common/js/user"; import User from '@/common/js/user'
// import './WatchDataModal.less'; // import './WatchDataModal.less';
import dealTimeDuration from "../../course-manage/utils/dealTimeDuration"; import dealTimeDuration from '../../course-manage/utils/dealTimeDuration'
import KnowledgeAPI from "@/data-source/knowledge/request-api"; import KnowledgeAPI from '@/data-source/knowledge/request-api'
const { Search } = Input; const { Search } = Input
class WatchDataModal extends React.Component { class WatchDataModal extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
dataSource: [], dataSource: [],
size: 10, size: 10,
...@@ -28,227 +28,225 @@ class WatchDataModal extends React.Component { ...@@ -28,227 +28,225 @@ class WatchDataModal extends React.Component {
current: 1, current: 1,
}, },
totalCount: 0, totalCount: 0,
}; }
} }
componentDidMount() { componentDidMount() {
this.handleFetchDataList(); this.handleFetchDataList()
} }
// 获取观看视频数据列表 // 获取观看视频数据列表
handleFetchDataList = () => { handleFetchDataList = () => {
const { query, size, totalCount } = this.state; const { query, size, totalCount } = this.state
const { id, type } = this.props; const { id, type } = this.props
const params = { const params = {
...query, ...query,
size, size,
id, id,
storeId: User.getStoreId(), storeId: User.getStoreId(),
}; }
type === "FOLDER" type === 'FOLDER'
? KnowledgeAPI.queryPageKnowledgeFolderWatchInfo(params).then((res) => { ? KnowledgeAPI.queryPageKnowledgeFolderWatchInfo(params).then((res) => {
const { result = {} } = res; const { result = {} } = res
const { records = [], total = 0 } = result; const { records = [], total = 0 } = result
this.setState({ this.setState({
dataSource: records, dataSource: records,
totalCount: Number(total), totalCount: Number(total),
});
}) })
: KnowledgeAPI.queryPageKnowledgeMediaCourseWatchInfo(params).then( })
(res) => { : KnowledgeAPI.queryPageKnowledgeMediaCourseWatchInfo(params).then((res) => {
const { result = {} } = res; const { result = {} } = res
const { records = [], total = 0 } = result; const { records = [], total = 0 } = result
this.setState({ this.setState({
dataSource: records, dataSource: records,
totalCount: Number(total), totalCount: Number(total),
}); })
})
} }
);
};
handleChangNickname = (value) => { handleChangNickname = (value) => {
const isPhone = (value || "").match(/^\d+$/); const isPhone = (value || '').match(/^\d+$/)
const { query } = this.state; const { query } = this.state
if (isPhone) { if (isPhone) {
query.phone = value; query.phone = value
query.nickName = null; query.nickName = null
} else { } else {
query.nickName = value; query.nickName = value
query.phone = null; query.phone = null
} }
query.current = 1; query.current = 1
this.setState({ this.setState({
query, query,
}); })
}; }
onShowSizeChange = (current, size) => { onShowSizeChange = (current, size) => {
if (current == size) { if (current == size) {
return; return
} }
this.setState( this.setState(
{ {
size, size,
}, },
() => { () => {
this.handleFetchDataList(); this.handleFetchDataList()
}
)
} }
);
};
// 请求表头 // 请求表头
parseColumns = () => { parseColumns = () => {
const { type } = this.props; const { type } = this.props
const columns = [ const columns = [
{ {
title: "观看学员", title: '观看学员',
key: "name", key: 'name',
dataIndex: "name", dataIndex: 'name',
}, },
{ {
title: "手机号", title: '手机号',
key: "phone", key: 'phone',
dataIndex: "phone", dataIndex: 'phone',
}, },
{ {
title: "观看者类型", title: '观看者类型',
key: "userRole", key: 'userRole',
dataIndex: "userRole", dataIndex: 'userRole',
}, },
{ {
title: "首次观看时间", title: '首次观看时间',
key: "firstWatch", key: 'firstWatch',
dataIndex: "firstWatch", dataIndex: 'firstWatch',
render: (val) => { render: (val) => {
return formatDate("YYYY-MM-DD H:i", val); return formatDate('YYYY-MM-DD H:i', val)
}, },
}, },
]; ]
if (type === "VOICE" || type === "PICTURE") { if (type === 'VOICE' || type === 'PICTURE') {
columns.push({ columns.push({
title: "观看时长", title: '观看时长',
key: "watchDuration", key: 'watchDuration',
dataIndex: "watchDuration", dataIndex: 'watchDuration',
render: (val) => { render: (val) => {
return <span>{val ? dealTimeDuration(val) : "00:00:00"}</span>; return <span>{val ? dealTimeDuration(val) : '00:00:00'}</span>
}, },
}); })
} }
if (type === "PICTURE") { if (type === 'PICTURE') {
columns.push({ columns.push({
title: "学习进度", title: '学习进度',
key: "progress", key: 'progress',
dataIndex: "progress", dataIndex: 'progress',
render: (val) => { render: (val) => {
return val === 100 ? "已完成" : <span>{val + "%"}</span>; return val === 100 ? '已完成' : <span>{val + '%'}</span>
}, },
}); })
}
return columns
} }
return columns;
};
handleExportV5 = () => { handleExportV5 = () => {
const { query } = this.state; const { query } = this.state
const { id, type } = this.props; const { id, type } = this.props
const params = { const params = {
...query, ...query,
knowledgeId: id, knowledgeId: id,
storeId: User.getStoreId(), storeId: User.getStoreId(),
}; }
switch (type) { switch (type) {
case "FOLDER": case 'FOLDER':
KnowledgeAPI.exportFolderLearnSync(params).then((res) => { KnowledgeAPI.exportFolderLearnSync(params).then((res) => {
if (res.result) { if (res.result) {
window.open(res.result); const dom = document.getElementById('load-data')
dom.setAttribute('href', res.result)
dom.click()
} else { } else {
message.error("导出失败"); message.error('导出失败')
} }
}); })
break; break
case "VOICE": case 'VOICE':
KnowledgeAPI.exportVideoLearnSync(params).then((res) => { KnowledgeAPI.exportVideoLearnSync(params).then((res) => {
if (res.result) { if (res.result) {
window.open(res.result); const dom = document.getElementById('load-data')
dom.setAttribute('href', res.result)
dom.click()
} else { } else {
message.error("导出失败"); message.error('导出失败')
} }
}); })
break; break
case "PICTURE": case 'PICTURE':
KnowledgeAPI.exportPicLearnSync(params).then((res) => { KnowledgeAPI.exportPicLearnSync(params).then((res) => {
if (res.result) { if (res.result) {
window.open(res.result); const dom = document.getElementById('load-data')
dom.setAttribute('href', res.result)
dom.click()
} else { } else {
message.error("导出失败"); message.error('导出失败')
} }
}); })
break; break
default: default:
break; break
}
} }
};
render() { render() {
const { size, dataSource, totalCount, query } = this.state; const { size, dataSource, totalCount, query } = this.state
return ( return (
<div className="watch-data"> <div className='watch-data'>
<div className="search-container"> <a download id='load-data' style={{ position: 'absolute', left: '-10000px' }}></a>
<div className='search-container'>
<Search <Search
placeholder="搜索学员姓名/手机号" placeholder='搜索学员姓名/手机号'
style={{ width: 200 }} style={{ width: 200 }}
onChange={(e) => { onChange={(e) => {
this.handleChangNickname(e.target.value); this.handleChangNickname(e.target.value)
}} }}
onSearch={() => { onSearch={() => {
this.handleFetchDataList(); this.handleFetchDataList()
}} }}
enterButton={<span className="icon iconfont">&#xe832;</span>} enterButton={<span className='icon iconfont'>&#xe832;</span>}
/> />
</div> </div>
<div className="filter"> <div className='filter'>
<Button <Button
style={{ height: 32, margin: "16px auto 13px 0" }} style={{ height: 32, margin: '16px auto 13px 0' }}
onClick={_.debounce( onClick={_.debounce(
() => { () => {
if (!dataSource.length) { if (!dataSource.length) {
message.warning("暂无数据可导出"); message.warning('暂无数据可导出')
return; return
} }
this.handleExportV5(); this.handleExportV5()
}, },
500, 500,
true true
)} )}>
>
导出 导出
</Button> </Button>
</div> </div>
<div> <div>
<Table <Table rowKey={(record) => record.id} dataSource={dataSource} columns={this.parseColumns()} pagination={false} bordered />
rowKey={(record) => record.id}
dataSource={dataSource}
columns={this.parseColumns()}
pagination={false}
bordered
/>
{dataSource.length > 0 && ( {dataSource.length > 0 && (
<div className="box-footer"> <div className='box-footer'>
<PageControl <PageControl
current={query.current - 1} current={query.current - 1}
pageSize={size} pageSize={size}
total={totalCount} total={totalCount}
size="small" size='small'
toPage={(page) => { toPage={(page) => {
const _query = { ...query, current: page + 1 }; const _query = { ...query, current: page + 1 }
this.setState( this.setState(
{ {
query: _query, query: _query,
}, },
() => { () => {
this.handleFetchDataList(); this.handleFetchDataList()
} }
); )
}} }}
onShowSizeChange={this.onShowSizeChange} onShowSizeChange={this.onShowSizeChange}
/> />
...@@ -256,8 +254,8 @@ class WatchDataModal extends React.Component { ...@@ -256,8 +254,8 @@ class WatchDataModal extends React.Component {
)} )}
</div> </div>
</div> </div>
); )
} }
} }
export default WatchDataModal; export default WatchDataModal
...@@ -2,110 +2,96 @@ ...@@ -2,110 +2,96 @@
* @Description: * @Description:
* @Author: zangsuyun * @Author: zangsuyun
* @Date: 2021-03-12 10:43:10 * @Date: 2021-03-12 10:43:10
* @LastEditors: zangsuyun * @LastEditors: fusanqiasng
* @LastEditTime: 2021-04-10 16:39:30 * @LastEditTime: 2021-05-30 19:46:49
* @Copyright: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyright: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React from "react"; import React from "react"
import KnowledgeBaseFilter from "./components/KnowledgeBaseFilter"; import KnowledgeBaseFilter from "./components/KnowledgeBaseFilter"
import KnowledgeBaseOpt from "./components/KnowledgeBaseOpt"; import KnowledgeBaseOpt from "./components/KnowledgeBaseOpt"
import KnowledgeBaseList from "./components/KnowledgeBaseList"; import KnowledgeBaseList from "./components/KnowledgeBaseList"
import Classification from "./components/Classification"; import Classification from "./components/Classification"
import KnowledgeAPI from "@/data-source/knowledge/request-api"; import KnowledgeAPI from "@/data-source/knowledge/request-api"
import User from "@/common/js/user"; import User from "@/common/js/user"
export default class KnowledgeBase extends React.Component { export default class KnowledgeBase extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
query: { query: {
size: 10, size: 10,
current: 1, current: 1,
storeId: User.getStoreId(), storeId: User.getStoreId(),
categoryId: 0, categoryId: 0
}, },
dataSource: [], // 知识库列表 dataSource: [], // 知识库列表
totalCount: 0, // 知识库数据总条数 totalCount: 0, // 知识库数据总条数
categoryId: '0', categoryId: "0",
updateCategoryFlag: false, updateCategoryFlag: false
}; }
} }
componentWillMount() { componentWillMount() {
// 获取知识库列表 // 获取知识库列表
this.handleFetchScheduleList(); this.handleFetchScheduleList()
// this.getCategoryTree() // this.getCategoryTree()
} }
getSelectedCategoryId = (categoryId) => { getSelectedCategoryId = (categoryId) => {
this.setState({ this.setState({
categoryId, categoryId
}); })
this.handleFetchScheduleList({ categoryId,current:1 }); this.handleFetchScheduleList({ categoryId, current: 1 })
}; }
// 更新分类树 // 更新分类树
updateCategoryTree = () => { updateCategoryTree = () => {
this.setState({ this.setState({
updateCategoryFlag: !this.state.updateCategoryFlag, updateCategoryFlag: !this.state.updateCategoryFlag
}); })
}; }
// 获取知识库列表 // 获取知识库列表
handleFetchScheduleList = (_query = {},flag = true) => { handleFetchScheduleList = (_query = {}, flag = true) => {
const query = { const query = {
...this.state.query, ...this.state.query,
..._query, ..._query
}; }
// 更新请求参数 // 更新请求参数
this.setState({ query }); this.setState({ query })
flag && KnowledgeAPI.queryPageKnowledgeForManager(query).then((res) => { flag &&
KnowledgeAPI.queryPageKnowledgeForManager(query).then((res) => {
// KnowledgeAPI.videoSchedulePage(query).then((res) => { // KnowledgeAPI.videoSchedulePage(query).then((res) => {
const { result = {} } = res || {}; const { result = {} } = res || {}
const { records = [], total = 0 } = result; const { records = [], total = 0 } = result
this.setState({ this.setState({
dataSource: records, dataSource: records,
totalCount: Number(total), totalCount: Number(total)
}); })
}); })
}; }
render() { render() {
const { const { dataSource, totalCount, query, categoryId, updateCategoryFlag } = this.state
dataSource,
totalCount,
query,
categoryId,
updateCategoryFlag,
} = this.state;
return ( return (
<div className="page"> <div className='page'>
<div className="content-header">知识库</div> <div className='content-header'>知识库</div>
<div className="box" style={{ display: "flex" }}> <div className='box' style={{ display: "flex" }}>
{/* 搜索模块 */} {/* 搜索模块 */}
<div className="left" style={{ width: 245 }}> <div className='left' style={{ width: 245 }}>
<Classification <Classification updateCategoryFlag={updateCategoryFlag} categoryId={categoryId} getSelectedCategoryId={this.getSelectedCategoryId} />
updateCategoryFlag={updateCategoryFlag}
categoryId={categoryId}
getSelectedCategoryId={this.getSelectedCategoryId}
/>
</div> </div>
<div <div className='liner' style={{ backgroundColor: "rgb(238, 238, 238)", width: 0.5, margin: "1px 16px 1px 2px" }}></div>
className="liner" <div className='right' style={{ width: "calc(100% - 285px)" }}>
style={{ backgroundColor: "rgb(238, 238, 238)", width: 0.5, margin: "1px 16px 1px 2px" }}
></div>
<div className="right" style={{ width: "calc(100% - 285px)" }}>
<KnowledgeBaseFilter onChange={this.handleFetchScheduleList} /> <KnowledgeBaseFilter onChange={this.handleFetchScheduleList} />
{/* 操作模块 */} {/* 操作模块 */}
{categoryId != 0 && <KnowledgeBaseOpt {categoryId !== "0" && (
onChange={this.handleFetchScheduleList} <KnowledgeBaseOpt onChange={this.handleFetchScheduleList} updateCategoryTree={this.updateCategoryTree} categoryId={categoryId} />
updateCategoryTree={this.updateCategoryTree} )}
categoryId={categoryId}
/>}
{/* 知识库列表模块 */} {/* 知识库列表模块 */}
<KnowledgeBaseList <KnowledgeBaseList
...@@ -119,6 +105,6 @@ export default class KnowledgeBase extends React.Component { ...@@ -119,6 +105,6 @@ export default class KnowledgeBase extends React.Component {
</div> </div>
</div> </div>
</div> </div>
); )
} }
} }
...@@ -2,107 +2,137 @@ ...@@ -2,107 +2,137 @@
* @Description: * @Description:
* @Author: zangsuyun * @Author: zangsuyun
* @Date: 2021-03-13 09:54:26 * @Date: 2021-03-13 09:54:26
* @LastEditors: zangsuyun * @LastEditors: fusanqiasng
* @LastEditTime: 2021-04-07 11:56:10 * @LastEditTime: 2021-05-28 20:19:06
* @Copyright: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyright: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React from "react"; import React from 'react'
import { import { Row, Modal, Button, message, Radio, Table, Input, Tabs, Tooltip, TreeSelect } from 'antd'
Row, import { PageControl } from '@/components'
Modal, import TableSelectedData from '@/components/TableSelectedData'
Button, import KnowledgeAPI from '@/data-source/knowledge/request-api'
message, import AidToolService from '@/domains/aid-tool-domain/AidToolService'
Table, import User from '@/common/js/user'
Select, import './LiveList.less'
Radio,
Input, import _ from 'underscore'
Tabs, import dealTimeDuration from '../../course-manage/utils/dealTimeDuration'
Tooltip,
TreeSelect, const { Search } = Input
} from "antd"; const { TabPane } = Tabs
import { PageControl } from "@/components";
import TableSelectedData from "@/components/TableSelectedData";
import CourseService from "@/domains/course-domain/CourseService";
import KnowledgeAPI from "@/data-source/knowledge/request-api";
import User from "@/common/js/user";
import "./LiveList.less";
import _ from "underscore";
import dealTimeDuration from "../../course-manage/utils/dealTimeDuration";
const { Search } = Input;
const { TabPane } = Tabs;
const courseStateShow = { const courseStateShow = {
UN_START: { UN_START: {
code: 1, code: 1,
title: "待开课", title: '待开课',
color: "#FFB714", color: '#2966FF',
}, },
STARTING: { STARTING: {
code: 2, code: 2,
title: "上课中", title: '上课中',
color: "#238FFF", color: '#238FFF',
}, },
FINISH: { FINISH: {
code: 3, code: 3,
title: "已完成", title: '已完成',
color: "#3BBDAA", color: '#3BBDAA',
}, },
EXPIRED: { EXPIRED: {
code: 4, code: 4,
title: "未成功开课", title: '未成功开课',
color: "#999", color: '#999',
}, },
}; }
class AddCourse extends React.Component { class AddCourse extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
liveDataSource: [], liveDataSource: [],
liveSize: 10, liveSize: 10,
liveQuery: { liveQuery: {
current: 1, current: 1,
excludeUsed: true, excludeUsed: true,
courseType: "LIVE", courseType: 'LIVE',
storeId: User.getStoreId(), storeId: User.getStoreId(),
toRefKnowledgeCategoryId: this.props.categoryId, toRefKnowledgeCategoryId: this.props.categoryId,
}, },
liveTotalCount: 0, liveTotalCount: 0,
selectLive: [], //弹窗内已选择的直播课程 selectLive: [], //弹窗内已选择的直播课程
videoDataSource: [], videoCourseDivision: 'internal',
videoSize: 10, videoDataSource: {
external: [],
internal: [],
},
videoSize: {
external: 10,
internal: 10,
},
videoSearchDefalt: {
external: {
categoryId: '',
courseName: '',
},
internal: {
categoryId: '',
courseName: '',
},
},
videoQuery: { videoQuery: {
external: {
categoryId: '',
courseName: '',
current: 1, current: 1,
courseType: "VOICE", courseType: 'VOICE',
excludeUsed: true, excludeUsed: true,
storeId: User.getStoreId(), storeId: User.getStoreId(),
toRefKnowledgeCategoryId: this.props.categoryId, toRefKnowledgeCategoryId: this.props.categoryId,
}, },
videoTotalCount: 0, internal: {
selectVideo: [], //弹窗内已选择的视频课程 categoryId: '',
courseName: '',
current: 1,
courseType: 'VOICE',
excludeUsed: true,
storeId: User.getStoreId(),
toRefKnowledgeCategoryId: this.props.categoryId,
},
},
videoTotalCount: {
external: 0,
internal: 0,
},
selectVideo: {
external: [],
internal: [],
}, //弹窗内已选择的视频课程
currentVideoCourseListData: {
external: [],
internal: [],
}, //页面中已关联的视频课程
pictureDataSource: [], pictureDataSource: [],
pictureSize: 10, pictureSize: 10,
pictureQuery: { pictureQuery: {
current: 1, current: 1,
excludeUsed: true, excludeUsed: true,
courseType: "PICTURE", courseType: 'PICTURE',
storeId: User.getStoreId(), storeId: User.getStoreId(),
toRefKnowledgeCategoryId: this.props.categoryId, toRefKnowledgeCategoryId: this.props.categoryId,
}, },
pictureTotalCount: 0, pictureTotalCount: 0,
selectPicture: [], //弹窗内已选择的图文课程 selectPicture: [], //弹窗内已选择的图文课程
categoryList: [], categoryList: [], //内部分类列表
}; categoryListExternal: [], //外部分类列表
}
} }
componentDidMount() { componentDidMount() {
this.handleFetchLiveList(); this.handleFetchLiveList()
this.handleFetchVideoList(); this.handleFetchVideoList()
this.handleFetchPictureList(); this.handleFetchPictureList()
this.queryCategoryTree(); this.queryCategoryTree()
} }
// 查询分类树 // 查询分类树
...@@ -110,135 +140,155 @@ class AddCourse extends React.Component { ...@@ -110,135 +140,155 @@ class AddCourse extends React.Component {
let query = { let query = {
storeId: User.getStoreId(), storeId: User.getStoreId(),
withCount: false, withCount: false,
}; }
let queryInternal = {
bizType: 'QUESTION',
source: 2,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
count: false,
}
AidToolService.queryExternalCategoryTree(queryInternal).then((res) => {
const { categoryList = [] } = res.result
this.setState({
categoryListExternal: this.renderTreeNodes(categoryList),
})
})
KnowledgeAPI.getCategoryTree(query).then((res) => { KnowledgeAPI.getCategoryTree(query).then((res) => {
const { categoryList = [] } = res.result; const { categoryList = [] } = res.result
this.setState({ this.setState({
categoryList: this.renderTreeNodes(categoryList), categoryList: this.renderTreeNodes(categoryList),
}); })
}); })
}; }
renderTreeNodes = (data) => { renderTreeNodes = (data) => {
let newTreeData = data.map((item) => { let newTreeData = data.map((item) => {
item.title = ( item.title = (
<span> <span>
<span className="icon iconfont" style={{ color: "#FBD140" }}> <span className='icon iconfont' style={{ color: '#FBD140' }}>
&#xe7f1;&nbsp; &#xe7f1;&nbsp;
</span> </span>
{item.categoryName} {item.categoryName}
</span> </span>
); )
item.key = item.id; item.key = item.id
if (item.sonCategoryList) { if (item.sonCategoryList) {
item.children = this.renderTreeNodes(item.sonCategoryList); item.children = this.renderTreeNodes(item.sonCategoryList)
}
return item
})
return newTreeData
} }
return item;
});
return newTreeData;
};
// 获取直播课列表 // 获取直播课列表
handleFetchLiveList = () => { handleFetchLiveList = () => {
const { liveQuery, liveSize } = this.state; const { liveQuery, liveSize } = this.state
const params = { const params = {
...liveQuery, ...liveQuery,
size: liveSize, size: liveSize,
}; }
// CourseService.getLiveCloudCoursePage(params).then((res) => { // CourseService.getLiveCloudCoursePage(params).then((res) => {
KnowledgeAPI.knowledgeLiveCoursePage(params).then((res) => { KnowledgeAPI.knowledgeLiveCoursePage(params).then((res) => {
const { result = {} } = res; const { result = {} } = res
const { records = [], total = 0 } = result; const { records = [], total = 0 } = result
this.setState({ this.setState({
liveDataSource: records, liveDataSource: records,
liveTotalCount: Number(total), liveTotalCount: Number(total),
}); })
}); })
}; }
// 获取视频课列表 // 获取视频课列表
handleFetchVideoList = () => { handleFetchVideoList = () => {
const { videoQuery, videoSize } = this.state; const { videoQuery, videoSize, videoCourseDivision, videoDataSource, videoTotalCount } = this.state
const params = { const params = {
...videoQuery, ...videoQuery[videoCourseDivision],
size: videoSize, size: videoSize[videoCourseDivision],
}; courseDivision: videoCourseDivision === 'internal' ? 'INTERNAL' : 'EXTERNAL',
}
// CourseService.videoSchedulePage(query).then((res) => { // CourseService.videoSchedulePage(query).then((res) => {
KnowledgeAPI.knowledgeMediaCoursePage(params).then((res) => { KnowledgeAPI.knowledgeMediaCoursePage(params).then((res) => {
const { result = {} } = res || {}; const { result = {} } = res || {}
const { records = [], total = 0 } = result; const { records = [], total = 0 } = result
this.setState({ this.setState({
videoDataSource: records, videoDataSource: {
videoTotalCount: Number(total), ...videoDataSource,
}); [videoCourseDivision]: records,
}); },
}; videoTotalCount: {
...videoTotalCount,
[videoCourseDivision]: Number(total),
},
})
})
}
// 获取图文课列表 // 获取图文课列表
handleFetchPictureList = () => { handleFetchPictureList = () => {
const { pictureQuery, pictureSize } = this.state; const { pictureQuery, pictureSize } = this.state
const params = { const params = {
...pictureQuery, ...pictureQuery,
size: pictureSize, size: pictureSize,
}; }
// CourseService.pictureSchedulePage(query).then((res) => { // CourseService.pictureSchedulePage(query).then((res) => {
KnowledgeAPI.knowledgeMediaCoursePage(params).then((res) => { KnowledgeAPI.knowledgeMediaCoursePage(params).then((res) => {
const { result = {} } = res || {}; const { result = {} } = res || {}
const { records = [], total = 0 } = result; const { records = [], total = 0 } = result
this.setState({ this.setState({
pictureDataSource: records, pictureDataSource: records,
pictureTotalCount: Number(total), pictureTotalCount: Number(total),
}); })
}); })
}; }
onShowLiveSizeChange = (current, size) => { onShowLiveSizeChange = (current, size) => {
if (current == size) { if (current === size) {
return; return
} }
this.setState( this.setState(
{ {
liveSize: size, liveSize: size,
}, },
() => { () => {
this.handleFetchLiveList(); this.handleFetchLiveList()
}
)
} }
);
};
onShowVideoSizeChange = (current, size) => { onShowVideoSizeChange = (current, size) => {
if (current == size) { if (current === size) {
return; return
} }
this.setState( this.setState(
{ {
videoSize: size, videoSize: size,
}, },
() => { () => {
this.handleFetchVideoList(); this.handleFetchVideoList()
}
)
} }
);
};
onShowPictureSizeChange = (current, size) => { onShowPictureSizeChange = (current, size) => {
if (current == size) { if (current === size) {
return; return
} }
this.setState( this.setState(
{ {
pictureSize: size, pictureSize: size,
}, },
() => { () => {
this.handleFetchPictureList(); this.handleFetchPictureList()
}
)
} }
);
};
liveColumns = () => { liveColumns = () => {
const columns = [ const columns = [
...@@ -246,177 +296,180 @@ class AddCourse extends React.Component { ...@@ -246,177 +296,180 @@ class AddCourse extends React.Component {
title: ( title: (
<span> <span>
<span>课程信息</span> <span>课程信息</span>
<Tooltip <Tooltip title={<div>已加入该分类的课程不支持重复选择,因此不显示。</div>}>
title={<div>已加入该分类的课程不支持重复选择,因此不显示。</div>}
>
<i <i
className="icon iconfont" className='icon iconfont'
style={{ style={{
marginLeft: "5px", marginLeft: '5px',
cursor: "pointer", cursor: 'pointer',
color: "#bfbfbf", color: '#bfbfbf',
fontSize: "14px", fontSize: '14px',
}} }}>
>
&#xe61d; &#xe61d;
</i> </i>
</Tooltip> </Tooltip>
</span> </span>
), ),
width: 371, width: 371,
key: "course", key: 'course',
dataIndex: "courseName", dataIndex: 'courseName',
render: (val, record) => { render: (val, record) => {
let hasCover = false; let hasCover = false
return ( return (
<div className="record__item"> <div className='record__item'>
{record.courseMediaVOS.map((item, index) => { {record.courseMediaVOS.map((item) => {
if (item.contentType === "COVER") { if (item.contentType === 'COVER') {
hasCover = true; hasCover = true
return <img className="course-cover" src={item.mediaUrl} />; return <img className='course-cover' src={item.mediaUrl} alt='' />
} }
})} })}
{!hasCover && ( {!hasCover && <img className='course-cover' src={'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'} alt='' />}
<img
className="course-cover"
src={"https://image.xiaomaiketang.com/xm/YNfi45JwFA.png"}
/>
)}
<div> <div>
{record.courseName.length > 17 ? ( <Choose>
<When condition={record.courseName.length > 17}>
<Tooltip title={record.courseName}> <Tooltip title={record.courseName}>
<div className="course-name">{record.courseName}</div> <div className='course-name'>{record.courseName}</div>
</Tooltip> </Tooltip>
) : ( </When>
<div className="course-name">{record.courseName}</div> <Otherwise>
)} <div className='course-name'>{record.courseName}</div>
</Otherwise>
</Choose>
<div> <div>
<span <span
className="course-status" className='course-status'
style={{ style={{
color: courseStateShow[record.courseState].color, color: courseStateShow[record.courseState].color,
border: `1px solid ${ border: `1px solid ${courseStateShow[record.courseState].color}`,
courseStateShow[record.courseState].color }}>
}`,
}}
>
{courseStateShow[record.courseState].title} {courseStateShow[record.courseState].title}
</span> </span>
</div> </div>
</div> </div>
</div> </div>
); )
}, },
}, },
{ {
title: "上课时间", title: '上课时间',
width: 110, width: 110,
key: "couseCatalog", key: 'couseCatalog',
dataIndex: "couseCatalog", dataIndex: 'couseCatalog',
render: (val, item) => { render: (val, item) => {
return ( return (
<span className="course-time"> <span className='course-time'>
{formatDate("YYYY-MM-DD", parseInt(item.startTime))} <br></br> {formatDate('YYYY-MM-DD', parseInt(item.startTime))} <br></br>
{formatDate("H:i", parseInt(item.startTime))}~ {formatDate('H:i', parseInt(item.startTime))}~{formatDate('H:i', parseInt(item.endTime))}
{formatDate("H:i", parseInt(item.endTime))}
</span> </span>
); )
}, },
}, },
{ {
title: "课程分类", title: '课程分类',
// width: "10%", // width: "10%",
key: "couseCatalog", key: 'couseCatalog',
dataIndex: "couseCatalog", dataIndex: 'couseCatalog',
render: (val, record) => { render: (val, record) => {
return ( return (
<div className="categoryName"> <div className='categoryName'>
{record.categoryOneName} {record.categoryOneName}
{record.categoryTwoName ? `-${record.categoryTwoName}` : ""} {record.categoryTwoName ? `-${record.categoryTwoName}` : ''}
</div> </div>
); )
}, },
}, },
]; ]
return columns; return columns
}; }
videoColumns = () => { videoColumns = () => {
const { videoCourseDivision } = this.state
const columns = [ const columns = [
{ {
title: ( title: (
<span> <span>
<span>课程信息</span> <span>课程信息</span>
<Tooltip <Tooltip title={<div>已加入该分类的课程不支持重复选择,因此不显示。</div>}>
title={<div>已加入该分类的课程不支持重复选择,因此不显示。</div>}
>
<i <i
className="icon iconfont" className='icon iconfont'
style={{ style={{
marginLeft: "5px", marginLeft: '5px',
cursor: "pointer", cursor: 'pointer',
color: "#bfbfbf", color: '#bfbfbf',
fontSize: "14px", fontSize: '14px',
}} }}>
>
&#xe61d; &#xe61d;
</i> </i>
</Tooltip> </Tooltip>
</span> </span>
), ),
key: "scheduleName", key: 'scheduleName',
dataIndex: "scheduleName", dataIndex: 'scheduleName',
width: 371, width: 300,
render: (val, record) => { render: (val, record) => {
const { coverUrl, mediaCourseUrl } = record; const { coverUrl, mediaCourseUrl } = record
return ( return (
<div className="record__item"> <div className='record__item'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */} {/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
<img <img
className="course-cover" className='course-cover'
src={ src={
coverUrl || coverUrl ||
`${mediaCourseUrl}?x-oss-process=video/snapshot,t_0,m_fast` (videoCourseDivision === 'internal'
? `${mediaCourseUrl}?x-oss-process=video/snapshot,t_0,m_fast`
: 'https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png')
} }
alt=''
/> />
{record.courseName.length > 25 ? ( <Choose>
<When condition={record.courseName.length > 25}>
<Tooltip title={record.courseName}> <Tooltip title={record.courseName}>
<div className="course-name">{record.courseName}</div> <div className='course-name'>{record.courseName}</div>
</Tooltip> </Tooltip>
) : ( </When>
<div className="course-name">{record.courseName}</div> <Otherwise>
)} <div className='course-name'>{record.courseName}</div>
</Otherwise>
</Choose>
</div> </div>
); )
}, },
}, },
{ {
title: "课程时长", title: '课程时长',
key: "videoDuration", key: 'videoDuration',
width: 80, width: 80,
dataIndex: "videoDuration", dataIndex: 'videoDuration',
render: (text, item) => { render: (text, item) => {
return <span>{text ? dealTimeDuration(text) : "-"}</span>; return <span>{text ? dealTimeDuration(text) : '-'}</span>
}, },
}, },
{ {
title: "课程分类", title: '课程分类',
key: "categoryName", key: 'categoryName',
dataIndex: "categoryName", dataIndex: 'categoryName',
render: (val, record) => { render: (val, record) => {
return ( return (
<div className="record__item"> <Choose>
<When condition={videoCourseDivision === 'internal'}>
<div className='record__item'>
{record.categoryOneName} {record.categoryOneName}
{record.categoryTwoName ? `-${record.categoryTwoName}` : ""} {record.categoryTwoName ? `-${record.categoryTwoName}` : ''}
</div> </div>
); </When>
<Otherwise>
<div className='record__item'>{record.categorySonName}</div>
</Otherwise>
</Choose>
)
}, },
}, },
]; ]
return columns; return columns
}; }
pictureColumns = () => { pictureColumns = () => {
const columns = [ const columns = [
...@@ -424,208 +477,240 @@ class AddCourse extends React.Component { ...@@ -424,208 +477,240 @@ class AddCourse extends React.Component {
title: ( title: (
<span> <span>
<span>课程信息</span> <span>课程信息</span>
<Tooltip <Tooltip title={<div>已加入该分类的课程不支持重复选择,因此不显示。</div>}>
title={<div>已加入该分类的课程不支持重复选择,因此不显示。</div>}
>
<i <i
className="icon iconfont" className='icon iconfont'
style={{ style={{
marginLeft: "5px", marginLeft: '5px',
cursor: "pointer", cursor: 'pointer',
color: "#bfbfbf", color: '#bfbfbf',
fontSize: "14px", fontSize: '14px',
}} }}>
>
&#xe61d; &#xe61d;
</i> </i>
</Tooltip> </Tooltip>
</span> </span>
), ),
key: "scheduleName", key: 'scheduleName',
dataIndex: "scheduleName", dataIndex: 'scheduleName',
width: 371, width: 371,
render: (val, record) => { render: (val, record) => {
const { coverUrl } = record; const { coverUrl } = record
return ( return (
<div className="record__item"> <div className='record__item'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */} {/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
<img <img className='course-cover' src={coverUrl || 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'} alt='' />
className="course-cover" <Choose>
src={ <When condition={record.courseName.length > 25}>
coverUrl ||
"https://image.xiaomaiketang.com/xm/YNfi45JwFA.png"
}
/>
{record.courseName.length > 25 ? (
<Tooltip title={record.courseName}> <Tooltip title={record.courseName}>
<div className="course-name">{record.courseName}</div> <div className='course-name'>{record.courseName}</div>
</Tooltip> </Tooltip>
) : ( </When>
<div className="course-name">{record.courseName}</div> <Otherwise>
)} <div className='course-name'>{record.courseName}</div>
</Otherwise>
</Choose>
</div> </div>
); )
}, },
}, },
{ {
title: "课程分类", title: '课程分类',
key: "categoryName", key: 'categoryName',
dataIndex: "categoryName", dataIndex: 'categoryName',
render: (val, record) => { render: (val, record) => {
return ( return (
<div className="record__item"> <div className='record__item'>
{record.categoryOneName} {record.categoryOneName}
{record.categoryTwoName ? `-${record.categoryTwoName}` : ""} {record.categoryTwoName ? `-${record.categoryTwoName}` : ''}
</div> </div>
); )
}, },
}, },
]; ]
return columns; return columns
}; }
selectLiveList = (record, selected) => { selectLiveList = (record, selected) => {
let { selectLive } = this.state; let { selectLive } = this.state
let _list = []; let _list = []
if ( if (selected || !_.find(selectLive, (item) => item.liveCourseId === record.liveCourseId)) {
selected || _list = _.uniq(selectLive.concat([record]), false, (item) => item.liveCourseId)
!_.find(selectLive, (item) => item.liveCourseId == record.liveCourseId)
) {
_list = _.uniq(
selectLive.concat([record]),
false,
(item) => item.liveCourseId
);
} else { } else {
_list = _.reject( _list = _.reject(selectLive, (item) => item.liveCourseId === record.liveCourseId)
selectLive, }
(item) => item.liveCourseId === record.liveCourseId this.setState({ selectLive: _list })
);
} }
this.setState({ selectLive: _list });
};
selectVideoList = (record, selected) => { selectVideoList = (record, selected) => {
console.log(record); const { selectVideo, videoCourseDivision } = this.state
let { selectVideo } = this.state;
let _list = []; let { [videoCourseDivision]: selectList } = selectVideo
if (selected || !_.find(selectVideo, (item) => item.id == record.id)) {
_list = _.uniq(selectVideo.concat([record]), false, (item) => item.id); let _list = []
if (selected || !_.find(selectList, (item) => item.id === record.id)) {
_list = _.uniq(selectList.concat([record]), false, (item) => item.id)
} else { } else {
_list = _.reject(selectVideo, (item) => item.id === record.id); _list = _.reject(selectList, (item) => item.id === record.id)
}
this.setState({
selectVideo: {
...selectVideo,
[videoCourseDivision]: _list,
},
})
} }
this.setState({ selectVideo: _list });
};
selectPictureList = (record, selected) => { selectPictureList = (record, selected) => {
console.log(record); console.log(record)
let { selectPicture } = this.state; let { selectPicture } = this.state
let _list = []; let _list = []
if (selected || !_.find(selectPicture, (item) => item.id == record.id)) { if (selected || !_.find(selectPicture, (item) => item.id == record.id)) {
_list = _.uniq(selectPicture.concat([record]), false, (item) => item.id); _list = _.uniq(selectPicture.concat([record]), false, (item) => item.id)
} else { } else {
_list = _.reject(selectPicture, (item) => item.id === record.id); _list = _.reject(selectPicture, (item) => item.id === record.id)
}
this.setState({ selectPicture: _list })
} }
this.setState({ selectPicture: _list });
};
callback(key) { callback(key) {
console.log(key); console.log(key)
} }
handleChangVideoFilter = (key, value) => { handleChangVideoFilter = (key, value) => {
const { videoQuery } = this.state; const { videoQuery, videoCourseDivision, videoSearchDefalt } = this.state
videoQuery[key] = value; videoQuery[videoCourseDivision][key] = value
videoQuery.current = 1; videoSearchDefalt[videoCourseDivision][key] = value
videoQuery[videoCourseDivision].current = 1
this.setState( this.setState(
{ {
videoQuery, videoQuery,
videoSearchDefalt,
}, },
() => { () => {
this.handleFetchVideoList(); this.handleFetchVideoList()
}
)
}
handleChangVideoCourseName = (e) => {
const { videoSearchDefalt, videoCourseDivision } = this.state
videoSearchDefalt[videoCourseDivision].courseName = e.target.value
this.setState({
videoSearchDefalt,
})
} }
);
};
handleChangLiveFilter = (key, value) => { handleChangLiveFilter = (key, value) => {
const { liveQuery } = this.state; const { liveQuery } = this.state
liveQuery[key] = value; liveQuery[key] = value
liveQuery.current = 1; liveQuery.current = 1
this.setState( this.setState(
{ {
liveQuery, liveQuery,
}, },
() => { () => {
this.handleFetchLiveList(); this.handleFetchLiveList()
}
)
} }
);
};
handleChangPictureFilter = (key, value) => { handleChangPictureFilter = (key, value) => {
const { pictureQuery } = this.state; const { pictureQuery } = this.state
pictureQuery[key] = value; pictureQuery[key] = value
pictureQuery.current = 1; pictureQuery.current = 1
this.setState( this.setState(
{ {
pictureQuery, pictureQuery,
}, },
() => { () => {
this.handleFetchPictureList(); this.handleFetchPictureList()
}
)
} }
);
};
handAddCourse = () => { handAddCourse = () => {
const { selectVideo, selectLive, selectPicture } = this.state; const { selectVideo, selectLive, selectPicture } = this.state
const batchAddList = []; const batchAddList = []
if (selectVideo.length) { if (selectVideo.external.length) {
batchAddList.push({
categoryId: this.props.categoryId,
refIds: _.pluck(selectVideo.external, 'id'),
storeId: User.getStoreId(),
type: 'VOICE',
createId: User.getStoreUserId(),
})
}
if (selectVideo.internal.length) {
batchAddList.push({ batchAddList.push({
categoryId: this.props.categoryId, categoryId: this.props.categoryId,
refIds: _.pluck(selectVideo, "id"), refIds: _.pluck(selectVideo.internal, 'id'),
storeId: User.getStoreId(), storeId: User.getStoreId(),
type: "VOICE", type: 'VOICE',
createId: User.getStoreUserId(), createId: User.getStoreUserId(),
}); })
} }
if (selectLive.length) { if (selectLive.length) {
batchAddList.push({ batchAddList.push({
categoryId: this.props.categoryId, categoryId: this.props.categoryId,
refIds: _.pluck(selectLive, "liveCourseId"), refIds: _.pluck(selectLive, 'liveCourseId'),
storeId: User.getStoreId(), storeId: User.getStoreId(),
type: "LIVE", type: 'LIVE',
createId: User.getStoreUserId(), createId: User.getStoreUserId(),
}); })
} }
if (selectPicture.length) { if (selectPicture.length) {
batchAddList.push({ batchAddList.push({
categoryId: this.props.categoryId, categoryId: this.props.categoryId,
refIds: _.pluck(selectPicture, "id"), refIds: _.pluck(selectPicture, 'id'),
storeId: User.getStoreId(), storeId: User.getStoreId(),
type: "PICTURE", type: 'PICTURE',
createId: User.getStoreUserId(), createId: User.getStoreUserId(),
}); })
} }
KnowledgeAPI.addDifTypeKnowledge({ batchAddList }).then(({ success }) => { KnowledgeAPI.addDifTypeKnowledge({ batchAddList }).then(({ success }) => {
if (success) { if (success) {
message.success("新增成功"); message.success('新增成功')
this.props.onClose(); this.props.onClose()
this.props.onChange(); this.props.onChange()
this.props.updateCategoryTree(); this.props.updateCategoryTree()
}
})
}
videoCourseDivisionChange = (e) => {
const { videoQuery, videoSearchDefalt } = this.state
this.setState(
{
videoCourseDivision: e.target.value,
videoSearchDefalt: {
...videoSearchDefalt,
[e.target.value]: {
courseName: videoQuery[e.target.value].courseName,
categoryId: videoQuery[e.target.value].categoryId,
},
},
},
() => {
this.handleFetchVideoList()
}
)
} }
});
};
renderFooter = () => { renderFooter = () => {
const {selectVideo,selectPicture,selectLive} =this.state const { selectVideo, selectPicture, selectLive } = this.state
return ( return (
<div> <div>
<Button onClick={this.props.onClose}>取消</Button> <Button onClick={this.props.onClose}>取消</Button>
<Button disabled={!(selectLive.length || selectVideo.length || selectPicture.length)} type="primary" onClick={this.handAddCourse}> <Button
disabled={!(selectLive.length || selectVideo.external.length || selectVideo.internal.length || selectPicture.length)}
type='primary'
onClick={this.handAddCourse}>
确定 确定
</Button> </Button>
</div> </div>
); )
}; }
render() { render() {
const { const {
...@@ -638,325 +723,311 @@ class AddCourse extends React.Component { ...@@ -638,325 +723,311 @@ class AddCourse extends React.Component {
videoSize, videoSize,
videoQuery, videoQuery,
videoTotalCount, videoTotalCount,
videoCourseDivision,
selectVideo, selectVideo,
pictureDataSource, pictureDataSource,
pictureSize, pictureSize,
pictureQuery, pictureQuery,
pictureTotalCount, pictureTotalCount,
selectPicture, selectPicture,
videoSearchDefalt,
categoryList, categoryList,
} = this.state; categoryListExternal,
} = this.state
const LiveSelection = { const LiveSelection = {
selectedRowKeys: _.pluck(selectLive, "liveCourseId"), selectedRowKeys: _.pluck(selectLive, 'liveCourseId'),
onSelect: this.selectLiveList, onSelect: this.selectLiveList,
onSelectAll: (selected, _selectedRows, changeRows) => { onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = []; let _list = []
if (selected) { if (selected) {
_list = _.uniq( _list = _.uniq(selectLive.concat(changeRows), false, (item) => item.liveCourseId)
selectLive.concat(changeRows),
false,
(item) => item.liveCourseId
);
} else { } else {
_list = _.reject(selectLive, (item) => _list = _.reject(selectLive, (item) => _.find(changeRows, (data) => data.liveCourseId === item.liveCourseId))
_.find(
changeRows,
(data) => data.liveCourseId === item.liveCourseId
)
);
} }
this.setState({ selectLive: _list }); this.setState({ selectLive: _list })
}, },
}; }
const VideoSelection = { const VideoSelection = {
selectedRowKeys: _.pluck(selectVideo, "id"), selectedRowKeys: _.pluck(selectVideo[videoCourseDivision], 'id'),
onSelect: this.selectVideoList, onSelect: this.selectVideoList,
onSelectAll: (selected, _selectedRows, changeRows) => { onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = []; let _list = []
if (selected) { if (selected) {
_list = _.uniq( _list = _.uniq(selectVideo[videoCourseDivision].concat(changeRows), false, (item) => item.id)
selectVideo.concat(changeRows),
false,
(item) => item.id
);
} else { } else {
_list = _.reject(selectVideo, (item) => _list = _.reject(selectVideo[videoCourseDivision], (item) => _.find(changeRows, (data) => data.id === item.id))
_.find(changeRows, (data) => data.id === item.id)
);
} }
this.setState({ selectVideo: _list }); this.setState({
selectVideo: {
...selectVideo,
[videoCourseDivision]: _list,
}, },
}; })
},
}
const PictureSelection = { const PictureSelection = {
selectedRowKeys: _.pluck(selectPicture, "id"), selectedRowKeys: _.pluck(selectPicture, 'id'),
onSelect: this.selectPictureList, onSelect: this.selectPictureList,
onSelectAll: (selected, _selectedRows, changeRows) => { onSelectAll: (selected, _selectedRows, changeRows) => {
console.log(changeRows); let _list = []
let _list = [];
if (selected) { if (selected) {
_list = _.uniq( _list = _.uniq(selectPicture.concat(changeRows), false, (item) => item.id)
selectPicture.concat(changeRows),
false,
(item) => item.id
);
} else { } else {
_list = _.reject(selectPicture, (item) => _list = _.reject(selectPicture, (item) => _.find(changeRows, (data) => data.id === item.id))
_.find(changeRows, (data) => data.id === item.id)
);
} }
this.setState({ selectPicture: _list }); this.setState({ selectPicture: _list })
}, },
}; }
return ( return (
<Modal <Modal visible={true} width={800} title='新增课程' footer={this.renderFooter()} onCancel={this.props.onClose} className='add-course-modal'>
visible={true} <Tabs defaultActiveKey='LIVE'>
width={720} <TabPane tab='直播课' key='LIVE'>
title="新增课程" <div className='live-list'>
footer={this.renderFooter()}
onCancel={this.props.onClose}
className="add-course-modal"
>
<Tabs defaultActiveKey="VIDEO" onChange={this.callback} centered>
<TabPane tab="视频课" key="VIDEO">
<div className="live-list">
<div> <div>
<Row type="flex" justify="space-between" align="top"> <Row type='flex' justify='space-between' align='top'>
<div> <div>
<span style={{ lineHeight: "32px" }}>课程名称:</span> <span style={{ lineHeight: '32px' }}>课程名称:</span>
<Search <Search
// value={courseName} style={{ width: 'calc(100% - 75px)' }}
style={{ width: "calc(100% - 75px)" }} placeholder='搜索课程名称'
placeholder="搜索课程名称"
onSearch={(value) => { onSearch={(value) => {
this.handleChangVideoFilter("courseName", value); this.handleChangLiveFilter('courseName', value)
}} }}
enterButton={ enterButton={<span className='icon iconfont'>&#xe832;</span>}
<span className="icon iconfont">&#xe832;</span>
}
/> />
</div> </div>
<div style={{ width: "50%" }}> <div style={{ width: '50%' }}>
<span className="shelf-status">课程分类:</span> <span className='shelf-status'>课程分类:</span>
<TreeSelect <TreeSelect
style={{ width: "calc(100% - 75px)" }} treeNodeFilterProp='categoryName'
dropdownStyle={{ maxHeight: 400, overflow: "auto" }} showSearch
style={{ width: 'calc(100% - 75px)' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={categoryList} treeData={categoryList}
placeholder="请选择课程类型" placeholder='请选择课程类型'
allowClear allowClear
onChange={(value) => { onChange={(value) => {
this.handleChangVideoFilter("categoryId", value); this.handleChangLiveFilter('categoryId', value)
}} }}
/> />
</div> </div>
</Row> </Row>
</div> </div>
<TableSelectedData <TableSelectedData
selectedNum={ selectedNum={selectVideo.internal.length + selectVideo.external.length + selectLive.length + selectPicture.length}
selectVideo.length + selectLive.length + selectPicture.length
}
clearSelectedData={() => { clearSelectedData={() => {
this.setState({ this.setState({
selectVideo: [], selectVideo: {
internal: [],
external: [],
},
selectLive: [], selectLive: [],
selectPicture: [], selectPicture: [],
}); })
}} }}
/> />
<Table <Table
rowKey={(record) => record.id}
dataSource={videoDataSource}
columns={this.videoColumns()}
size="middle"
rowSelection={VideoSelection}
pagination={false}
bordered bordered
className="video-list-table" size='middle'
style={{ maxHeight: 359, overflow: "scroll" }} pagination={false}
columns={this.liveColumns()}
rowSelection={LiveSelection}
// loading={loading}
dataSource={liveDataSource}
style={{ maxHeight: 359, overflow: 'scroll' }}
rowKey={(row) => row.liveCourseId}
/> />
{liveTotalCount > 0 && (
<div className="box-footer"> <div className='box-footer'>
{videoTotalCount > 0 && (
<PageControl <PageControl
current={videoQuery.current - 1} current={liveQuery.current - 1}
pageSize={videoSize} pageSize={liveSize}
total={videoTotalCount} total={parseInt(liveTotalCount)}
toPage={(page) => { toPage={(page) => {
const _query = { ...videoQuery, current: page + 1 }; const _query = { ...liveQuery, current: page + 1 }
this.setState( this.setState(
{ {
videoQuery: _query, liveQuery: _query,
}, },
() => { () => {
this.handleFetchVideoList(); this.handleFetchLiveList()
} }
); )
}} }}
onShowSizeChange={this.onShowVideoSizeChange} onShowSizeChange={this.onShowLiveSizeChange}
/> />
)}
</div> </div>
)}
</div> </div>
</TabPane> </TabPane>
<TabPane tab="直播课" key="LIVE"> <TabPane tab='视频课' key='VIDEO'>
<div className="live-list"> <Radio.Group value={videoCourseDivision} onChange={this.videoCourseDivisionChange} style={{ marginBottom: 8 }}>
<Radio.Button value='internal'>内部课程</Radio.Button>
<Radio.Button value='external'>外部课程</Radio.Button>
</Radio.Group>
<div className='live-list'>
<div> <div>
<Row type="flex" justify="space-between" align="top"> <Row type='flex' justify='space-between' align='top'>
<div> <div>
<span style={{ lineHeight: "32px" }}>课程名称:</span> <span style={{ lineHeight: '32px' }}>课程名称:</span>
<Search <Search
// value={courseName} value={videoSearchDefalt[videoCourseDivision].courseName}
style={{ width: "calc(100% - 75px)" }} style={{ width: 'calc(100% - 75px)' }}
placeholder="搜索课程名称" placeholder='搜索课程名称'
// onChange={(e) => { onChange={this.handleChangVideoCourseName}
// this.handleChangLiveFilter(
// "courseName",
// e.target.value
// );
// }}
onSearch={(value) => { onSearch={(value) => {
this.handleChangLiveFilter("courseName", value); this.handleChangVideoFilter('courseName', value)
}} }}
enterButton={ enterButton={<span className='icon iconfont'>&#xe832;</span>}
<span className="icon iconfont">&#xe832;</span>
}
/> />
</div> </div>
<div style={{ width: "50%" }}> <div style={{ width: '50%' }}>
<span className="shelf-status">课程分类:</span> <span className='shelf-status'>课程分类:</span>
<TreeSelect <TreeSelect
style={{ width: "calc(100% - 75px)" }} treeNodeFilterProp='categoryName'
dropdownStyle={{ maxHeight: 400, overflow: "auto" }} showSearch
treeData={categoryList} style={{ minWidth: 'calc(100% - 75px)' }}
placeholder="请选择课程类型" dropdownMatchSelectWidth={false}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={videoCourseDivision === 'internal' ? categoryList : categoryListExternal}
placeholder='请选择课程类型'
allowClear allowClear
onChange={(value) => { onChange={(value) => {
this.handleChangLiveFilter("categoryId", value); this.handleChangVideoFilter('categoryId', value)
}} }}
/> />
</div> </div>
</Row> </Row>
</div> </div>
<TableSelectedData <TableSelectedData
selectedNum={ selectedNum={selectVideo.internal.length + selectVideo.external.length + selectLive.length + selectPicture.length}
selectVideo.length + selectLive.length + selectPicture.length
}
clearSelectedData={() => { clearSelectedData={() => {
this.setState({ this.setState({
selectVideo: [], selectVideo: {
internal: [],
external: [],
},
selectLive: [], selectLive: [],
selectPicture: [], selectPicture: [],
}); })
}} }}
/> />
<Table <Table
bordered rowKey={(record) => record.id}
size="middle" dataSource={videoDataSource[videoCourseDivision]}
columns={this.videoColumns()}
size='middle'
rowSelection={VideoSelection}
pagination={false} pagination={false}
columns={this.liveColumns()} bordered
rowSelection={LiveSelection} className='video-list-table'
// loading={loading} style={{ maxHeight: 359, overflow: 'scroll' }}
dataSource={liveDataSource}
style={{ maxHeight: 359, overflow: "scroll" }}
rowKey={(row) => row.liveCourseId}
/> />
{liveTotalCount > 0 && (
<div className="box-footer"> <div className='box-footer'>
{videoDataSource[videoCourseDivision].length > 0 && (
<PageControl <PageControl
current={liveQuery.current - 1} current={videoQuery[videoCourseDivision].current - 1}
pageSize={liveSize} pageSize={videoSize[videoCourseDivision]}
total={parseInt(liveTotalCount)} total={videoTotalCount[videoCourseDivision]}
toPage={(page) => { toPage={(page) => {
const _query = { ...liveQuery, current: page + 1 }; const _query = { ...videoQuery[videoCourseDivision], current: page + 1 }
console.log('_query', _query)
this.setState( this.setState(
{ {
liveQuery: _query, videoQuery: {
...videoQuery,
[videoCourseDivision]: _query,
},
}, },
() => { () => {
this.handleFetchLiveList(); this.handleFetchVideoList()
} }
); )
}} }}
onShowSizeChange={this.onShowLiveSizeChange} onShowSizeChange={this.onShowVideoSizeChange}
/> />
</div>
)} )}
</div> </div>
</div>
</TabPane> </TabPane>
<TabPane tab="图文课" key="PICTURE">
<div className="live-list"> <TabPane tab='图文课' key='PICTURE'>
<div className='live-list'>
<div> <div>
<Row type="flex" justify="space-between" align="top"> <Row type='flex' justify='space-between' align='top'>
<div> <div>
<span style={{ lineHeight: "32px" }}>课程名称:</span> <span style={{ lineHeight: '32px' }}>课程名称:</span>
<Search <Search
style={{ width: "calc(100% - 75px)" }} style={{ width: 'calc(100% - 75px)' }}
placeholder="搜索课程名称" placeholder='搜索课程名称'
onSearch={(value) => { onSearch={(value) => {
this.handleChangPictureFilter("courseName", value); this.handleChangPictureFilter('courseName', value)
}} }}
enterButton={ enterButton={<span className='icon iconfont'>&#xe832;</span>}
<span className="icon iconfont">&#xe832;</span>
}
/> />
</div> </div>
<div style={{ width: "50%" }}> <div style={{ width: '50%' }}>
<span className="shelf-status">课程分类:</span> <span className='shelf-status'>课程分类:</span>
<TreeSelect <TreeSelect
style={{ width: "calc(100% - 75px)" }} treeNodeFilterProp='categoryName'
dropdownStyle={{ maxHeight: 400, overflow: "auto" }} showSearch
style={{ width: 'calc(100% - 75px)' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={categoryList} treeData={categoryList}
placeholder="请选择课程类型" placeholder='请选择课程类型'
allowClear allowClear
onChange={(value) => { onChange={(value) => {
this.handleChangPictureFilter("categoryId", value); this.handleChangPictureFilter('categoryId', value)
}} }}
/> />
</div> </div>
</Row> </Row>
</div> </div>
<TableSelectedData <TableSelectedData
selectedNum={ selectedNum={selectVideo.internal.length + selectVideo.external.length + selectLive.length + selectPicture.length}
selectVideo.length + selectLive.length + selectPicture.length
}
clearSelectedData={() => { clearSelectedData={() => {
this.setState({ this.setState({
selectVideo: [], selectVideo: {
internal: [],
external: [],
},
selectLive: [], selectLive: [],
selectPicture: [], selectPicture: [],
}); })
}} }}
/> />
<Table <Table
bordered bordered
size="middle" size='middle'
pagination={false} pagination={false}
columns={this.pictureColumns()} columns={this.pictureColumns()}
rowSelection={PictureSelection} rowSelection={PictureSelection}
// loading={loading} // loading={loading}
dataSource={pictureDataSource} dataSource={pictureDataSource}
style={{ maxHeight: 359, overflow: "scroll" }} style={{ maxHeight: 359, overflow: 'scroll' }}
rowKey={(row) => row.id} rowKey={(row) => row.id}
/> />
{pictureTotalCount > 0 && ( {pictureTotalCount > 0 && (
<div className="box-footer"> <div className='box-footer'>
<PageControl <PageControl
current={pictureQuery.current - 1} current={pictureQuery.current - 1}
pageSize={pictureSize} pageSize={pictureSize}
total={parseInt(pictureTotalCount)} total={parseInt(pictureTotalCount)}
toPage={(page) => { toPage={(page) => {
const _query = { ...pictureQuery, current: page + 1 }; const _query = { ...pictureQuery, current: page + 1 }
this.setState( this.setState(
{ {
pictureQuery: _query, pictureQuery: _query,
}, },
() => { () => {
this.handleFetchPictureList(); this.handleFetchPictureList()
} }
); )
}} }}
onShowSizeChange={this.onShowPictureSizeChange} onShowSizeChange={this.onShowPictureSizeChange}
/> />
...@@ -966,7 +1037,7 @@ class AddCourse extends React.Component { ...@@ -966,7 +1037,7 @@ class AddCourse extends React.Component {
</TabPane> </TabPane>
</Tabs> </Tabs>
</Modal> </Modal>
); )
} }
} }
export default AddCourse; export default AddCourse
...@@ -71,13 +71,13 @@ ...@@ -71,13 +71,13 @@
} }
.courseware { .courseware {
font-size: 14px; font-size: 14px;
color: #5289fa; color: #2966ff;
line-height: 20px; line-height: 20px;
text-align: right; text-align: right;
cursor: pointer; cursor: pointer;
} }
.quota-icon { .quota-icon {
color: #5289fa; color: #2966ff;
cursor: pointer; cursor: pointer;
} }
.operate { .operate {
...@@ -85,7 +85,7 @@ ...@@ -85,7 +85,7 @@
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
.operate__item { .operate__item {
color: #5289fa; color: #2966ff;
cursor: pointer; cursor: pointer;
&.split { &.split {
margin: 0 8px; margin: 0 8px;
...@@ -94,7 +94,7 @@ ...@@ -94,7 +94,7 @@
} }
} }
.operate-text { .operate-text {
color: #5289fa; color: #2966ff;
cursor: pointer; cursor: pointer;
} }
.course-start-end { .course-start-end {
...@@ -125,42 +125,10 @@ ...@@ -125,42 +125,10 @@
} }
} }
.add-course-modal { .add-course-modal {
.ant-tabs-top > .ant-tabs-nav::before {
border-bottom: 0px;
}
.ant-tabs-nav-list {
margin: 0 auto;
flex:none !important
}
.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn {
font-weight: normal;
border-bottom: 0px;
}
.ant-tabs-nav .ant-tabs-tab {
padding: 6px 12px !important;
margin: 0;
border: 0.5px solid #e8e8e8;
font-size: 14px !important;
color: #999;
&:nth-child(1) {
border-radius: 4px 0px 0px 4px;
}
&:nth-child(3) {
border-radius: 0px 4px 4px 0px;
}
}
.ant-tabs-nav .ant-tabs-tab-active { .ant-tabs-nav .ant-tabs-tab-active {
border: 1px solid #ffb714; color: #2966ff;
color: #ffb714;
}
.ant-tabs-top .ant-tabs-ink-bar-animated:after {
height: 0;
} }
.ant-modal-content tr > td {
.ant-modal-content tr > td{ padding: 12px 8px !important;
padding:12px 8px !important;
} }
} }
/* /*
* @Author: zhangleyuan * @Author: zhangleyuan
* @Date: 2021-02-20 16:13:39 * @Date: 2021-02-20 16:13:39
* @LastEditors: zhangleyuan * @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-10 10:15:58 * @LastEditTime: 2021-05-24 00:00:31
* @Description: 描述一下 * @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from 'react'
import { Button, message, Modal} from 'antd'; import { Button, message, Modal } from 'antd'
import ShowTips from "@/components/ShowTips"; import ShowTips from '@/components/ShowTips'
import Breadcrumbs from "@/components/Breadcrumbs"; import Breadcrumbs from '@/components/Breadcrumbs'
import BasicInfo from './components/BasicInfo'; import BasicInfo from './components/BasicInfo'
import TrainingTask from './components/TrainingTask'; import TrainingTask from './components/TrainingTask'
import ExpiredCourseList from './components/ExpiredCourseList'; import ExpiredCourseList from './components/ExpiredCourseList'
import PlanService from '@/domains/plan-domain/planService' import PlanService from '@/domains/plan-domain/planService'
import User from '@/common/js/user'; import User from '@/common/js/user'
import _ from "underscore"; import _ from 'underscore'
import './AddPlan.less' import './AddPlan.less'
const defaultCover = 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'; const defaultCover = 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'
const defaultBasicData = { const defaultBasicData = {
planName:"", planName: '',
coverUrl: defaultCover, coverUrl: defaultCover,
coverId:null, coverId: null,
enableState:"YES", enableState: 'YES',
selectOperatorList:[], selectOperatorList: [],
instro:'', instro: '',
operateType:'All_Operate', operateType: 'All_Operate',
percentCompleteLive:80, percentCompleteLive: 80,
percentCompleteVideo:80, percentCompleteVideo: 80,
percentCompletePicture:100 percentCompletePicture: 100
} }
const defaultTaskList = []; const defaultTaskList = []
function AddPlan() { function AddPlan() {
const id = getParameterByName("id"); const id = getParameterByName('id')
const type = getParameterByName("type"); const type = getParameterByName('type')
const [basicData,setBasicData] = useState(defaultBasicData); const [basicData, setBasicData] = useState(defaultBasicData)
const [taskList,setTaskList] = useState(defaultTaskList); const [taskList, setTaskList] = useState(defaultTaskList)
const [expiredCourseList,setExpiredCourseList] = useState([]); const [expiredCourseList, setExpiredCourseList] = useState([])
const [hasGetDetail,setHasGetDetail]= useState(false); const [hasGetDetail, setHasGetDetail] = useState(false)
const [submitDisabled,setSubmitDisabled] = useState(false); const [submitDisabled, setSubmitDisabled] = useState(false)
useEffect(()=>{ useEffect(() => {
if(type==='edit'){ if (type === 'edit') {
getPlanDetail(); getPlanDetail()
getPlanCustomerState(); getPlanCustomerState()
} }
},id) }, id)
function getPlanCustomerState(){ function getPlanCustomerState() {
PlanService.getTrainingCourseAutoCancel({ PlanService.getTrainingCourseAutoCancel({
planId: id planId: id
}).then((res) => { }).then((res) => {
const expiredCourseList = res.result; const expiredCourseList = res.result
setExpiredCourseList(expiredCourseList) setExpiredCourseList(expiredCourseList)
}) })
} }
function getPlanDetail (){ function getPlanDetail() {
PlanService.getTrainingPlanDetail({ PlanService.getTrainingPlanDetail({
planId: id planId: id
}).then((res) => { }).then((res) => {
...@@ -68,231 +68,231 @@ function AddPlan() { ...@@ -68,231 +68,231 @@ function AddPlan() {
percentCompletePicture, percentCompletePicture,
courseMediaVOS, courseMediaVOS,
trainingTaskList trainingTaskList
} = res.result; } = res.result
let coverId; let coverId
let coverUrl; let coverUrl
let instro; let instro
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 "INTRO": case 'INTRO':
instro = item.mediaContent; instro = item.mediaContent
break; break
default: default:
break; break
} }
return item; return item
}) })
let _selectOperatorList = []; let _selectOperatorList = []
if(operateIds){ if (operateIds) {
_selectOperatorList = operateIds.map((item,index)=>{ _selectOperatorList = operateIds.map((item, index) => {
let _item = {}; let _item = {}
_item.id = item; _item.id = item
return _item return _item
}) })
} }
setTaskList(trainingTaskList); setTaskList(trainingTaskList)
setBasicData({ setBasicData({
planName, planName,
coverUrl:coverUrl || defaultCover, coverUrl: coverUrl || defaultCover,
coverId, coverId,
enableState, enableState,
selectOperatorList:_selectOperatorList, selectOperatorList: _selectOperatorList,
instro, instro,
operateType, operateType,
percentCompleteLive, percentCompleteLive,
percentCompleteVideo, percentCompleteVideo,
percentCompletePicture percentCompletePicture
}) })
setHasGetDetail(true); setHasGetDetail(true)
}) })
} }
function handleChangeBasicInfo(field, value,option){ function handleChangeBasicInfo(field, value, option) {
setBasicData( { setBasicData({
...basicData, ...basicData,
[field]: value, [field]: value
}) })
} }
function handleChangeTaskInfo(value){ function handleChangeTaskInfo(value) {
setTaskList(value) setTaskList(value)
} }
function submitInfo(){ function submitInfo() {
const {planName,enableState,selectOperatorList,instro,operateType,percentCompleteLive,percentCompleteVideo,percentCompletePicture,coverId,coverUrl} = basicData; const {
let input = /^[\s]*$/; planName,
if(!planName || input.test(planName)){ enableState,
message.warning('请输入的培训计划名称'); selectOperatorList,
return; instro,
} operateType,
if(operateType==='Assign_Operate' && selectOperatorList.length===0){ percentCompleteLive,
message.warning('请选择指定运营师'); percentCompleteVideo,
return; percentCompletePicture,
} coverId,
if(!percentCompleteLive && percentCompleteLive !==0 ){ coverUrl
message.warning('请输入完成标准'); } = basicData
return; let input = /^[\s]*$/
} if (!planName || input.test(planName)) {
if(!percentCompleteVideo && percentCompleteVideo !==0 ){ message.warning('请输入的培训计划名称')
message.warning('请输入完成标准'); return
return; }
} if (operateType === 'Assign_Operate' && selectOperatorList.length === 0) {
if(!percentCompletePicture && percentCompletePicture !==0 ){ message.warning('请选择指定运营师')
message.warning('请输入完成标准'); return
return; }
} if (!percentCompleteLive && percentCompleteLive !== 0) {
if(taskList.length === 0){ message.warning('请输入完成标准')
message.warning('请输入培训计划内容'); return
return; }
} if (!percentCompleteVideo && percentCompleteVideo !== 0) {
for(let i=0;i<taskList.length;i++){ message.warning('请输入完成标准')
if(input.test(taskList[i].taskName)){ return
message.warning('培训任务名称不能为空'); }
return false; if (!percentCompletePicture && percentCompletePicture !== 0) {
} message.warning('请输入完成标准')
if(taskList[i].courseList.length === 0){ return
}
if (taskList.length === 0) {
message.warning('请输入培训计划内容')
return
}
for (let i = 0; i < taskList.length; i++) {
if (input.test(taskList[i].taskName)) {
message.warning('培训任务名称不能为空')
return false
}
if (taskList[i].courseList.length === 0) {
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>
}) })
return false; return false
} }
} }
let scheduleMediaRequests = []; let scheduleMediaRequests = []
let coverObj ={ let coverObj = {
contentType:'COVER', contentType: 'COVER',
mediaContent:coverId, mediaContent: coverId,
mediaType:'PICTURE', mediaType: 'PICTURE',
mediaUrl: coverUrl, mediaUrl: coverUrl
} }
if(coverId){ if (coverId) {
scheduleMediaRequests = [...scheduleMediaRequests,coverObj]; scheduleMediaRequests = [...scheduleMediaRequests, coverObj]
} }
let instroObj = { let instroObj = {
contentType:"INTRO", contentType: 'INTRO',
mediaType: 'TEXT', mediaType: 'TEXT',
mediaContent:instro, mediaContent: instro
} }
if(instro){ if (instro) {
scheduleMediaRequests = [...scheduleMediaRequests,instroObj]; scheduleMediaRequests = [...scheduleMediaRequests, instroObj]
} }
const params = { const params = {
createId:User.getStoreUserId(), createId: User.getStoreUserId(),
enableState, enableState,
operateIds:_.pluck(selectOperatorList,'id'), operateIds: _.pluck(selectOperatorList, 'id'),
operateType, operateType,
percentCompleteLive, percentCompleteLive,
percentCompleteVideo, percentCompleteVideo,
percentCompletePicture, percentCompletePicture,
planName, planName,
scheduleMediaRequests, scheduleMediaRequests,
storeId:User.getStoreId(), storeId: User.getStoreId(),
trainingTaskList:handleSubmitTaskData(taskList) trainingTaskList: handleSubmitTaskData(taskList)
} }
if (type === 'add') { if (type === 'add') {
PlanService.createTrainingPlan(params).then((res) => { PlanService.createTrainingPlan(params).then((res) => {
if (res.success){ if (res.success) {
message.success("新建成功"); message.success('新建成功')
setSubmitDisabled(true); setSubmitDisabled(true)
window.RCHistory.goBack(); window.RCHistory.goBack()
} }
}); })
}else{ } else {
const _params = { const _params = {
...params, ...params,
id id
} }
PlanService.updateTrainingPlan(_params).then((res) => { PlanService.updateTrainingPlan(_params).then((res) => {
if (res.success){ if (res.success) {
message.success("更新成功"); message.success('更新成功')
window.RCHistory.goBack(); window.RCHistory.goBack()
} }
}); })
} }
} }
function handleSubmitTaskData(taskData){ function handleSubmitTaskData(taskData) {
return taskData.map((item,index)=>{ return taskData.map((item, index) => {
let _item = {}; let _item = {}
_item.taskId = item.taskId; _item.taskId = item.taskId
_item.taskName =item.taskName; _item.taskName = item.taskName
_item.courseList = item.courseList.map((childItem,index)=>{ _item.courseList = item.courseList.map((childItem, index) => {
let _childItem = {} let _childItem = {}
_childItem.courseId = childItem.courseId; _childItem.courseId = childItem.courseId
_childItem.courseName = childItem.courseName; _childItem.courseName = childItem.courseName
_childItem.courseType = childItem.courseType; _childItem.courseType = childItem.courseType
return _childItem; return _childItem
}); })
return _item; return _item
}) })
} }
// 取消编辑并返回上一级路由 // 取消编辑并返回上一级路由
function handleGoBack (){ function handleGoBack() {
if (!_.isEqual(basicData, defaultBasicData) || !_.isEqual(taskList, defaultTaskList) if (!_.isEqual(basicData, defaultBasicData) || !_.isEqual(taskList, defaultTaskList)) {
) {
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.goBack(); window.RCHistory.goBack()
} }
}) })
} else { } else {
window.RCHistory.goBack(); window.RCHistory.goBack()
} }
} }
return ( return (
<div className="page add-plan-page"> <div className='page add-plan-page'>
<Breadcrumbs <Breadcrumbs navList={type == 'add' ? '新建培训计划' : '编辑培训计划'} goBack={handleGoBack} />
navList={type == "add" ? "新建培训计划" : "编辑培训计划"} <div className='box'>
goBack={handleGoBack} <div className='show-tips'>
/> <ShowTips message='请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利' />
<div className="box">
<div className="show-tips">
<ShowTips message="请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利" />
</div> </div>
<div className="add-plan-page__form"> <div className='add-plan-page__form'>
<div className="basic-info__wrap"> <div className='basic-info__wrap'>
<div className="title">基本信息</div> <div className='title'>基本信息</div>
<BasicInfo <BasicInfo data={basicData} onChange={handleChangeBasicInfo} />
data={basicData}
onChange={handleChangeBasicInfo}
/>
</div> </div>
<div className="basic-info__wrap"> <div className='basic-info__wrap'>
<div className="title">培训任务</div> <div className='title'>培训任务</div>
{ (type==='edit' && hasGetDetail) && {type === 'edit' && hasGetDetail && <TrainingTask data={taskList} onChange={handleChangeTaskInfo} />}
<TrainingTask data={taskList} onChange={handleChangeTaskInfo} /> {type === 'add' && <TrainingTask data={taskList} onChange={handleChangeTaskInfo} />}
}
{ type==='add' &&
<TrainingTask data={taskList} onChange={handleChangeTaskInfo} />
}
</div> </div>
{ (type==='edit' && expiredCourseList.length > 0) && {type === 'edit' && expiredCourseList.length > 0 && (
<div className="expired-info__wrap"> <div className='expired-info__wrap'>
<div className="title">失效课程</div> <div className='title'>失效课程</div>
<ExpiredCourseList expiredCourseList={expiredCourseList}/> <ExpiredCourseList expiredCourseList={expiredCourseList} />
</div> </div>
} )}
</div> </div>
</div> </div>
<div className="footer"> <div className='footer'>
<Button onClick={handleGoBack}>取消</Button> <Button onClick={handleGoBack}>取消</Button>
<Button type="primary" onClick={submitInfo} disabled={submitDisabled}>保存</Button> <Button type='primary' onClick={submitInfo} disabled={submitDisabled}>
保存
</Button>
</div> </div>
</div> </div>
) )
} }
export default AddPlan; export default AddPlan
\ No newline at end of file
/* /*
* @Author: zhangleyuan * @Author: zhangleyuan
* @Date: 2021-02-20 16:13:39 * @Date: 2021-02-20 16:13:39
* @LastEditors: zhangleyuan * @LastEditors: fusanqiasng
* @LastEditTime: 2021-03-10 18:38:50 * @LastEditTime: 2021-05-23 23:59:43
* @Description: 描述一下 * @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from 'react'
import PlanFilter from './components/PlanFilter'; import PlanFilter from './components/PlanFilter'
import PlanOpt from './components/PlanOpt'; import PlanOpt from './components/PlanOpt'
import PlanList from './components/PlanList'; import PlanList from './components/PlanList'
import PlanService from "@/domains/plan-domain/planService"; import PlanService from '@/domains/plan-domain/planService'
import User from '@/common/js/user'; import User from '@/common/js/user'
function PlanPage() { function PlanPage() {
const [planListData, setPlanListData] = useState([])
const [planListData, setPlanListData] = useState([]); const [query, setQuery] = useState({
const [query,setQuery] = useState({ current: 1,
current:1, size: 10
size:10, })
});
useEffect(() => { useEffect(() => {
handleFetchPlanList(); function handleFetchPlanList(_query) {
}, [query]);
const [totalCount,setTotalCount] = useState(0);
function queryChange(_query){
const params = { const params = {
...query, ...query,
..._query, ..._query,
}; storeUserId: User.getStoreUserId()
setQuery(params);
} }
function handleFetchPlanList(_query){
const params = {
...query,
..._query,
storeUserId:User.getStoreUserId()
};
//动态获取计划列表 //动态获取计划列表
PlanService.getTrainingPlanPage(params).then((res) => { PlanService.getTrainingPlanPage(params).then((res) => {
const { result: { records = [], total } } = res; const {
setPlanListData(records); result: { records = [], total }
setTotalCount(total); } = res
setPlanListData(records)
setTotalCount(total)
}) })
} }
handleFetchPlanList()
}, [query])
const [totalCount, setTotalCount] = useState(0)
function queryChange(_query) {
const params = {
...query,
..._query
}
setQuery(params)
}
return ( return (
<div className="page"> <div className='page'>
<div className="content-header">培训计划</div> <div className='content-header'>培训计划</div>
<div className="box"> <div className='box'>
<PlanFilter onChange={queryChange}/> <PlanFilter onChange={queryChange} />
<PlanOpt/> <PlanOpt />
<PlanList <PlanList planListData={planListData} query={query} totalCount={totalCount} onChange={queryChange} />
planListData={planListData}
query={query}
totalCount={totalCount}
onChange={queryChange}
/>
</div> </div>
</div> </div>
) )
} }
export default PlanPage; export default PlanPage
\ No newline at end of file
/* /*
* @Author: zhangleyuan * @Author: zhangleyuan
* @Date: 2021-02-20 16:45:51 * @Date: 2021-02-20 16:45:51
* @LastEditors: zhangleyuan * @LastEditors: fusanqiasng
* @LastEditTime: 2021-03-27 14:43:17 * @LastEditTime: 2021-05-24 15:15:06
* @Description: 描述一下 * @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React from 'react'; import React from 'react'
import { Table ,Button,Input,Form,Collapse,Modal } from 'antd'; import { Input, Form, Modal } from 'antd'
import { sortableContainer, sortableElement, sortableHandle} from 'react-sortable-hoc'; import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc'
import { MenuOutlined } from '@ant-design/icons';
import arrayMove from 'array-move'; import arrayMove from 'array-move'
import RelatedCourseModal from '../modal/relatedCourseModal' import RelatedCourseModal from '../modal/relatedCourseModal'
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom'
import './TrainingTask.less'; import './TrainingTask.less'
const { Panel } = Collapse;
const { confirm } = Modal; const { confirm } = Modal
const CourseType = { const CourseType = {
LIVE: { LIVE: {
text: "直播课" text: '直播课'
}, },
VOICE : { VOICE: {
text:"视频课" text: '视频课'
}, },
RECORD : { RECORD: {
text:'录播课' text: '录播课'
}, },
PICTURE:{ PICTURE: {
text:'图文课' text: '图文课'
} }
}; }
const courseStateShow = { const courseStateShow = {
UN_START: { UN_START: {
title: "待开播", title: '待开播'
}, },
STARTING: { STARTING: {
title: "直播中", title: '直播中'
}, },
FINISH: { FINISH: {
title: "回放", title: '回放'
}, },
EXPIRED: { EXPIRED: {
title: "未成功开课", title: '未成功开课'
}, }
}; }
const DragHandle = sortableHandle(() => ( const DragHandle = sortableHandle(() => (
<span className="operate__item" > <span className='operate__item'>
<span className="icon iconfont">&#xe7cd;</span> <span className='icon iconfont'>&#xe7cd;</span>
<span className="text">移动</span> <span className='text'>移动</span>
</span> </span>
)); ))
const SortableTaskItem = sortableElement((props) => <div {...props}>{props.taskitem}</div>)
const SortableTaskContainer = sortableContainer((props) => <div {...props}></div>)
const SortableTaskItem = sortableElement(props => <div {...props}>{props.taskItem}</div>) const SortableCourseItem = sortableElement((props) => <div {...props}>{props.courseitem}</div>)
const SortableTaskContainer = sortableContainer(props => <div {...props}></div>); const SortableCourseContainer = sortableContainer((props) => <div {...props}></div>)
const SortableCourseItem = sortableElement(props => <div {...props}>{props.courseItem}</div>)
const SortableCourseContainer = sortableContainer(props => <div {...props}></div>);
class TrainingTask extends React.Component { class TrainingTask extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
dataSource:this.props.data, dataSource: this.props.data,
selectedTaskIndex:0, selectedTaskIndex: 0,
relatedCourseModalVisible:false relatedCourseModalVisible: false
};
} }
componentWillMount(){
} }
componentWillMount() {}
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {}
}
onTaskSortEnd = ({ oldIndex, newIndex }) => { onTaskSortEnd = ({ oldIndex, newIndex }) => {
const { dataSource } = this.state; const { dataSource } = this.state
if (oldIndex !== newIndex) { if (oldIndex !== newIndex) {
const newData = arrayMove([].concat(dataSource), oldIndex, newIndex).filter((el) => !!el)
const newData = arrayMove([].concat(dataSource), oldIndex, newIndex).filter(el => !!el); this.setState(
this.setState({ {
dataSource:newData, dataSource: newData
},()=>{this.props.onChange(newData);}) },
() => {
this.props.onChange(newData)
} }
}; )
onCourseSortEnd = ({ oldIndex, newIndex },parentIndex) => { }
}
const { dataSource } = this.state; onCourseSortEnd = ({ oldIndex, newIndex }, parentIndex) => {
const { dataSource } = this.state
const _dataSource = [...dataSource]; const _dataSource = [...dataSource]
if (oldIndex !== newIndex) { if (oldIndex !== newIndex) {
_dataSource[parentIndex].courseList = arrayMove([].concat(dataSource[parentIndex].courseList), oldIndex, newIndex).filter(el => !!el); _dataSource[parentIndex].courseList = arrayMove([].concat(dataSource[parentIndex].courseList), oldIndex, newIndex).filter((el) => !!el)
this.setState({ this.setState(
dataSource:_dataSource, {
},()=>{this.props.onChange(_dataSource);}) dataSource: _dataSource
},
() => {
this.props.onChange(_dataSource)
}
)
}
} }
};
addTask = () => { addTask = () => {
const { dataSource } = this.state; const { dataSource } = this.state
const taskObj={ const taskObj = {
taskName: '', taskName: '',
index:dataSource.length, index: dataSource.length,
type:'input', type: 'input',
open:true, open: true,
courseList:[ courseList: []
]
} }
const newData = [...dataSource,taskObj]; const newData = [...dataSource, taskObj]
this.setState({ this.setState(
dataSource:newData {
},()=>{this.props.onChange(newData);}) dataSource: newData
},
() => {
this.props.onChange(newData)
}
)
} }
handleRenameTaskName = (e,record) => { handleRenameTaskName = (e, record) => {
const { value } = e.target; const { value } = e.target
const { dataSource } = this.state; const { dataSource } = this.state
record.taskName = value; record.taskName = value
this.setState({ this.setState(
dataSource, {
},()=>{this.props.onChange(dataSource);}) dataSource
} },
handleTaskNameBlur = (e,record)=>{ () => {
const { value } = e.target; this.props.onChange(dataSource)
const { dataSource }= this.state;
let input = /^[\s]*$/;
if(value && !input.test(value)){
record.type="text";
this.setState({
dataSource,
},()=>{this.props.onChange(dataSource);})
} }
)
}
handleTaskNameBlur = (e, record) => {
const { value } = e.target
const { dataSource } = this.state
let input = /^[\s]*$/
if (value && !input.test(value)) {
record.type = 'text'
this.setState(
{
dataSource
},
() => {
this.props.onChange(dataSource)
}
)
} }
handleRenameCourseName = (e,record) => {
const { value } = e.target;
const { dataSource } = this.state;
record.courseName = value;
this.setState({
dataSource,
},()=>{this.props.onChange(dataSource);})
} }
handleCourseNameBlur = (e,record)=>{ handleRenameCourseName = (e, record) => {
const { value } = e.target; const { value } = e.target
const { dataSource }= this.state; const { dataSource } = this.state
let input = /^[\s]*$/; record.courseName = value
if(value && !input.test(value)){ this.setState(
record.type="text"; {
this.setState({ dataSource
dataSource, },
},()=>{this.props.onChange(dataSource);}) () => {
this.props.onChange(dataSource)
}
)
} }
handleCourseNameBlur = (e, record) => {
const { value } = e.target
const { dataSource } = this.state
let input = /^[\s]*$/
if (value && !input.test(value)) {
record.type = 'text'
this.setState(
{
dataSource
},
() => {
this.props.onChange(dataSource)
}
)
} }
handleDeleteTask = (index)=>{ }
handleDeleteTask = (index) => {
return confirm({ return confirm({
title: "删除任务", title: '删除任务',
content: "删除该任务会同步删除任务下的课程,是否仍要删除?", content: '删除该任务会同步删除任务下的课程,是否仍要删除?',
icon: ( icon: <span className='icon iconfont default-confirm-icon'>&#xe839; </span>,
<span className="icon iconfont default-confirm-icon">&#xe839; </span> okText: '删除',
), okType: 'danger',
okText: "删除", cancelText: '取消',
okType: "danger",
cancelText: "取消",
onOk: () => { onOk: () => {
this.handleConfirmDeleteTask(index); this.handleConfirmDeleteTask(index)
}
})
}
handleConfirmDeleteTask = (index) => {
const { dataSource } = this.state
const newData = [...dataSource]
newData.splice(index, 1)
this.setState(
{
dataSource: newData
}, },
}); () => {
this.props.onChange(newData)
} }
handleConfirmDeleteTask = (index)=>{ )
const {dataSource}= this.state;
const newData=[...dataSource];
newData.splice(index,1);
this.setState({
dataSource:newData,
},()=>{this.props.onChange(newData);})
} }
handleDeleteCourse = (parentIndex,index)=>{ handleDeleteCourse = (parentIndex, index) => {
return confirm({ return confirm({
title: "删除课程", title: '删除课程',
content: "确定删除该课程吗?", content: '确定删除该课程吗?',
icon: ( icon: <span className='icon iconfont default-confirm-icon'>&#xe839; </span>,
<span className="icon iconfont default-confirm-icon">&#xe839; </span> okText: '删除',
), okType: 'danger',
okText: "删除", cancelText: '取消',
okType: "danger",
cancelText: "取消",
onOk: () => { onOk: () => {
this.handleConfirmDeleteCourse(parentIndex,index); this.handleConfirmDeleteCourse(parentIndex, index)
}, }
}); })
} }
handleConfirmDeleteCourse = (parentIndex,index)=>{ handleConfirmDeleteCourse = (parentIndex, index) => {
const {dataSource}= this.state; const { dataSource } = this.state
const newData=[...dataSource]; const newData = [...dataSource]
const selectData = [...newData[parentIndex].courseList] const selectData = [...newData[parentIndex].courseList]
selectData.splice(index,1) selectData.splice(index, 1)
newData[parentIndex].courseList= selectData; newData[parentIndex].courseList = selectData
this.setState({ this.setState(
dataSource:newData, {
},()=>{this.props.onChange(newData);}) dataSource: newData
},
() => {
this.props.onChange(newData)
}
)
} }
showRelatedCourseModal = (index)=>{ showRelatedCourseModal = (index) => {
this.setState({ this.setState({
selectedTaskIndex:index, selectedTaskIndex: index,
relatedCourseModalVisible:true relatedCourseModalVisible: true
}) })
} }
closeRelatedCourseModal = (index)=>{ closeRelatedCourseModal = (index) => {
this.setState({ this.setState({
relatedCourseModalVisible:false relatedCourseModalVisible: false
}) })
} }
confirmSelectCourse = (selectList) =>{ confirmSelectCourse = (selectList) => {
console.log("selectList",selectList); console.log('selectList', selectList)
const {selectedTaskIndex}= this.state; const { selectedTaskIndex } = this.state
const { dataSource } = this.state const { dataSource } = this.state
const newData=[...dataSource]; const newData = [...dataSource]
const selectData = [...newData[selectedTaskIndex].courseList] const selectData = [...newData[selectedTaskIndex].courseList]
const _selectData =[...selectData,...selectList]; const _selectData = [...selectData, ...selectList]
newData[selectedTaskIndex].courseList= _selectData; newData[selectedTaskIndex].courseList = _selectData
this.setState({ this.setState(
relatedCourseModalVisible:false, {
dataSource:newData, relatedCourseModalVisible: false,
},()=>{ dataSource: newData
this.props.onChange(newData); },
}) () => {
this.props.onChange(newData)
} }
openOrCloseTask = (index)=>{ )
const {dataSource}= this.state;
const newData=[...dataSource];
newData[index].open = !newData[index].open;
this.setState({
dataSource:newData,
},()=>{this.props.onChange(newData);})
} }
handleValidatorTaskName = (rule,value)=>{ openOrCloseTask = (index) => {
let input = /^[\s]*$/; const { dataSource } = this.state
if (input.test(value) || !value){ const newData = [...dataSource]
newData[index].open = !newData[index].open
this.setState(
{
dataSource: newData
},
() => {
this.props.onChange(newData)
}
)
}
handleValidatorTaskName = (rule, value) => {
let input = /^[\s]*$/
if (input.test(value) || !value) {
return Promise.reject(new Error('请输入任务名称')) return Promise.reject(new Error('请输入任务名称'))
} }
return Promise.resolve() return Promise.resolve()
} }
handleValidatorCourseName = (rule,value)=>{ handleValidatorCourseName = (rule, value) => {
let input = /^[\s]*$/; let input = /^[\s]*$/
if (input.test(value) || !value){ if (input.test(value) || !value) {
return Promise.reject(new Error('请输入课程名称')) return Promise.reject(new Error('请输入课程名称'))
} }
return Promise.resolve() return Promise.resolve()
} }
renderTaskItem = (record,index)=>{ renderTaskItem = (record, index) => {
return <div className="plan-sort-task-item"> return (
<div className="task-con"> <div className='plan-sort-task-item'>
<div className="task-instro"> <div className='task-con'>
<span onClick={()=>this.openOrCloseTask(index)}> <div className='task-instro'>
{record.open? <span onClick={() => this.openOrCloseTask(index)}>
<span className="icon iconfont open-icon">&#xe82d;</span>: {record.open ? <span className='icon iconfont open-icon'>&#xe82d;</span> : <span className='icon iconfont open-icon'>&#xe835;</span>}
<span className="icon iconfont open-icon">&#xe835;</span>
}
</span> </span>
{record.type==='input'? <Choose>
<div className="task-name-con"> <When condition={record.type === 'input'}>
<span className="number">{index + 1}.</span> <div className='task-name-con'>
<span className='number'>{index + 1}.</span>
<Form> <Form>
<Form.Item <Form.Item
initialValue={record.taskName}
validateTrigger={['onChange', 'onBlur']} validateTrigger={['onChange', 'onBlur']}
name={['taskName']} name={['taskName']}
rules={[ rules={[
{ {
validator:(rule,value)=>this.handleValidatorTaskName(rule,value) validator: (rule, value) => this.handleValidatorTaskName(rule, value)
} }
]}> ]}>
<Input className="task-name-input" defaultValue={record.taskName} style={{ width: 300 }} placeholder="请输入任务名称(20字以内)" maxLength={20} onChange={(e) => { this.handleRenameTaskName(e,record)}} onBlur={(e)=>{this.handleTaskNameBlur(e,record)}}/> <Input
className='task-name-input'
style={{ width: 300 }}
placeholder='请输入任务名称(20字以内)'
maxLength={20}
onChange={(e) => {
this.handleRenameTaskName(e, record)
}}
onBlur={(e) => {
this.handleTaskNameBlur(e, record)
}}
/>
</Form.Item> </Form.Item>
</Form> </Form>
</div> </div>
: </When>
<div className="task-name-con"> <Otherwise>
<span className="number">{index + 1}.</span> <div className='task-name-con'>
<span className="task-name">{record.taskName}</span> <span className='number'>{index + 1}.</span>
<span className='task-name'>{record.taskName}</span>
</div> </div>
} </Otherwise>
</Choose>
</div> </div>
<div className="operate"> <div className='operate'>
<DragHandle /> <DragHandle />
<span className="operate__item" onClick={()=>{const { dataSource }= this.state; record.type="input";this.setState({dataSource})}}> <span
<span className="icon iconfont">&#xe6f5;</span> className='operate__item'
<span className="text">重命名</span> onClick={() => {
const { dataSource } = this.state
record.type = 'input'
this.setState({ dataSource })
}}>
<span className='icon iconfont'>&#xe6f5;</span>
<span className='text'>重命名</span>
</span> </span>
<span className="operate__item" onClick={()=>{this.handleDeleteTask(index)}} > <span
<span className="icon iconfont">&#xe6f6;</span> className='operate__item'
<span className="text">删除</span> onClick={() => {
this.handleDeleteTask(index)
}}>
<span className='icon iconfont'>&#xe6f6;</span>
<span className='text'>删除</span>
</span> </span>
</div> </div>
</div> </div>
{record.open && {record.open && (
<div className="course-box"> <div className='course-box'>
<SortableCourseContainer <SortableCourseContainer useDragHandle disableAutoscroll helperClass='row-dragging' onSortEnd={(record) => this.onCourseSortEnd(record, index)}>
useDragHandle {record.courseList.map((item, courseIndex) => (
disableAutoscroll <SortableCourseItem courseitem={this.renderCourseItem(item, courseIndex, index)} index={courseIndex} key={courseIndex}></SortableCourseItem>
helperClass="row-dragging" ))}
onSortEnd={(record)=>this.onCourseSortEnd(record,index)} >
{record.courseList.map((courseItem, courseIndex) =>
<SortableCourseItem courseItem={this.renderCourseItem(courseItem,courseIndex,index)} index={courseIndex}>
</SortableCourseItem>
)}
</SortableCourseContainer> </SortableCourseContainer>
<div className="add-course-con"> <div className='add-course-con'>
{record.courseList.length>19? <Choose>
<span className="add-course-btn-disabled" onClick={()=>{this.showRelatedCourseModal(index)}}><span>+</span><span>关联课程</span></span> <When condition={record.courseList.length > 19}>
: <span
<span className="add-course-btn" onClick={()=>{this.showRelatedCourseModal(index)}}><span>+</span><span>关联课程</span></span> className='add-course-btn-disabled'
} onClick={() => {
this.showRelatedCourseModal(index)
}}>
<span>+</span>
<span>关联课程</span>
</span>
</When>
<Otherwise>
<span
className='add-course-btn'
onClick={() => {
this.showRelatedCourseModal(index)
}}>
<span>+</span>
<span>关联课程</span>
</span>
</Otherwise>
</Choose>
</div> </div>
</div> </div>
} )}
</div> </div>
)
} }
renderCourseItem = (record,index,parentIndex)=>{ renderCourseItem = (record, index, parentIndex) => {
return <div className="plan-course-sort-item"> return (
<div className="course-info"> <div className='plan-course-sort-item'>
<span className="course-type">{CourseType[record.courseType].text}</span> <div className='course-info'>
{record.type==='input'? <span className='course-type'>{CourseType[record.courseType].text}</span>
<Choose>
<When condition={record.type === 'input'}>
<Form> <Form>
<Form.Item <Form.Item
initialValue={record.courseName}
validateTrigger={['onChange', 'onBlur']} validateTrigger={['onChange', 'onBlur']}
name={['courseName']} name={['courseName']}
rules={[ rules={[
{ {
validator:(rule,value)=>this.handleValidatorCourseName(rule,value) validator: (rule, value) => this.handleValidatorCourseName(rule, value)
} }
]}> ]}>
<Input className="course-name-input" defaultValue={record.courseName} style={{ width: 300 }} placeholder="请输入课程名称(40字以内)" maxLength={40} onChange={(e) => { this.handleRenameCourseName(e,record)}} onBlur={(e)=>{this.handleCourseNameBlur(e,record)}}/></Form.Item> <Input
className='course-name-input'
style={{ width: 300 }}
placeholder='请输入课程名称(40字以内)'
maxLength={40}
onChange={(e) => {
this.handleRenameCourseName(e, record)
}}
onBlur={(e) => {
this.handleCourseNameBlur(e, record)
}}
/>
</Form.Item>
</Form> </Form>
: </When>
<span className="course-name">{parentIndex + 1}.{index + 1} {record.courseName}</span> <Otherwise>
} <span className='course-name'>
{record.courseState === "EXPIRED" && {parentIndex + 1}.{index + 1} {record.courseName}
<span className="icon iconfont tip">&#xe834;</span> </span>
} </Otherwise>
{ record.courseType==="LIVE" && </Choose>
<span className="course-state">{courseStateShow[record.courseState].title}</span>
}
{record.courseState === 'EXPIRED' && <span className='icon iconfont tip'>&#xe834;</span>}
{record.courseType === 'LIVE' && <span className='course-state'>{courseStateShow[record.courseState].title}</span>}
</div> </div>
<div className="course-operate"> <div className='course-operate'>
<DragHandle /> <DragHandle />
{/* <span className="operate__item">
<span className="icon iconfont">&#xe6f5;</span> <span
<span className="text" onClick={(e)=>{const { dataSource } = this.state; record.type="input";this.setState({dataSource})}}>重命名</span> className='operate__item'
</span> */} onClick={() => {
<span className="operate__item" onClick={()=>{this.handleDeleteCourse(parentIndex,index)}}> this.handleDeleteCourse(parentIndex, index)
<span className="icon iconfont">&#xe6f6;</span> }}>
<span className="text">删除</span> <span className='icon iconfont'>&#xe6f6;</span>
<span className='text'>删除</span>
</span> </span>
</div> </div>
</div> </div>
)
} }
render() { render() {
const { dataSource,selectedTaskIndex,relatedCourseModalVisible} = this.state; const { dataSource, selectedTaskIndex, relatedCourseModalVisible } = this.state
console.log('dataSource', dataSource)
const { data } = this.props;
return ( return (
<div className="training-task"> <div className='training-task'>
<SortableTaskContainer <SortableTaskContainer useDragHandle disableAutoscroll helperClass='row-dragging' onSortEnd={this.onTaskSortEnd} className='plan-task-sort-container'>
useDragHandle {dataSource.map((item, index) => (
disableAutoscroll <SortableTaskItem taskitem={this.renderTaskItem(item, index)} index={index} key={index}></SortableTaskItem>
helperClass="row-dragging" ))}
onSortEnd={this.onTaskSortEnd}
className="plan-task-sort-container"
>
{dataSource.map((item, index) =>
<SortableTaskItem taskItem={this.renderTaskItem(item,index)} index={index}>
</SortableTaskItem>
)}
</SortableTaskContainer> </SortableTaskContainer>
<div className="add-task-con"> <div className='add-task-con'>
{dataSource.length > 9 ? <Choose>
<span className="add-task-btn-disabled"><span>+</span><span>添加任务</span></span> <When condition={dataSource.length > 9}>
: <span className='add-task-btn-disabled'>
<span className="add-task-btn" onClick={()=>this.addTask()}><span>+</span><span>添加任务</span></span> <span>+</span>
} <span>添加任务</span>
</span>
</When>
<Otherwise>
<span className='add-task-btn' onClick={() => this.addTask()}>
<span>+</span>
<span>添加任务</span>
</span>
</Otherwise>
</Choose>
</div> </div>
{ relatedCourseModalVisible && {relatedCourseModalVisible && (
<RelatedCourseModal <RelatedCourseModal
selectedTaskIndex={selectedTaskIndex} selectedTaskIndex={selectedTaskIndex}
data={dataSource} data={dataSource}
...@@ -403,9 +492,9 @@ class TrainingTask extends React.Component { ...@@ -403,9 +492,9 @@ class TrainingTask extends React.Component {
onClose={this.closeRelatedCourseModal} onClose={this.closeRelatedCourseModal}
onSelect={this.confirmSelectCourse} onSelect={this.confirmSelectCourse}
/> />
} )}
</div> </div>
); )
} }
} }
......
import React from 'react'; import React from 'react'
import { withRouter } from "react-router-dom"; import { withRouter } from 'react-router-dom'
import {Table, Modal,Input,message,Tooltip} from 'antd'; import { Table, Modal, message, Tooltip } from 'antd'
import { PageControl } from "@/components"; import { PageControl } from '@/components'
import UserLearningDataFilter from './UserLearningDataFilter'; import UserLearningDataFilter from './UserLearningDataFilter'
import PlanService from '@/domains/plan-domain/planService'; import PlanService from '@/domains/plan-domain/planService'
import UserLearnDetailModal from "../modal/UserLearnDetailModal"; import UserLearnDetailModal from '../modal/UserLearnDetailModal'
import UnbundEmployeeModal from '../modal/UnbundEmployeeModal' import UnbundEmployeeModal from '../modal/UnbundEmployeeModal'
import User from '@/common/js/user'; import User from '@/common/js/user'
import './UserLearningData.less'; import './UserLearningData.less'
const userRole = User.getUserRole(); const { confirm } = Modal
const { Search } = Input;
const { confirm } = Modal;
const LearnState = { const LearnState = {
UN_PLAY: { UN_PLAY: {
text: "未开始" text: '未开始',
}, },
UNDER_WAY: { UNDER_WAY: {
text:"进行中" text: '进行中',
}, },
FINISH : { FINISH: {
text:'已完成' text: '已完成',
} },
}; }
class UserLearningData extends React.Component { class UserLearningData extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
const id = getParameterByName("id"); const id = getParameterByName('id')
this.state = { this.state = {
id, id,
dataSource:[], dataSource: [],
size:10, size: 10,
query: { query: {
current: 1, current: 1,
}, },
totalCount:0, totalCount: 0,
userLearnDetailModalSHow:false, userLearnDetailModalSHow: false,
unbundEmployeeModalVisible:false, unbundEmployeeModalVisible: false,
} }
} }
componentDidMount(){ componentDidMount() {
this.handleFetchDataList(); this.handleFetchDataList()
} }
handleFetchDataList = (_query)=>{ handleFetchDataList = (_query) => {
const { query ,size,id} = this.state; const { query, size, id } = this.state
const params ={ const params = {
...query, ...query,
..._query, ..._query,
size, size,
planId:id, planId: id,
storeId:User.getStoreId(), storeId: User.getStoreId(),
storeUserId:User.getStoreUserId(), storeUserId: User.getStoreUserId(),
} }
this.setState({ query: params }); this.setState({ query: params })
PlanService.getPlanCustomerRecordPage(params).then((res) => { PlanService.getPlanCustomerRecordPage(params).then((res) => {
const { result = {} } = res ; const { result = {} } = res
const { records = [], total = 0 } = result; const { records = [], total = 0 } = result
this.setState({ this.setState({
dataSource: records, dataSource: records,
totalCount: Number(total) totalCount: Number(total),
}); })
}); })
} }
onShowSizeChange = (current, size) => { onShowSizeChange = (current, size) => {
if (current == size) { if (current == size) {
return return
} }
this.setState({ this.setState(
size {
},()=>{this.handleFetchDataList()}) size,
},
() => {
this.handleFetchDataList()
}
)
}
handleChangeTable = (pagination, filters, sorter) => {
const { columnKey, order } = sorter
const { query } = this.state
let _columnKey
let _order
if (columnKey === 'latelyLearnTime' && order === 'ascend') {
_columnKey = 'LATE_LEARN_TIME'
_order = 'SORT_ASC'
} }
handleChangeTable = (pagination, filters, sorter)=> {
const { columnKey, order } = sorter;
const { query } = this.state;
let _columnKey;
let _order;
if (columnKey === 'latelyLearnTime' && order === 'ascend') {_columnKey="LATE_LEARN_TIME"; _order = 'SORT_ASC'; }
if (columnKey === 'latelyLearnTime' && order === 'descend') { _columnKey="LATE_LEARN_TIME"; _order = 'SORT_DESC';} if (columnKey === 'latelyLearnTime' && order === 'descend') {
_columnKey = 'LATE_LEARN_TIME'
_order = 'SORT_DESC'
}
if (columnKey === 'startLearnTime' && order === 'ascend') { _columnKey="START_LEARN_TIME"; _order = 'SORT_ASC'; } if (columnKey === 'startLearnTime' && order === 'ascend') {
_columnKey = 'START_LEARN_TIME'
_order = 'SORT_ASC'
}
if (columnKey === 'startLearnTime' && order === 'descend') { _columnKey="START_LEARN_TIME"; _order = 'SORT_DESC'; } if (columnKey === 'startLearnTime' && order === 'descend') {
_columnKey = 'START_LEARN_TIME'
_order = 'SORT_DESC'
}
if (columnKey === 'learnNum' && order === 'ascend') { _columnKey="LEARN_NUM"; _order = 'SORT_ASC'; } if (columnKey === 'learnNum' && order === 'ascend') {
_columnKey = 'LEARN_NUM'
_order = 'SORT_ASC'
}
if (columnKey === 'learnNum' && order === 'descend') { _columnKey="LEARN_NUM"; _order = 'SORT_DESC'; } if (columnKey === 'learnNum' && order === 'descend') {
_columnKey = 'LEARN_NUM'
_order = 'SORT_DESC'
}
const _query = { const _query = {
...query, ...query,
sortMap:{} sortMap: {},
};
_query.sortMap[_columnKey]=_order;
this.setState({
query:_query
}, ()=>this.handleFetchDataList())
} }
watchDetail = (record)=>{ _query.sortMap[_columnKey] = _order
this.setState(
{
query: _query,
},
() => this.handleFetchDataList()
)
}
watchDetail = (record) => {
this.setState({ this.setState({
userLearnDetailModalSHow:true, userLearnDetailModalSHow: true,
storeCustomerId:record.storeCustomerId, storeCustomerId: record.storeCustomerId,
planId:record.planId planId: record.planId,
}) })
} }
closeUserLearnDetailModal = ()=>{ closeUserLearnDetailModal = () => {
this.setState({ this.setState({
userLearnDetailModalSHow:false, userLearnDetailModalSHow: false,
}) })
} }
UnbundEmployee = (record)=>{ UnbundEmployee = (record) => {
if(User.getUserRole()==='CloudOperator'){ if (User.getUserRole() === 'CloudOperator') {
return confirm({ return confirm({
title: "你确定要解绑与学员的关系吗?", title: '你确定要解绑与学员的关系吗?',
content: "解绑后,学员该培训计划的学习数据将同步移出", content: '解绑后,学员该培训计划的学习数据将同步移出',
icon: ( icon: <span className='icon iconfont default-confirm-icon'>&#xe839; </span>,
<span className="icon iconfont default-confirm-icon">&#xe839; </span> okText: '删除',
), okType: 'danger',
okText: "删除", cancelText: '取消',
okType: "danger",
cancelText: "取消",
onOk: () => { onOk: () => {
this.handleConfirmUnbundEmployee(record.storeCustomerId); this.handleConfirmUnbundEmployee(record.storeCustomerId)
}, },
}); })
}else{ } else {
this.setState({ this.setState({
unbundEmployeeModalVisible:true, unbundEmployeeModalVisible: true,
storeCustomerId:record.storeCustomerId storeCustomerId: record.storeCustomerId,
}) })
} }
} }
handleConfirmUnbundEmployee=(storeCustomerId)=>{ handleConfirmUnbundEmployee = (storeCustomerId) => {
let removeUserIds = []; let removeUserIds = []
removeUserIds.push(storeCustomerId) removeUserIds.push(storeCustomerId)
const params = { const params = {
planId:getParameterByName("id"), planId: getParameterByName('id'),
removeUserIds, removeUserIds,
storeCustomerId:storeCustomerId, storeCustomerId: storeCustomerId,
storeId:User.getStoreId(), storeId: User.getStoreId(),
storeUserId:User.getStoreUserId() storeUserId: User.getStoreUserId(),
} }
PlanService.removePlanCustomer(params).then((res) => { PlanService.removePlanCustomer(params).then((res) => {
this.handleFetchDataList(); this.handleFetchDataList()
message.success('解绑成功'); message.success('解绑成功')
}); })
} }
handleCloseUnbundEmployeeModal = ()=>{ handleCloseUnbundEmployeeModal = () => {
this.setState({ this.setState({
unbundEmployeeModalVisible:false, unbundEmployeeModalVisible: false,
}) })
} }
// 请求表头 // 请求表头
parselumns = () => { parselumns = () => {
let columns; let columns
if(User.getUserRole() === "CloudManager" || User.getUserRole() === "StoreManager"){ if (User.getUserRole() === 'CloudManager' || User.getUserRole() === 'StoreManager') {
columns = [ columns = [
{ {
title: '学员', title: '学员',
key: 'storeCustomerName', key: 'storeCustomerName',
dataIndex: 'storeCustomerName', dataIndex: 'storeCustomerName',
render: (val, record) => { render: (val, record) => {
return ( return <div>{val}</div>
<div> },
{val}
</div>
)
}
}, },
{ {
title: '学习状态', title: '学习状态',
key: 'learnState', key: 'learnState',
dataIndex: 'learnState', dataIndex: 'learnState',
render: (val, record) => { render: (val, record) => {
return ( return <div>{LearnState[val].text}</div>
<div> },
{LearnState[val].text}
</div>
)
}
}, },
{ {
title: <span> title: (
<span>
<span>负责人</span> <span>负责人</span>
<Tooltip title="培训计划的分享者/跟进人"><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px'}}>&#xe61d;</i></Tooltip> <Tooltip title='培训计划的分享者/跟进人'>
</span>, <i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px' }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
key: 'userNameList', key: 'userNameList',
dataIndex: 'userNameList', dataIndex: 'userNameList',
render: (val, record) => { render: (val, record) => {
return ( return (
<div> <div>
{record.userNameList.map((item,index)=>{ {record.userNameList.map((item, index) => {
return <span>{item} { (index < record.userNameList.length-1)&&(<span> 、</span>)} </span> return (
<span>
{item} {index < record.userNameList.length - 1 && <span> 、</span>}{' '}
</span>
)
})} })}
</div> </div>
) )
} },
}, },
{ {
title: '最近学习时间', title: '最近学习时间',
key: 'latelyLearnTime', key: 'latelyLearnTime',
dataIndex: 'latelyLearnTime', dataIndex: 'latelyLearnTime',
sorter:true, sorter: true,
width:240, width: 240,
render: (val, record) => { render: (val, record) => {
return ( return `${formatDate('YYYY-MM-DD H:i', parseInt(record.latelyLearnTime))}`
`${formatDate('YYYY-MM-DD H:i',parseInt(record.latelyLearnTime))}` },
)
}
}, },
{ {
title: '开始学习时间', title: '开始学习时间',
key: 'startLearnTime', key: 'startLearnTime',
dataIndex: 'startLearnTime', dataIndex: 'startLearnTime',
width:240, width: 240,
sorter:true, sorter: true,
render: (val, record) => { render: (val, record) => {
return ( return <div>{formatDate('YYYY-MM-DD H:i', val)}</div>
<div> },
{formatDate('YYYY-MM-DD H:i', val)}
</div>
)
}
}, },
{ {
title: <span> title: (
<span>
<span>学习进度</span> <span>学习进度</span>
<Tooltip title="学员培训计划中达到“已完成”状态的课程数/总课程数"><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px'}}>&#xe61d;</i></Tooltip> <Tooltip title='学员培训计划中达到“已完成”状态的课程数/总课程数'>
</span>, <i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px' }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
key: 'learnNum', key: 'learnNum',
dataIndex: 'learnNum', dataIndex: 'learnNum',
width:130, width: 130,
sorter:true, sorter: true,
render: (val, record) => { render: (val, record) => {
return ( return (
<div className="learn-num"> <div className='learn-num'>
<span>{record.courseFinishNum}</span> <span>{record.courseFinishNum}</span>
<span>/</span> <span>/</span>
<span>{record.courseNum}</span> <span>{record.courseNum}</span>
</div> </div>
) )
} },
}, },
{ {
title: '操作', title: '操作',
...@@ -247,75 +272,73 @@ class UserLearningData extends React.Component { ...@@ -247,75 +272,73 @@ class UserLearningData extends React.Component {
dataIndex: 'operate', dataIndex: 'operate',
render: (val, record) => { render: (val, record) => {
return ( return (
<div className="operate-area"> <div className='operate-area'>
<span className="operate-item" onClick={()=>this.watchDetail(record)}>学习详情</span> <span className='operate-item' onClick={() => this.watchDetail(record)}>
<span className="split">|</span> 学习详情
<span className="operate-item" onClick={()=>{this.UnbundEmployee(record)}}>解绑</span> </span>
<span className='split'>|</span>
<span
className='operate-item'
onClick={() => {
this.UnbundEmployee(record)
}}>
解绑
</span>
</div> </div>
) )
} },
} },
]; ]
}else{ } else {
columns = [ columns = [
{ {
title: '学员', title: '学员',
key: 'storeCustomerName', key: 'storeCustomerName',
dataIndex: 'storeCustomerName', dataIndex: 'storeCustomerName',
render: (val, record) => { render: (val, record) => {
return ( return <div>{val}</div>
<div> },
{val}
</div>
)
}
}, },
{ {
title: '学习状态', title: '学习状态',
key: 'learnState', key: 'learnState',
dataIndex: 'learnState', dataIndex: 'learnState',
render: (val, record) => { render: (val, record) => {
return ( return <div>{LearnState[val].text}</div>
<div> },
{LearnState[val].text}
</div>
)
}
}, },
{ {
title: '最近学习时间', title: '最近学习时间',
key: 'latelyLearnTime', key: 'latelyLearnTime',
dataIndex: 'latelyLearnTime', dataIndex: 'latelyLearnTime',
sorter:true, sorter: true,
render: (val, record) => { render: (val, record) => {
return ( return <div>{formatDate('YYYY-MM-DD H:i', val)}</div>
<div> },
{formatDate('YYYY-MM-DD H:i', val)}
</div>
)
}
}, },
{ {
title: '开始学习时间', title: '开始学习时间',
key: 'startLearnTime', key: 'startLearnTime',
dataIndex: 'startLearnTime', dataIndex: 'startLearnTime',
sorter:true, sorter: true,
render: (val, record) => { render: (val, record) => {
return ( return <div>{formatDate('YYYY-MM-DD H:i', val)}</div>
<div> },
{formatDate('YYYY-MM-DD H:i', val)}
</div>
)
}
}, },
{ {
title: <span> title: (
<span>
<span>学习进度</span> <span>学习进度</span>
<Tooltip title="学员培训计划中达到“已完成”状态的课程数/总课程数"><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px'}}>&#xe61d;</i></Tooltip> <Tooltip title='学员培训计划中达到“已完成”状态的课程数/总课程数'>
</span>, <i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px' }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
key: 'learnNum', key: 'learnNum',
dataIndex: 'learnNum', dataIndex: 'learnNum',
sorter:true, sorter: true,
render: (val, record) => { render: (val, record) => {
return ( return (
<div> <div>
...@@ -324,7 +347,7 @@ class UserLearningData extends React.Component { ...@@ -324,7 +347,7 @@ class UserLearningData extends React.Component {
<span>{record.courseNum}</span> <span>{record.courseNum}</span>
</div> </div>
) )
} },
}, },
{ {
title: '操作', title: '操作',
...@@ -332,70 +355,84 @@ class UserLearningData extends React.Component { ...@@ -332,70 +355,84 @@ class UserLearningData extends React.Component {
dataIndex: 'operate', dataIndex: 'operate',
render: (val, record) => { render: (val, record) => {
return ( return (
<div className="operate-area"> <div className='operate-area'>
<span className="operate-item" onClick={()=>this.watchDetail(record)}>学习详情</span> <span className='operate-item' onClick={() => this.watchDetail(record)}>
<span className="split">|</span> 学习详情
<span className="operate-item" onClick={()=>{this.UnbundEmployee(record)}}>解绑</span> </span>
<span className='split'>|</span>
<span
className='operate-item'
onClick={() => {
this.UnbundEmployee(record)
}}>
解绑
</span>
</div> </div>
) )
},
},
]
} }
}
];
}
return columns; return columns
} }
render() { render() {
const { dataSource,query,size,totalCount,userLearnDetailModalSHow,storeCustomerId,planId,unbundEmployeeModalVisible} = this.state; const { dataSource, query, size, totalCount, userLearnDetailModalSHow, storeCustomerId, planId, unbundEmployeeModalVisible } = this.state
return ( return (
<div className="user-learning-data"> <div className='user-learning-data'>
<div className="search-container"> <div className='search-container'>
<UserLearningDataFilter onChange={this.handleFetchDataList}/> <UserLearningDataFilter onChange={this.handleFetchDataList} />
</div> </div>
<div> <div>
<Table <Table
rowKey={record => record.storeCustomerId} rowKey={(record) => record.storeCustomerId}
dataSource={dataSource} dataSource={dataSource}
columns={this.parselumns()} columns={this.parselumns()}
pagination={false} pagination={false}
className="user-learning-table" className='user-learning-table'
onChange={this.handleChangeTable} onChange={this.handleChangeTable}
showSorterTooltip={false} showSorterTooltip={false}
bordered bordered
/> />
{dataSource.length >0 && {dataSource.length > 0 && (
<div className="box-footer"> <div className='box-footer'>
<PageControl <PageControl
current={query.current - 1} current={query.current - 1}
pageSize={size} pageSize={size}
total={totalCount} total={totalCount}
toPage={(page) => { toPage={(page) => {
const _query = {...query, current: page + 1}; const _query = { ...query, current: page + 1 }
this.setState({ this.setState(
query:_query {
},()=>{ this.handleFetchDataList()}) query: _query,
},
() => {
this.handleFetchDataList()
}
)
}} }}
onShowSizeChange={this.onShowSizeChange} onShowSizeChange={this.onShowSizeChange}
/> />
</div> </div>
} )}
</div> </div>
{ userLearnDetailModalSHow && {userLearnDetailModalSHow && (
<UserLearnDetailModal storeCustomerId={storeCustomerId} planId={planId} visible={userLearnDetailModalSHow} onClose={this.closeUserLearnDetailModal}/> <UserLearnDetailModal storeCustomerId={storeCustomerId} planId={planId} visible={userLearnDetailModalSHow} onClose={this.closeUserLearnDetailModal} />
} )}
{unbundEmployeeModalVisible && {unbundEmployeeModalVisible && (
<UnbundEmployeeModal <UnbundEmployeeModal
visible={unbundEmployeeModalVisible} visible={unbundEmployeeModalVisible}
onClose={this.handleCloseUnbundEmployeeModal} onClose={this.handleCloseUnbundEmployeeModal}
storeCustomerId={storeCustomerId} storeCustomerId={storeCustomerId}
onConfirm={()=>{this.handleFetchDataList();this.handleCloseUnbundEmployeeModal();}} onConfirm={() => {
this.handleFetchDataList()
this.handleCloseUnbundEmployeeModal()
}}
/> />
} )}
</div> </div>
) )
} }
} }
export default withRouter(UserLearningData); export default withRouter(UserLearningData)
\ No newline at end of file
.user-learning-data{ .user-learning-data {
.user-learning-table{ .user-learning-table {
margin-top:4px; margin-top: 4px;
} }
.operate-area{ .operate-area {
.operate-item{ .operate-item {
font-size:14px; font-size: 14px;
color:#5289FA; color: #2966ff;
cursor: pointer; cursor: pointer;
} }
.split{ .split {
margin:0 4px; margin: 0 4px;
color: #BFBFBF; color: #bfbfbf;
} }
} }
.learn-num{ .learn-num {
text-align:right; text-align: right;
margin-right:20px; margin-right: 20px;
}
.ant-table-column-sorters:after {
display: none;
} }
} }
...@@ -6,93 +6,94 @@ ...@@ -6,93 +6,94 @@
* @Description: 大班直播分享弹窗 * @Description: 大班直播分享弹窗
*/ */
import React from 'react'; import React from 'react'
import { Modal, Input, Button, message } from 'antd'; import { Modal, Button, message } from 'antd'
import domtoimage from 'dom-to-image'; import domtoimage from 'dom-to-image'
import qrcode from "@/libs/qrcode/qrcode.js"; import qrcode from '@/libs/qrcode/qrcode.js'
import User from '@/common/js/user'; import User from '@/common/js/user'
import $ from 'jquery'; import $ from 'jquery'
import CourseService from "@/domains/course-domain/CourseService"; import CourseService from '@/domains/course-domain/CourseService'
import './SharePlanModal.less'; import './SharePlanModal.less'
const storeName = User.getStoreName(); const DEFAULT_COVER = 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'
const DEFAULT_COVER = 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png';
class ShareLiveModal extends React.Component { class ShareLiveModal extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
shareUrl: '' shareUrl: '',
} }
} }
componentDidMount() { componentDidMount() {
// 获取短链接 // 获取短链接
this.handleConvertShortUrl(); this.handleConvertShortUrl()
} }
handleConvertShortUrl = () => { handleConvertShortUrl = () => {
const { longUrl } = this.props.data; const { longUrl } = this.props.data
// 发请求 // 发请求
CourseService.getQrcode({ CourseService.getQrcode({
urls: [longUrl] urls: [longUrl],
}).then((res) => { }).then((res) => {
const { result = [] } = res; const { result = [] } = res
this.setState({ this.setState(
shareUrl: result[0].shortUrl {
}, () => { shareUrl: result[0].shortUrl,
const qrcodeWrapDom = document.querySelector('#qrcodeWrap'); },
() => {
const qrcodeWrapDom = document.querySelector('#qrcodeWrap')
const qrcodeNode = new qrcode({ const qrcodeNode = new qrcode({
text: this.state.shareUrl, text: this.state.shareUrl,
size: 98, size: 98,
}) })
qrcodeWrapDom.appendChild(qrcodeNode); qrcodeWrapDom.appendChild(qrcodeNode)
}); }
)
}) })
} }
componentWillUnmount() { componentWillUnmount() {
// 页面销毁之前清空定时器 // 页面销毁之前清空定时器
clearTimeout(this.timer); clearTimeout(this.timer)
} }
// 下载海报 // 下载海报
handleDownloadPoster = () => { handleDownloadPoster = () => {
this.setState({ this.setState(
showImg:true, {
time:new Date().valueOf() showImg: true,
},()=>{ time: new Date().valueOf(),
this.setState({time:new Date().valueOf()},()=>{ },
let node = document.getElementById('poster'); () => {
domtoimage.toPng(node) this.setState({ time: new Date().valueOf() }, () => {
.then((imgData) => { let node = document.getElementById('poster')
domtoimage.toPng(node).then((imgData) => {
console.log(imgData) console.log(imgData)
const download = document.createElement('a'); const download = document.createElement('a')
const { planName } = this.props.data; const { planName } = this.props.data
$(download).attr('href', imgData).attr('download', `${planName}.png`).get(0).click(); $(download).attr('href', imgData).attr('download', `${planName}.png`).get(0).click()
})
}) })
}) })
}
)
} }
// 复制分享链接 // 复制分享链接
handleCopy = () => { handleCopy = () => {
const textContent = document.getElementById('shareUrl').innerText; const textContent = document.getElementById('shareUrl').innerText
window.copyText(textContent); window.copyText(textContent)
message.success('复制成功!'); message.success('复制成功!')
} }
render() { render() {
const {data} = this.props; const { data } = this.props
const { planName, coverUrl = DEFAULT_COVER} = data; const { planName, coverUrl = DEFAULT_COVER } = data
const { shareUrl,showImg,time} = this.state; const { shareUrl, showImg, time } = this.state
return ( return (
<Modal <Modal
title={'分享培训计划'} title={'分享培训计划'}
...@@ -100,52 +101,55 @@ class ShareLiveModal extends React.Component { ...@@ -100,52 +101,55 @@ class ShareLiveModal extends React.Component {
visible={true} visible={true}
footer={null} footer={null}
maskClosable={false} maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>} closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
className="share-live-modal" className='share-live-modal'
onCancel={this.props.close} onCancel={this.props.close}>
> <div className='left'>
<div className="left"> <div id='poster'>
<div id="poster"> <div className='store-name'>
<div className="store-name"> <span className='text'>{User.getStoreName()}</span>
<span className="text">{storeName}</span>
</div> </div>
<div className="course-name-title">邀请你参与培训:</div> <div className='course-name-title'>邀请你参与培训:</div>
<div class="live-couse-name">{planName}</div> <div class='live-couse-name'>{planName}</div>
{ <Choose>
showImg ? <img <When condition={showImg}>
crossOrigin='*' <img crossOrigin='*' src={coverUrl + `?=${time}`} className='course-cover' alt='' />
src={coverUrl+`?=${time}`} </When>
className="course-cover" <Otherwise>
/>: <img <img src={coverUrl + `?=${time}`} className='course-cover' alt='' />
src={coverUrl+`?=${time}`} </Otherwise>
className="course-cover" </Choose>
/>
} <div className='qrcode-wrap'>
<div className="qrcode-wrap"> <div className='qrcode-wrap__left'>
<div className="qrcode-wrap__left"> <div className='text'>长按识别二维码进入观看</div>
<div className="text">长按识别二维码进入观看</div> <img className='finger' src='https://image.xiaomaiketang.com/xm/thpkWDwJsC.png' alt='' />
<img className="finger" src="https://image.xiaomaiketang.com/xm/thpkWDwJsC.png"/>
</div> </div>
<div className="qrcode-wrap__right" id="qrcodeWrap"> <div className='qrcode-wrap__right' id='qrcodeWrap'></div>
</div> </div>
</div> </div>
</div> </div>
<div className='right'>
<div className='share-poster right__item'>
<div className='title'>① 海报分享</div>
<div className='sub-title'>学员可通过微信扫描海报二维码,查看培训计划</div>
<div className='content' onClick={this.handleDownloadPoster}>
下载海报
</div> </div>
<div className="right">
<div className="share-poster right__item">
<div className="title">① 海报分享</div>
<div className="sub-title">学员可通过微信扫描海报二维码,查看培训计划</div>
<div className="content" onClick={this.handleDownloadPoster}>下载海报</div>
</div> </div>
<div className="share-url right__item"> <div className='share-url right__item'>
<div className="title">② 链接分享</div> <div className='title'>② 链接分享</div>
<div className="sub-title">学员可通过微信或浏览器打开以下链接,查看培训计划</div> <div className='sub-title'>学员可通过微信或浏览器打开以下链接,查看培训计划</div>
<div className="content url-content"> <div className='content url-content'>
<div className="share-url" id="shareUrl">{shareUrl}</div> <div className='share-url' id='shareUrl'>
<Button type="primary" onClick={this.handleCopy}>复制</Button> {shareUrl}
</div>
<Button type='primary' onClick={this.handleCopy}>
复制
</Button>
</div> </div>
</div> </div>
</div> </div>
...@@ -154,4 +158,4 @@ class ShareLiveModal extends React.Component { ...@@ -154,4 +158,4 @@ class ShareLiveModal extends React.Component {
} }
} }
export default ShareLiveModal; export default ShareLiveModal
import React from 'react'; import React from "react"
import {Table, Modal,Input,message,Button,Tooltip} from 'antd'; import _ from "underscore"
import { PageControl } from "@/components"; import { Table, Radio, Tabs, Modal, Input, message, Button, Tooltip } from "antd"
import CourseService from "@/domains/course-domain/CourseService";
import User from '@/common/js/user'; import { PageControl } from "@/components"
import Service from '@/common/js/service';
import { Tabs } from 'antd'; import CourseService from "@/domains/course-domain/CourseService"
import './relatedCourseModal.less'; import User from "@/common/js/user"
import _ from "underscore"; import Service from "@/common/js/service"
import dealTimeDuration from "../../course-manage/utils/dealTimeDuration"; import dealTimeDuration from "../../course-manage/utils/dealTimeDuration"
const { Search } = Input; import "./relatedCourseModal.less"
const { TabPane } = Tabs;
const { Search } = Input
const { TabPane } = Tabs
const courseStateShow = { const courseStateShow = {
UN_START: { UN_START: {
code: 1, code: 1,
title: "待开课", title: "待开课",
color: "#FFB714", color: "#FFB714"
}, },
STARTING: { STARTING: {
code: 2, code: 2,
title: "上课中", title: "上课中",
color: "#238FFF", color: "#238FFF"
}, },
FINISH: { FINISH: {
code: 3, code: 3,
title: "已完成", title: "已完成",
color: "#3BBDAA", color: "#3BBDAA"
}, },
EXPIRED: { EXPIRED: {
code: 4, code: 4,
title: "未成功开课", title: "未成功开课",
color: "#999", color: "#999"
}, }
}; }
class SelectOperatorModal extends React.Component { class SelectOperatorModal extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
liveDataSource:[], liveDataSource: [],
liveSize:10, liveSize: 10,
liveQuery: { liveQuery: {
current: 1, current: 1
}, },
liveTotalCount:0, liveTotalCount: 0,
selectLive:[],//弹窗内已选择的直播课程 selectLive: [], //弹窗内已选择的直播课程
currentCourseListData:[], currentCourseListData: [],
currentLiveCourseListData:[], //页面中已关联的直播课程 currentLiveCourseListData: [], //页面中已关联的直播课程
videoDataSource:[], videoCourseDivision: "internal",
videoSize:10, videoDataSource: {
external: [],
internal: []
},
videoSize: {
external: 10,
internal: 10
},
videoSearchName: {
external: "",
internal: ""
},
videoSearchDefalt: "",
videoQuery: { videoQuery: {
current: 1, external: {
current: 1
}, },
videoTotalCount:0, internal: {
selectVideo:[], //弹窗内已选择的视频课程 current: 1
currentVideoCourseListData:[], //页面中已关联的视频课程 }
},
pictureDataSource:[], videoTotalCount: {
pictureSize:10, external: 0,
internal: 0
},
selectVideo: {
external: [],
internal: []
}, //弹窗内已选择的视频课程
currentVideoCourseListData: {
external: [],
internal: []
}, //页面中已关联的视频课程
pictureDataSource: [],
pictureSize: 10,
pictureQuery: { pictureQuery: {
current: 1, current: 1
}, },
pictureTotalCount:0, pictureTotalCount: 0,
selectPicture:[], //弹窗内已选择的视频课程 selectPicture: [], //弹窗内已选择的视频课程
currentPictureCourseListData:[], //页面中已关联的视频课程 currentPictureCourseListData: [], //页面中已关联的视频课程
activeKey:'video', activeKey: "video",
currentTaskCourseData:this.props.data[this.props.selectedTaskIndex].courseList || [] currentTaskCourseData: this.props.data[this.props.selectedTaskIndex].courseList || []
} }
} }
componentDidMount() { componentDidMount() {
this.handleFetchLiveDataList(); this.handleFetchLiveDataList()
this.handleFetchVideoDataList(); this.handleFetchVideoDataList()
this.handleFetchPictureDataList(); this.handleFetchPictureDataList()
} }
// 获取直播课列表 // 获取直播课列表
handleFetchLiveDataList = () => { handleFetchLiveDataList = () => {
const {liveQuery,liveSize} = this.state; const { liveQuery, liveSize } = this.state
const { selectedTaskIndex } =this.props; const _data = [...this.props.data]
const _data = [...this.props.data]; let currentLiveCourseListData = []
let currentLiveCourseListData = []; _data.map((item) => {
_data.map((item,index) => { item.courseList.map((childItem, childIndex) => {
item.courseList.map((childItem,childIndex)=>{ if (childItem.courseType === "LIVE") {
if(childItem.courseType ==="LIVE"){
currentLiveCourseListData.push(childItem.courseId) currentLiveCourseListData.push(childItem.courseId)
} }
return childItem return childItem
}) })
return item return item
}); })
const params ={ const params = {
...liveQuery, ...liveQuery,
size:liveSize, size: liveSize,
excludeCourseIdList:currentLiveCourseListData excludeCourseIdList: currentLiveCourseListData
} }
CourseService.getLiveCloudCourseBasePage(params).then((res) => { CourseService.getLiveCloudCourseBasePage(params).then((res) => {
const { result = {} } = res ; const { result = {} } = res
const { records = [], total = 0 } = result; const { records = [], total = 0 } = result
this.setState({ this.setState({
liveDataSource: records, liveDataSource: records,
liveTotalCount: Number(total), liveTotalCount: Number(total),
currentLiveCourseListData currentLiveCourseListData
}); })
}); })
} }
// 获取视频课列表 // 获取视频课列表
handleFetchVideoDataList = () => { handleFetchVideoDataList = () => {
const {videoQuery,videoSize,videoTotalCount} = this.state; const { videoQuery, videoSize, videoDataSource, videoTotalCount, videoCourseDivision } = this.state
const { selectedTaskIndex } =this.props;
const _data = [...this.props.data]; const _data = [...this.props.data]
let currentVideoCourseListData = []; let currentVideoCourseListData = []
_data.map((item,index) => { _data.map((item, index) => {
item.courseList.map((childItem,childIndex)=>{ item.courseList.map((childItem, childIndex) => {
if(childItem.courseType ==="VOICE"){ if (childItem.courseType === "VOICE") {
currentVideoCourseListData.push(childItem.courseId) currentVideoCourseListData.push(childItem.courseId)
} }
return childItem return childItem
}) })
return item return item
}); })
const params ={ const params = {
...videoQuery, ...videoQuery[videoCourseDivision],
size:videoSize, size: videoSize[videoCourseDivision],
excludeCourseIdList:currentVideoCourseListData courseDivision: videoCourseDivision === "internal" ? "INTERNAL" : "EXTERNAL",
excludeCourseIdList: currentVideoCourseListData
} }
CourseService.videoScheduleBasePage(params).then((res) => { CourseService.videoScheduleBasePage(params).then((res) => {
const { result = {} } = res ; const { result = {} } = res
const { records = [], total = 0 } = result; console.log("result", result)
const { records = [], total = 0 } = result
this.setState({ this.setState({
videoDataSource: records, videoDataSource: {
videoTotalCount: Number(total), ...videoDataSource,
[videoCourseDivision]: records
},
videoTotalCount: {
...videoTotalCount,
[videoCourseDivision]: Number(total)
},
currentVideoCourseListData currentVideoCourseListData
}); })
}); })
} }
// 获取图文课列表 // 获取图文课列表
handleFetchPictureDataList = () => { handleFetchPictureDataList = () => {
const {pictureQuery,pictureSize} = this.state; const { pictureQuery, pictureSize } = this.state
const { selectedTaskIndex } =this.props; const _data = [...this.props.data]
const _data = [...this.props.data]; let currentPictureCourseListData = []
let currentPictureCourseListData = []; _data.map((item, index) => {
_data.map((item,index) => { item.courseList.map((childItem, childIndex) => {
item.courseList.map((childItem,childIndex)=>{ if (childItem.courseType === "PICTURE") {
if(childItem.courseType ==="PICTURE"){
currentPictureCourseListData.push(childItem.courseId) currentPictureCourseListData.push(childItem.courseId)
} }
return childItem return childItem
}) })
return item return item
}); })
const params ={ const params = {
...pictureQuery, ...pictureQuery,
size:pictureSize, size: pictureSize,
courseType:"PICTURE", courseType: "PICTURE",
storeId:User.getStoreId(), storeId: User.getStoreId(),
excludeCourseIdList:currentPictureCourseListData excludeCourseIdList: currentPictureCourseListData
} }
Service.Hades('public/hades/mediaCoursePage', params).then((res) => { Service.Hades("public/hades/mediaCoursePage", params).then((res) => {
const { result = {} } = res ; const { result = {} } = res
const { records = [], total = 0 } = result; const { records = [], total = 0 } = result
this.setState({ this.setState({
pictureDataSource: records, pictureDataSource: records,
pictureTotalCount: Number(total), pictureTotalCount: Number(total),
currentPictureCourseListData currentPictureCourseListData
}); })
}); })
} }
handleChangVideoCourseName = (value)=>{ handleChangVideoCourseName = (value) => {
const { videoQuery } = this.state; const { videoQuery, videoCourseDivision, videoSearchName } = this.state
videoQuery.courseName = value; videoQuery[videoCourseDivision].courseName = value
videoQuery.current = 1; videoQuery[videoCourseDivision].current = 1
this.setState({ this.setState({
videoQuery ...videoQuery,
videoSearchDefalt: value,
videoSearchName: {
...videoSearchName,
[videoCourseDivision]: value
}
}) })
} }
handleChangLiveCourseName = (value)=>{ handleChangLiveCourseName = (value) => {
const { liveQuery } = this.state; const { liveQuery } = this.state
liveQuery.courseName = value; liveQuery.courseName = value
liveQuery.current = 1; liveQuery.current = 1
this.setState({ this.setState({
liveQuery liveQuery
}) })
} }
handleChangPictureCourseName = (value)=>{ handleChangPictureCourseName = (value) => {
const { pictureQuery } = this.state; const { pictureQuery } = this.state
pictureQuery.courseName = value; pictureQuery.courseName = value
pictureQuery.current = 1; pictureQuery.current = 1
this.setState({ this.setState({
pictureQuery pictureQuery
}) })
} }
onShowLiveSizeChange = (current, size) => { onShowLiveSizeChange = (current, size) => {
if (current == size) { if (current === size) {
return return
} }
this.setState({ this.setState(
liveSize:size {
},()=>{this.handleFetchLiveDataList()}) liveSize: size
},
() => {
this.handleFetchLiveDataList()
}
)
} }
onShowVideoSizeChange = (current, size) => { onShowVideoSizeChange = (current, size) => {
if (current == size) { if (current === size) {
return return
} }
this.setState({ this.setState(
videoSize:size {
},()=>{this.handleFetchLiveDataList()}) videoSize: size
},
() => {
this.handleFetchLiveDataList()
}
)
} }
onShowPictureSizeChange = (current, size) => { onShowPictureSizeChange = (current, size) => {
if (current == size) { if (current === size) {
return return
} }
this.setState({ this.setState(
pictureSize:size {
},()=>{this.handleFetchPictureDataList()}) pictureSize: size
},
() => {
this.handleFetchPictureDataList()
}
)
} }
// 请求表头 // 请求表头
parseLiveColumns = () => { parseLiveColumns = () => {
const columns = [ const columns = [
{ {
title: <span><span>课程信息</span><Tooltip title="仅显示未关联课程,已关联课程不支持重复选择"><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px'}}>&#xe61d;</i></Tooltip></span>, title: (
key: 'course', <span>
dataIndex: 'course', <span>课程信息</span>
width:'40%', <Tooltip title='仅显示未关联课程,已关联课程不支持重复选择'>
<i className='icon iconfont' style={{ marginLeft: "5px", cursor: "pointer", color: "#bfbfbf", fontSize: "14px" }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
key: "course",
dataIndex: "course",
width: "40%",
render: (val, record) => { render: (val, record) => {
let hasCover = false; let hasCover = false
return ( return (
<div className="course-info"> <div className='course-info'>
{ {record.courseMediaVOS.map((item) => {
record.courseMediaVOS.map((item,index)=>{ if (item.contentType === "COVER") {
if( item.contentType === "COVER"){ hasCover = true
hasCover = true; return <img className='course-cover' src={item.mediaUrl} alt='' />
return <img className="course-cover" src={item.mediaUrl}/> }
} return null
}) })}
} <If condition={!hasCover}>
{ !hasCover && <img className='course-cover' src={"https://image.xiaomaiketang.com/xm/YNfi45JwFA.png"} alt='' />
<img className="course-cover" src={'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'} /> </If>
}
<div> <div>
<div className="course-name">{record.courseName}</div> <div className='course-name'>{record.courseName}</div>
<span className="course-status" style={{color:courseStateShow[record.courseState].color,border:`1px solid ${courseStateShow[record.courseState].color}`}}>{courseStateShow[record.courseState].title}</span> <span
className='course-status'
style={{ color: courseStateShow[record.courseState].color, border: `1px solid ${courseStateShow[record.courseState].color}` }}>
{courseStateShow[record.courseState].title}
</span>
</div> </div>
</div> </div>
) )
} }
}, },
{ {
title: '上课时间', title: "上课时间",
key: 'courseTime', key: "courseTime",
dataIndex: 'courseTime', dataIndex: "courseTime",
width:'40%', width: "40%",
render: (val, record) => { render: (val, record) => {
return ( return (
<div> <div>
<div>{formatDate('YYYY-MM-DD', record.startTime)}</div> <div>{formatDate("YYYY-MM-DD", record.startTime)}</div>
<div>{formatDate('H:i', record.startTime)}~{formatDate('H:i', record.endTime)}</div> <div>
{formatDate("H:i", record.startTime)}~{formatDate("H:i", record.endTime)}
</div>
</div> </div>
) )
} }
}, },
{ {
title: '学院展示', title: "学院展示",
key: 'shelfState', key: "shelfState",
dataIndex: 'shelfState', dataIndex: "shelfState",
width:'20%', width: "20%",
render: (val, record) => { render: (val, record) => {
return ( return (
<span> <span>
{record.shelfState==="YES"? <Choose>
<When condition={record.shelfState === "YES"}>
<span>开启</span> <span>开启</span>
: </When>
<Otherwise>
<span>关闭</span> <span>关闭</span>
} </Otherwise>
</Choose>
</span> </span>
) )
} }
}, }
]
]; return columns
return columns;
} }
// 请求表头 // 请求表头
parseVideoColumns = () => { parseVideoColumns = () => {
const { videoCourseDivision } = this.state
const columns = [ const columns = [
{ {
title: <span><span>课程信息</span><Tooltip title="仅显示未关联课程,已关联课程不支持重复选择"><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px'}}>&#xe61d;</i></Tooltip></span>, title: (
key: 'course', <span>
dataIndex: 'course', <span>课程信息</span>
width:'60%', <Tooltip title='仅显示未关联课程,已关联课程不支持重复选择'>
<i className='icon iconfont' style={{ marginLeft: "5px", cursor: "pointer", color: "#bfbfbf", fontSize: "14px" }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
key: "course",
dataIndex: "course",
width: "40%",
render: (val, record) => { render: (val, record) => {
const { coverUrl, scheduleVideoUrl } = record; const { coverUrl, scheduleVideoUrl } = record
return ( return (
<div className="course-info"> <div className='course-info'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */} {/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
<img className="course-cover" src={coverUrl || `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`}/> <img
<div className="course-name">{record.courseName}</div> className='course-cover'
src={
coverUrl ||
(videoCourseDivision === "internal"
? `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`
: "https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png")
}
alt=''
/>
<div className='course-name'>{record.courseName}</div>
</div> </div>
) )
} }
}, },
{ {
title: '课程时长', title: "课程时长",
key: 'courseTime', key: "courseTime",
dataIndex: 'courseTime', dataIndex: "courseTime",
width:'20%', width: "20%",
render: (val, record) => { render: (val, record) => {
return ( return <span className='course-status'>{dealTimeDuration(record.videoDuration)}</span>
<span className="course-status">{dealTimeDuration(record.videoDuration)}</span>
)
} }
}, },
{ {
title: '学院展示', title: "学院展示",
key: 'shelfState', key: "shelfState",
dataIndex: 'shelfState', dataIndex: "shelfState",
width:'20%', width: "20%",
render: (val, record) => { render: (val, record) => {
return ( return (
<span> <Choose>
{record.shelfState==="YES"? <When condition={record.shelfState === "YES"}>
<span>开启</span> <span>开启</span>
: </When>
<Otherwise>
<span>关闭</span> <span>关闭</span>
} </Otherwise>
</span> </Choose>
) )
} }
} }
]
]; return columns
return columns;
} }
// 请求表头 // 请求表头
parsePictureColumns = () => { parsePictureColumns = () => {
const columns = [ const columns = [
{ {
title: <span><span>课程信息</span><Tooltip title="仅显示未关联课程,已关联课程不支持重复选择"><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px'}}>&#xe61d;</i></Tooltip></span>, title: (
key: 'course', <span>
dataIndex: 'course', <span>课程信息</span>
width:'55%', <Tooltip title='仅显示未关联课程,已关联课程不支持重复选择'>
<i className='icon iconfont' style={{ marginLeft: "5px", cursor: "pointer", color: "#bfbfbf", fontSize: "14px" }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
key: "course",
dataIndex: "course",
width: "55%",
render: (val, record) => { render: (val, record) => {
const { coverUrl } = record; const { coverUrl } = record
return ( return (
<div className="course-info"> <div className='course-info'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */} {/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
<img className="course-cover" src={coverUrl || 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'}/> <img className='course-cover' src={coverUrl || "https://image.xiaomaiketang.com/xm/YNfi45JwFA.png"} alt='' />
<div className="course-name">{record.courseName}</div> <div className='course-name'>{record.courseName}</div>
</div> </div>
) )
} }
}, },
{ {
title: '更新时间', title: "更新时间",
key: 'updated', key: "updated",
dataIndex: 'updated', dataIndex: "updated",
width:'25%', width: "25%",
render: (val, record) => { render: (val, record) => {
return ( return <span className='course-status'>{formatDate("YYYY-MM-DD", record.updated)}</span>
<span className="course-status">{formatDate('YYYY-MM-DD',record.updated)}</span>
)
} }
}, },
{ {
title: '学院展示', title: "学院展示",
key: 'shelfState', key: "shelfState",
dataIndex: 'shelfState', dataIndex: "shelfState",
width:'20%', width: "20%",
render: (val, record) => { render: (val, record) => {
return ( return <span>{record.shelfState === "YES" ? "开启" : "关闭"}</span>
<span>
{record.shelfState==="YES"?
<span>开启</span>
:
<span>关闭</span>
}
</span>
)
} }
} }
]
]; return columns
return columns;
} }
selectLiveList = (record,selected) =>{ selectLiveList = (record, selected) => {
const {selectVideo,currentTaskCourseData,selectLive,selectPicture} = this.state; const { selectVideo, currentTaskCourseData, selectLive, selectPicture } = this.state
let _list = []; let _list = []
if (selected || !_.find(selectLive, (item) => item.liveCourseId == record.liveCourseId)) { if (selected || !_.find(selectLive, (item) => item.liveCourseId === record.liveCourseId)) {
_list = _.uniq(selectLive.concat([record]), false, (item) => item.liveCourseId); _list = _.uniq(selectLive.concat([record]), false, (item) => item.liveCourseId)
} else { } else {
_list = _.reject(selectLive, (item) => item.liveCourseId === record.liveCourseId); _list = _.reject(selectLive, (item) => item.liveCourseId === record.liveCourseId)
} }
if(_list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length > 20){ if (_list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程'); message.warning("无法继续选择,一个任务最多关联20个课程")
return; return
} }
this.setState({selectLive:_list}); this.setState({ selectLive: _list })
} }
selectVideoList = (record,selected) =>{ selectVideoList = (record, selected) => {
const {selectVideo,currentTaskCourseData,selectLive,selectPicture} = this.state; const { selectVideo, currentTaskCourseData, selectLive, selectPicture, videoCourseDivision } = this.state
let _list = [];
if (selected || !_.find(selectVideo, (item) => item.id == record.id)) { let { [videoCourseDivision]: selectList } = selectVideo
_list = _.uniq(selectVideo.concat([record]), false, (item) => item.id);
let _list = []
if (selected || !_.find(selectList, (item) => item.id === record.id)) {
_list = _.uniq(selectList.concat([record]), false, (item) => item.id)
} else { } else {
_list = _.reject(selectVideo, (item) => item.id === record.id); _list = _.reject(selectList, (item) => item.id === record.id)
} }
if(_list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length > 20){ if (_list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程'); message.warning("无法继续选择,一个任务最多关联20个课程")
return; return
}
this.setState({
selectVideo: {
...selectVideo,
[videoCourseDivision]: _list
} }
this.setState({selectVideo:_list}); })
} }
selectPictureList = (record,selected) =>{ selectPictureList = (record, selected) => {
const {selectVideo,currentTaskCourseData,selectLive,selectPicture} = this.state; const { selectVideo, currentTaskCourseData, selectLive, selectPicture } = this.state
let _list = []; let _list = []
if (selected || !_.find(selectPicture, (item) => item.id == record.id)) { if (selected || !_.find(selectPicture, (item) => item.id === record.id)) {
_list = _.uniq(selectPicture.concat([record]), false, (item) => item.id); _list = _.uniq(selectPicture.concat([record]), false, (item) => item.id)
} else { } else {
_list = _.reject(selectPicture, (item) => item.id === record.id); _list = _.reject(selectPicture, (item) => item.id === record.id)
} }
if(_list.length + currentTaskCourseData.length + selectLive.length + selectVideo.length > 20){ if (_list.length + currentTaskCourseData.length + selectLive.length + selectVideo.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程'); message.warning("无法继续选择,一个任务最多关联20个课程")
return; return
} }
this.setState({selectPicture:_list}); this.setState({ selectPicture: _list })
} }
clearSelectCourse = ()=>{ clearSelectCourse = () => {
this.setState({ this.setState({
selectLive:[], selectLive: [],
selectVideo:[], selectVideo: {
selectPicture:[] internal: [],
external: []
},
selectPicture: []
}) })
} }
handleSelectVideo = (selectVideo)=>{ handleSelectVideo = (selectVideo) => {
return selectVideo.map((item,index)=>{ return selectVideo.map((item) => {
let _item = {}; let _item = {}
_item.courseId = item.id; _item.courseId = item.id
_item.courseType = "VOICE"; _item.courseType = "VOICE"
_item.courseName = item.courseName; _item.courseName = item.courseName
return _item; return _item
}) })
} }
handleSelectLive = (selectLive)=>{
return selectLive.map((item,index)=>{ handleSelectLive = (selectLive) => {
let _item = {}; return selectLive.map((item, index) => {
_item.courseId = item.liveCourseId; let _item = {}
_item.courseType = "LIVE"; _item.courseId = item.liveCourseId
_item.courseName = item.courseName; _item.courseType = "LIVE"
_item.courseState = item.courseState; _item.courseName = item.courseName
return _item; _item.courseState = item.courseState
return _item
}) })
} }
videoCourseDivisionChange = (e) => {
const { videoSearchName } = this.state
this.setState(
{
videoCourseDivision: e.target.value,
videoSearchDefalt: videoSearchName[e.target.value]
},
() => {
this.handleFetchVideoDataList()
}
)
}
handleSelectPicture = (selectPicture)=>{ handleSelectPicture = (selectPicture) => {
return selectPicture.map((item, index) => {
return selectPicture.map((item,index)=>{ let _item = {}
let _item = {}; _item.courseId = item.id
_item.courseId = item.id; _item.courseType = "PICTURE"
_item.courseType = "PICTURE"; _item.courseName = item.courseName
_item.courseName = item.courseName; return _item
return _item;
}) })
} }
renderFooter = ()=>{ renderFooter = () => {
const { activeKey } = this.state; const { activeKey } = this.state
let href = ''; let href = ""
switch (activeKey){ switch (activeKey) {
case 'live': case "live":
href = <a target='_blank' className="link-create-course" href={window.location.origin + window.location.pathname + '#/create-live-course?type=add'} onClick={this.props.onClose}>没有找到需要的直播课?<span>去创建</span></a> href = (
break; <a
case 'video': target='_blank'
href = <a target='_blank' className="link-create-course" href={window.location.origin + window.location.pathname + '#/create-video-course?type=add'} onClick={this.props.onClose}>没有找到需要的视频课?<span>去创建</span></a> rel='noopener noreferrer'
break; className='link-create-course'
case 'picture': href={window.location.origin + window.location.pathname + "#/create-live-course?type=add"}
href = <a target='_blank' className="link-create-course" href={window.location.origin + window.location.pathname + '#/create-graphics-course?type=add'} onClick={this.props.onClose}>没有找到需要的图文课?<span>去创建</span></a> onClick={this.props.onClose}>
break; 没有找到需要的直播课?<span>去创建</span>
</a>
)
break
case "video":
href = (
<a
target='_blank'
rel='noopener noreferrer'
className='link-create-course'
href={window.location.origin + window.location.pathname + "#/create-video-course?type=add"}
onClick={this.props.onClose}>
没有找到需要的视频课?<span>去创建</span>
</a>
)
break
case "picture":
href = (
<a
target='_blank'
rel='noopener noreferrer'
className='link-create-course'
href={window.location.origin + window.location.pathname + "#/create-graphics-course?type=add"}
onClick={this.props.onClose}>
没有找到需要的图文课?<span>去创建</span>
</a>
)
break
default:
break
} }
return href; return href
} }
render() { render() {
const { visible } = this.props; const { visible } = this.props
const { const {
liveDataSource, liveDataSource,
liveSize, liveSize,
...@@ -509,240 +649,327 @@ class SelectOperatorModal extends React.Component { ...@@ -509,240 +649,327 @@ class SelectOperatorModal extends React.Component {
videoDataSource, videoDataSource,
videoSize, videoSize,
videoQuery, videoQuery,
videoSearchDefalt,
videoTotalCount, videoTotalCount,
selectVideo, selectVideo,
currentTaskCourseData, currentTaskCourseData,
activeKey,
selectPicture, selectPicture,
pictureDataSource, pictureDataSource,
pictureSize, pictureSize,
pictureQuery, pictureQuery,
pictureTotalCount, pictureTotalCount,
} = this.state;
videoCourseDivision
} = this.state
return ( return (
<Modal <Modal
title="关联课程" title='关联课程'
onCancel={this.props.onClose} onCancel={this.props.onClose}
maskClosable={false} maskClosable={false}
visible={visible} visible={visible}
className="related-course-modal" className='related-course-modal'
closable={true} closable={true}
width={800} width={800}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>} closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
footer={[ footer={[
this.renderFooter() // <If condition={videoCourseDivision === 'internal'}>{}</If>,
, this.renderFooter(),
<Button <Button
onClick={() => { onClick={() => {
this.props.onClose() this.props.onClose()
}} }}>
>
取消 取消
</Button>, </Button>,
<Button type="primary" onClick={() => this.props.onSelect([...this.handleSelectVideo(selectVideo),...this.handleSelectLive(selectLive),...this.handleSelectPicture(selectPicture)]) }> <Button
type='primary'
onClick={() =>
this.props.onSelect([
...this.handleSelectVideo(selectVideo.internal),
...this.handleSelectVideo(selectVideo.external),
...this.handleSelectLive(selectLive),
...this.handleSelectPicture(selectPicture)
])
}>
确定 确定
</Button> </Button>
]} ]}>
>
<div> <div>
<Tabs defaultActiveKey="video" onChange={(activeKey)=>{this.setState({activeKey:activeKey})}}> <Tabs
<TabPane tab="视频课" key="video"> type='line'
<div className="search-container"> defaultActiveKey='live'
<Search enterButton={<span className="icon iconfont">&#xe832;</span>} placeholder="搜索课程名称" style={{ width: 200 }} onChange={(e) => { this.handleChangVideoCourseName(e.target.value)}} onSearch={ () => { this.handleFetchVideoDataList()}}/> onChange={(activeKey) => {
this.setState({ activeKey: activeKey })
}}>
<TabPane tab='直播课' key='live'>
<div className='search-container'>
<Search
enterButton={<span className='icon iconfont'>&#xe832;</span>}
placeholder='搜索课程名称'
style={{ width: 200 }}
onChange={(e) => {
this.handleChangLiveCourseName(e.target.value)
}}
onSearch={() => {
this.handleFetchLiveDataList()
}}
/>
</div> </div>
<div className="select-area">
<div className="select-box"> <div className='select-area'>
<div className='select-box'>
<div> <div>
<span className="icon iconfont tip-icon">&#xe61d;</span> <span className='icon iconfont tip-icon'>&#xe61d;</span>
<span className="select-num">已选择{selectVideo.length + selectLive.length + selectPicture.length }个</span> <span className='select-num'>
已选择{selectVideo.internal.length + selectVideo.external.length + selectLive.length + selectPicture.length}个
</span>
</div> </div>
<div> <div>
<span className="clear-btn" onClick={this.clearSelectCourse}>清空</span> <span className='clear-btn' onClick={this.clearSelectCourse}>
清空
</span>
</div> </div>
</div> </div>
<div className="related-box">该任务已关联{currentTaskCourseData.length}个课程,可继续选择{20- currentTaskCourseData.length }个</div> <div className='related-box'>
该任务已关联{currentTaskCourseData.length}个课程,可继续选择{20 - currentTaskCourseData.length}个
</div>
</div> </div>
<div> <div>
<Table <Table
rowKey={record => record.id} rowKey={(record) => record.liveCourseId}
dataSource={videoDataSource} dataSource={liveDataSource}
columns={this.parseVideoColumns()} columns={this.parseLiveColumns()}
pagination={false} pagination={false}
bordered bordered
rowSelection={{ rowSelection={{
type: 'checkbox', type: "checkbox",
selectedRowKeys: _.pluck(selectVideo, 'id'), selectedRowKeys: _.pluck(selectLive, "liveCourseId"),
onSelect: (record, selected) => { onSelect: (record, selected) => {
this.selectVideoList(record, selected); this.selectLiveList(record, selected)
}, },
onSelectAll: (selected, _selectedRows, changeRows) => { onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = []; let _list = []
if (selected) { if (selected) {
_list = _.uniq(selectVideo.concat(changeRows), false, (item) => item.id); _list = _.uniq(selectLive.concat(changeRows), false, (item) => item.liveCourseId)
} else { } else {
_list = _.reject(selectVideo, (item) => _.find(changeRows, (data) => data.id === item.id)); _list = _.reject(selectLive, (item) => _.find(changeRows, (data) => data.liveCourseId === item.liveCourseId))
} }
if(_list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length> 20){ if (_list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程'); message.warning("无法继续选择,一个任务最多关联20个课程")
const extraLength = (_list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length) -20; const extraLength = _list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length - 20
_list.splice(_list.length - extraLength,extraLength); _list.splice(_list.length - extraLength, extraLength)
}
this.setState({ selectLive: _list })
} }
this.setState({selectVideo:_list});
},
}} }}
/> />
{videoDataSource.length >0 && {liveDataSource.length > 0 && (
<div className="box-footer"> <div className='box-footer'>
<PageControl <PageControl
current={videoQuery.current - 1} current={liveQuery.current - 1}
pageSize={videoSize} pageSize={liveSize}
size="small" size='small'
total={videoTotalCount} total={liveTotalCount}
toPage={(page) => { toPage={(page) => {
const _query = {...videoQuery, current: page + 1}; const _query = { ...liveQuery, current: page + 1 }
this.setState({ this.setState(
videoQuery:_query {
},()=>{ this.handleFetchVideoDataList()}) liveQuery: _query
},
() => {
this.handleFetchLiveDataList()
}
)
}} }}
onShowSizeChange={this.onShowVideoSizeChange} onShowSizeChange={this.onShowLiveSizeChange}
/> />
</div> </div>
} )}
</div> </div>
</TabPane> </TabPane>
<TabPane tab="直播课" key="live">
<div className="search-container">
<Search enterButton={<span className="icon iconfont">&#xe832;</span>} placeholder="搜索课程名称" style={{ width: 200 }} onChange={(e) => { this.handleChangLiveCourseName(e.target.value)}} onSearch={ () => { this.handleFetchLiveDataList()}} />
</div>
<div className="select-area"> <TabPane tab='视频课' key='video'>
<div className="select-box"> <Radio.Group value={videoCourseDivision} onChange={this.videoCourseDivisionChange} style={{ marginBottom: 16 }}>
<Radio.Button value='internal'>内部课程</Radio.Button>
<Radio.Button value='external'>外部课程</Radio.Button>
</Radio.Group>
<div className='search-container'>
<Search
value={videoSearchDefalt}
enterButton={<span className='icon iconfont'>&#xe832;</span>}
placeholder='搜索课程名称'
style={{ width: 200 }}
onChange={(e) => {
this.handleChangVideoCourseName(e.target.value)
}}
onSearch={() => {
this.handleFetchVideoDataList()
}}
/>
</div>
<div className='select-area'>
<div className='select-box'>
<div> <div>
<span className="icon iconfont tip-icon">&#xe61d;</span> <span className='icon iconfont tip-icon'>&#xe61d;</span>
<span className="select-num">已选择{selectVideo.length + selectLive.length + selectPicture.length}个</span> <span className='select-num'>
已选择{selectVideo.internal.length + selectVideo.external.length + selectLive.length + selectPicture.length}个
</span>
</div> </div>
<div> <div>
<span className="clear-btn" onClick={this.clearSelectCourse}>清空</span> <span className='clear-btn' onClick={this.clearSelectCourse}>
清空
</span>
</div> </div>
</div> </div>
<div className="related-box">该任务已关联{currentTaskCourseData.length}个课程,可继续选择{20- currentTaskCourseData.length }个</div> <div className='related-box'>
该任务已关联{currentTaskCourseData.length}个课程,可继续选择{20 - currentTaskCourseData.length}个
</div>
</div> </div>
<div> <div>
<Table <Table
rowKey={record => record.liveCourseId} rowKey={(record) => record.id}
dataSource={liveDataSource} dataSource={videoDataSource[videoCourseDivision]}
columns={this.parseLiveColumns()} columns={this.parseVideoColumns()}
pagination={false} pagination={false}
bordered bordered
rowSelection={{ rowSelection={{
type: 'checkbox', type: "checkbox",
selectedRowKeys: _.pluck(selectLive, 'liveCourseId'), selectedRowKeys: _.pluck(selectVideo[videoCourseDivision], "id"),
onSelect: (record, selected) => { onSelect: (record, selected) => {
this.selectLiveList(record, selected) this.selectVideoList(record, selected)
}, },
onSelectAll: (selected, _selectedRows, changeRows) => { onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = []; let _list = []
if (selected) { if (selected) {
_list = _.uniq(selectLive.concat(changeRows), false, (item) => item.liveCourseId); _list = _.uniq(selectVideo[videoCourseDivision].concat(changeRows), false, (item) => item.id)
} else { } else {
_list = _.reject(selectLive, (item) => _.find(changeRows, (data) => data.liveCourseId === item.liveCourseId)); _list = _.reject(selectVideo[videoCourseDivision], (item) => _.find(changeRows, (data) => data.id === item.id))
} }
if(_list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length> 20){ if (_list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程'); message.warning("无法继续选择,一个任务最多关联20个课程")
const extraLength = (_list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length) -20; const extraLength = _list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length - 20
_list.splice(_list.length - extraLength,extraLength); _list.splice(_list.length - extraLength, extraLength)
}
this.setState({
selectVideo: {
...selectVideo,
[videoCourseDivision]: _list
}
})
} }
this.setState({selectLive:_list});
},
}} }}
/> />
{liveDataSource.length >0 && {videoDataSource[videoCourseDivision].length > 0 && (
<div className="box-footer"> <div className='box-footer'>
<PageControl <PageControl
current={liveQuery.current - 1} current={videoQuery[videoCourseDivision].current - 1}
pageSize={liveSize} pageSize={videoSize[videoCourseDivision]}
size="small" size='small'
total={liveTotalCount} total={videoTotalCount[videoCourseDivision]}
toPage={(page) => { toPage={(page) => {
const _query = {...liveQuery, current: page + 1}; const _query = { ...videoQuery[videoCourseDivision], current: page + 1 }
this.setState({
liveQuery:_query this.setState(
},()=>{ this.handleFetchLiveDataList()}) {
videoQuery: {
...videoQuery,
[videoCourseDivision]: _query
}
},
() => {
this.handleFetchVideoDataList()
}
)
}} }}
onShowSizeChange={this.onShowLiveSizeChange} onShowSizeChange={this.onShowVideoSizeChange}
/> />
</div> </div>
} )}
</div> </div>
</TabPane> </TabPane>
<TabPane tab="图文课" key="picture"> <TabPane tab='图文课' key='picture'>
<div className="search-container"> <div className='search-container'>
<Search enterButton={<span className="icon iconfont">&#xe832;</span>} placeholder="搜索课程名称" style={{ width: 200 }} onChange={(e) => { this.handleChangPictureCourseName(e.target.value)}} onSearch={ () => { this.handleFetchPictureDataList()}} /> <Search
enterButton={<span className='icon iconfont'>&#xe832;</span>}
placeholder='搜索课程名称'
style={{ width: 200 }}
onChange={(e) => {
this.handleChangPictureCourseName(e.target.value)
}}
onSearch={() => {
this.handleFetchPictureDataList()
}}
/>
</div> </div>
<div className="select-area"> <div className='select-area'>
<div className="select-box"> <div className='select-box'>
<div> <div>
<span className="icon iconfont tip-icon">&#xe61d;</span> <span className='icon iconfont tip-icon'>&#xe61d;</span>
<span className="select-num">已选择{selectVideo.length + selectLive.length + selectPicture.length }个</span> <span className='select-num'>
已选择{selectVideo.internal.length + selectVideo.external.length + selectLive.length + selectPicture.length}个
</span>
</div> </div>
<div> <div>
<span className="clear-btn" onClick={this.clearSelectCourse}>清空</span> <span className='clear-btn' onClick={this.clearSelectCourse}>
清空
</span>
</div>
</div> </div>
<div className='related-box'>
该任务已关联{currentTaskCourseData.length}个课程,可继续选择{20 - currentTaskCourseData.length}个
</div> </div>
<div className="related-box">该任务已关联{currentTaskCourseData.length}个课程,可继续选择{20- currentTaskCourseData.length }个</div>
</div> </div>
<div> <div>
<Table <Table
rowKey={record => record.id} rowKey={(record) => record.id}
dataSource={pictureDataSource} dataSource={pictureDataSource}
columns={this.parsePictureColumns()} columns={this.parsePictureColumns()}
pagination={false} pagination={false}
bordered bordered
rowSelection={{ rowSelection={{
type: 'checkbox', type: "checkbox",
selectedRowKeys: _.pluck(selectPicture, 'id'), selectedRowKeys: _.pluck(selectPicture, "id"),
onSelect: (record, selected) => { onSelect: (record, selected) => {
this.selectPictureList(record, selected) this.selectPictureList(record, selected)
}, },
onSelectAll: (selected, _selectedRows, changeRows) => { onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = []; let _list = []
if (selected) { if (selected) {
_list = _.uniq(selectPicture.concat(changeRows), false, (item) => item.id); _list = _.uniq(selectPicture.concat(changeRows), false, (item) => item.id)
} else { } else {
_list = _.reject(selectPicture, (item) => _.find(changeRows, (data) => data.id === item.id)); _list = _.reject(selectPicture, (item) => _.find(changeRows, (data) => data.id === item.id))
} }
if(_list.length + currentTaskCourseData.length + selectVideo.length + selectLive.length> 20){ if (_list.length + currentTaskCourseData.length + selectVideo.length + selectLive.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程'); message.warning("无法继续选择,一个任务最多关联20个课程")
const extraLength = (_list.length + currentTaskCourseData.length + selectVideo.length + selectLive.length) -20; const extraLength = _list.length + currentTaskCourseData.length + selectVideo.length + selectLive.length - 20
_list.splice(_list.length - extraLength,extraLength); _list.splice(_list.length - extraLength, extraLength)
}
this.setState({ selectPicture: _list })
} }
this.setState({selectPicture:_list});
},
}} }}
/> />
{pictureDataSource.length >0 && {pictureDataSource.length > 0 && (
<div className="box-footer"> <div className='box-footer'>
<PageControl <PageControl
current={pictureQuery.current - 1} current={pictureQuery.current - 1}
pageSize={pictureSize} pageSize={pictureSize}
size="small" size='small'
total={pictureTotalCount} total={pictureTotalCount}
toPage={(page) => { toPage={(page) => {
const _query = {...pictureQuery, current: page + 1}; const _query = { ...pictureQuery, current: page + 1 }
this.setState({ this.setState(
pictureQuery:_query {
},()=>{ this.handleFetchPictureDataList()}) pictureQuery: _query
},
() => {
this.handleFetchPictureDataList()
}
)
}} }}
onShowSizeChange={this.onShowPictureSizeChange} onShowSizeChange={this.onShowPictureSizeChange}
/> />
</div> </div>
} )}
</div> </div>
</TabPane> </TabPane>
</Tabs> </Tabs>
...@@ -752,4 +979,4 @@ class SelectOperatorModal extends React.Component { ...@@ -752,4 +979,4 @@ class SelectOperatorModal extends React.Component {
} }
} }
export default SelectOperatorModal; export default SelectOperatorModal
\ No newline at end of file
.related-course-modal{ .related-course-modal {
.ant-tabs-top > .ant-tabs-nav::before{ .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn {
border-bottom: 0px; font-weight: 500;
} }
.ant-tabs-nav-list{ // .ant-tabs-nav .ant-tabs-tab {
margin:0 auto; // padding: 6px 12px !important;
} // margin: 0;
.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn{ // border: 1px solid #e8e8e8;
font-weight:normal; // font-size: 14px !important;
} // color: #999;
.ant-tabs-nav .ant-tabs-tab{
padding:6px 12px !important;
margin:0;
border: 1px solid #E8E8E8;
font-size:14px !important;
color:#999;
&:nth-child(1){ // &:nth-child(1) {
border-radius: 4px 0px 0px 4px; // border-radius: 4px 0px 0px 4px;
} // }
&:nth-child(2){ // &:nth-child(2) {
border-radius: 0px 4px 4px 0px; // border-radius: 0px 4px 4px 0px;
} // }
} // }
.ant-tabs-nav .ant-tabs-tab-active{ // .ant-tabs-nav .ant-tabs-tab-active {
border: 1px solid #FFB714; // // border: 1px solid #ffb714;
color:#FFB714; // color: #ffb714;
} // }
.ant-tabs-top .ant-tabs-ink-bar-animated:after{ // .ant-tabs-top .ant-tabs-ink-bar-animated:after {
height:0; // height: 0;
// }
.ant-modal-footer {
display: flex;
} }
.link-create-course{ .link-create-course {
color:#666666; color: #666666;
font-size:14px; font-size: 14px;
width:638px; width: 638px;
text-align:left; text-align: left;
display:inline-block; display: inline-block;
span{ span {
color:#5289FA; color: #5289fa;
} }
} }
.search-container{ .search-container {
margin-bottom:16px; margin-bottom: 16px;
} }
.select-area{ .select-area {
margin-bottom:12px; margin-bottom: 12px;
display:flex; display: flex;
justify-content:space-between; justify-content: space-between;
.select-box{ .select-box {
display:inline-box; display: inline-box;
width: 186px; width: 186px;
background: #FFF4DD; background: #e9efff;
border-radius: 4px; border-radius: 4px;
padding:6px 16px; padding: 6px 16px;
margin-right:8px; margin-right: 8px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
.tip-icon{ .tip-icon {
color:#FF9D14; color: #2966ff;
font-size:14px; font-size: 14px;
margin-right:4px; margin-right: 4px;
} }
.select-num{ .select-num {
color:#666666; color: #666666;
font-size:14px; font-size: 14px;
} }
.clear-btn{ .clear-btn {
text-align:right; text-align: right;
color:#5289FA; color: #5289fa;
font-size:14px; font-size: 14px;
} }
} }
.related-box{ .related-box {
padding:6px 16px; padding: 6px 16px;
background: #FFF4DD; background: #e9efff;
border-radius: 4px; border-radius: 4px;
flex:1; flex: 1;
color:#666666; color: #666666;
font-size:14px; font-size: 14px;
} }
} }
.course-info{ .course-info {
display: flex; display: flex;
align-items: center; align-items: center;
.course-cover{ .course-cover {
width: 97px; width: 97px;
height: 55px; height: 55px;
display: inline-block; display: inline-block;
border-radius:4px; border-radius: 4px;
margin-right:8px; margin-right: 8px;
} }
.course-name{ .course-name {
font-size:14px; font-size: 14px;
color:#666; color: #666;
text-overflow: -o-ellipsis-lastline; text-overflow: -o-ellipsis-lastline;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
...@@ -101,15 +98,15 @@ ...@@ -101,15 +98,15 @@
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
line-clamp: 2; line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
width:238px; width: 180px;
} }
.course-status { .course-status {
font-size:12px; font-size: 12px;
line-height:18px; line-height: 18px;
display:inline-block; display: inline-block;
border-radius:2px; border-radius: 2px;
padding:0 8px; padding: 0 8px;
margin-top:8px; margin-top: 8px;
} }
} }
} }
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react'
import { import { withRouter } from 'react-router-dom'
withRouter import './Login.less'
} from 'react-router-dom'; import { Input, Popover, message, Tabs, Button } from 'antd'
import './Login.less'; import CheckBeforeSendCode from '../../components/CheckBeforeSendCode'
import { Input, Popover, message, Tabs, Button } from 'antd'; import User from '@/common/js/user'
import CheckBeforeSendCode from '../../components/CheckBeforeSendCode';
import User from '@/common/js/user';
import WechatLogin from './WechatLogin' import WechatLogin from './WechatLogin'
import BaseService from "@/domains/basic-domain/baseService"; import BaseService from '@/domains/basic-domain/baseService'
import axios from 'axios'; import axios from 'axios'
import _ from 'underscore'; import _ from 'underscore'
import user from '@/common/js/user'; import user from '@/common/js/user'
const { TabPane } = Tabs; const { TabPane } = Tabs
function Login(props) { function Login(props) {
const [phone, setPhone] = useState(''); // 登录手机号 const [phone, setPhone] = useState('') // 登录手机号
const [phoneverify, setPhoneverify] = useState(''); // 密码登录验证码 const [phoneverify, setPhoneverify] = useState('') // 密码登录验证码
const [openCheck1, setOpenCheck1] = useState(false); const [openCheck1, setOpenCheck1] = useState(false)
const [checking1, setChecking1] = useState(false); const [checking1, setChecking1] = useState(false)
const [codeText, setCodeText] = useState('获取验证码'); // 验证码提示语 const [codeText, setCodeText] = useState('获取验证码') // 验证码提示语
const [waitStatus, setWaitStatus] = useState(false); // 验证码是否在倒计时 const [waitStatus, setWaitStatus] = useState(false) // 验证码是否在倒计时
const [errorMessage, setErrorMessage] = useState(''); const [errorMessage, setErrorMessage] = useState('')
const [phoneError, setPhoneError] = useState(false); const [phoneError, setPhoneError] = useState(false)
const [checkObject1, setCheckObject1] = useState({}); const [checkObject1, setCheckObject1] = useState({})
useEffect(() => { useEffect(() => {
const enterpriseId = getParameterByName("enterpriseId"); const enterpriseId = getParameterByName('enterpriseId')
const userId = getParameterByName("userId"); const userId = getParameterByName('userId')
const from = getParameterByName("from"); const from = getParameterByName('from')
const storeId = getParameterByName("storeId"); const storeId = getParameterByName('storeId')
if (storeId) { if (storeId) {
User.setCustomerStoreId(storeId); User.setCustomerStoreId(storeId)
} }
if (from === 'customer' && enterpriseId && userId) { if (from === 'customer' && enterpriseId && userId) {
if (!user.getToken() || enterpriseId !== user.getEnterpriseId() || userId !== User.getUserId()) { if (!user.getToken() || enterpriseId !== user.getEnterpriseId() || userId !== User.getUserId()) {
getWXWorkLoginNoCheck(enterpriseId, userId); getWXWorkLoginNoCheck(enterpriseId, userId)
} else { } else {
window.RCHistory.push({ window.RCHistory.push({
pathname: `/switch-route`, pathname: `/switch-route`
}) })
} }
} else { } else {
User.removeUserId(); User.removeUserId()
User.removeToken(); User.removeToken()
User.removeEnterpriseId(); User.removeEnterpriseId()
} }
}, []) }, [])
function getWXWorkLoginNoCheck(enterpriseId, userId) { function getWXWorkLoginNoCheck(enterpriseId, userId) {
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) => { BaseService.getWXWorkLoginNoCheck(params).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.RCHistory.push({ window.RCHistory.push({
pathname: `/switch-route`, pathname: `/switch-route`
}) })
}) })
} }
async function checkAccount(code, callback = () => { }) { async function checkAccount(code, callback = () => {}) {
callback(); callback()
} }
function checkSend(code) { function checkSend(code) {
if (!phone) { if (!phone) {
setPhoneError(true); setPhoneError(true)
setErrorMessage("请输入手机号"); setErrorMessage('请输入手机号')
return; return
} }
if (phone.length != 11) { if (phone.length != 11) {
setPhoneError(true); setPhoneError(true)
setErrorMessage("请输入11位手机号") setErrorMessage('请输入11位手机号')
return; return
} }
!_.isEmpty(checkObject1) && checkObject1.reset(); !_.isEmpty(checkObject1) && checkObject1.reset()
setOpenCheck1(true); setOpenCheck1(true)
} }
function handleSendSMSCode(checkData, userType) { function handleSendSMSCode(checkData, userType) {
if (waitStatus) return; if (waitStatus) return
let timer; let timer
const params = { const params = {
phone: phone, phone: phone,
sig: checkData.sig, sig: checkData.sig,
sessionId: checkData.csessionid, sessionId: checkData.csessionid,
token: checkData.token, token: checkData.token,
scene: 'nc_login', scene: 'nc_login',
serverType: "CLOUD_CLASS_LOGIN", serverType: 'CLOUD_CLASS_LOGIN',
appTermEnum: 'XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN' appTermEnum: 'XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN'
} }
BaseService.sendLoginAuthCode(params).then((res) => { BaseService.sendLoginAuthCode(params).then((res) => {
if (!res.success) { if (!res.success) {
setErrorMessage(res.message); setErrorMessage(res.message)
} else { } else {
timeSub(60); timeSub(60)
setChecking1(true) setChecking1(true)
} }
}) })
function timeSub(waitTime, unit) { function timeSub(waitTime, unit) {
clearTimeout(timer); clearTimeout(timer)
timer = setTimeout(function () { timer = setTimeout(function () {
if (waitTime === 0) { if (waitTime === 0) {
setCodeText('发送验证码') setCodeText('发送验证码')
setChecking1(false) setChecking1(false)
setWaitStatus(false) setWaitStatus(false)
clearTimeout(timer); clearTimeout(timer)
} else { } else {
setCodeText(`${waitTime}秒后重发`) setCodeText(`${waitTime}秒后重发`)
setWaitStatus(true) setWaitStatus(true)
timeSub(--waitTime, 1000); timeSub(--waitTime, 1000)
} }
}, unit || 0); }, unit || 0)
} }
} }
function handleSubmit() { function handleSubmit() {
if (!phone) { if (!phone) {
setPhoneError(true); setPhoneError(true)
setErrorMessage("请输入手机号"); setErrorMessage('请输入手机号')
return; return
} }
if (phone.length != 11) { if (phone.length != 11) {
setPhoneError(true); setPhoneError(true)
setErrorMessage("请输入11位手机号") setErrorMessage('请输入11位手机号')
return; return
} }
if (!phoneverify) { if (!phoneverify) {
setErrorMessage("请输入验证码"); setErrorMessage('请输入验证码')
return; return
} }
const params = { const params = {
phone, phone,
authCode: phoneverify, authCode: phoneverify,
appTermEnum: "XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN" appTermEnum: 'XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN'
} }
BaseService.login(params).then((res) => { BaseService.login(params).then((res) => {
if (!res.success) { if (!res.success) {
setErrorMessage(res.message); setErrorMessage(res.message)
} else { } else {
User.setUserId(res.result.userId); User.setUserId(res.result.userId)
User.setToken(res.result.xmToken); User.setToken(res.result.xmToken)
window.RCHistory.push({ window.RCHistory.push({
pathname: `/switch-route`, pathname: `/switch-route`
}) })
} }
}) })
} }
return ( return (
<div className="login-page" > <div className='login-page'>
<div className="login-main"> <div className='login-main'>
<div className="left-banner"> <div className='left-banner'>
<div><img src="https://image.xiaomaiketang.com/xm/Newk4NrxKC.png" alt="" style={{ width: 60, height: 61 }} /></div> <div>
<div className="name">小麦企学院</div> <img src='https://image.xiaomaiketang.com/xm/Newk4NrxKC.png' alt='' style={{ width: 60, height: 61 }} />
<div className="desc">一键开启直播授课 让知识更有价值</div>
</div> </div>
<div className="login-box"> <div className='name'>小麦企学院</div>
<div className="login"> <div className='desc'>一键开启直播授课 让知识更有价值</div>
<div className="r"> </div>
<Tabs defaultActiveKey="1"> <div className='login-box'>
<TabPane tab="企业微信登录" key="1"> <div className='login'>
<div className='r'>
<Tabs defaultActiveKey='1'>
<TabPane tab='企业微信登录' key='1'>
<WechatLogin></WechatLogin> <WechatLogin></WechatLogin>
</TabPane> </TabPane>
<TabPane tab="手机号登录" key="2"> <TabPane tab='手机号登录' key='2'>
<div className="login-form"> <div className='login-form'>
<div className="form"> <div className='form'>
<div className="username" style={{ marginBottom: 16 }}> <div className='username' style={{ marginBottom: 16 }}>
<Input <Input
type="phone" type='phone'
autoComplete="off" autoComplete='off'
name="account" name='account'
maxLength={11} maxLength={11}
placeholder="手机号" placeholder='手机号'
value={phone} value={phone}
onChange={(e) => { setPhone(e.target.value) }} onChange={(e) => {
setPhone(e.target.value)
}}
/> />
</div> </div>
<div className="error-message"> <div className='error-message'></div>
<div className='phoneverify'>
</div>
<div className="phoneverify">
<Input <Input
type="text" type='text'
id="phoneverify" id='phoneverify'
name="phoneverify" name='phoneverify'
placeholder="验证码" placeholder='验证码'
autoComplete="off" autoComplete='off'
value={phoneverify} value={phoneverify}
maxLength={4} maxLength={4}
onChange={(e) => { setPhoneverify(e.target.value) }} onChange={(e) => {
setPhoneverify(e.target.value)
}}
/> />
<Popover <Popover
visible={openCheck1} visible={openCheck1}
trigger="click" trigger='click'
title="" title=''
content={<div> content={
<div>
<span style={{ fontSize: '12px', color: '#999', marginBottom: 8, display: 'block' }}>请完成安全验证</span> <span style={{ fontSize: '12px', color: '#999', marginBottom: 8, display: 'block' }}>请完成安全验证</span>
<CheckBeforeSendCode <CheckBeforeSendCode
callback={(data, nc) => { callback={(data, nc) => {
setCheckObject1(nc); setCheckObject1(nc)
checkAccount(1, (userType) => { checkAccount(1, (userType) => {
handleSendSMSCode(data, userType); handleSendSMSCode(data, userType)
setTimeout(() => { setTimeout(() => {
setOpenCheck1(false); setOpenCheck1(false)
}, 500) }, 500)
}) })
}} }}
/> />
</div>} </div>
}
onVisibleChange={(value) => { onVisibleChange={(value) => {
if (!value) { if (!value) {
setOpenCheck1(false); setOpenCheck1(false)
} }
}} }}
placement="bottomRight" placement='bottomRight'>
>
<div <div
className="btn" className='btn'
id="sendVerifyCode" id='sendVerifyCode'
onClick={() => { onClick={() => {
if (checking1) return; if (checking1) return
checkSend(1) checkSend(1)
}} }}>
>{codeText}</div> {codeText}
</Popover>
</div> </div>
<div className="error-message"> </Popover>
{errorMessage}
</div> </div>
<div className="submit"> <div className='error-message'>{errorMessage}</div>
<div className="btn"> <div className='submit'>
<span id='loginIn' onClick={() => { handleSubmit() }} >登录</span> <div className='btn'>
<span
id='loginIn'
onClick={() => {
handleSubmit()
}}>
登录
</span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</TabPane> </TabPane>
</Tabs> </Tabs>
</div> </div>
</div> </div>
...@@ -249,4 +254,4 @@ function Login(props) { ...@@ -249,4 +254,4 @@ function Login(props) {
) )
} }
export default withRouter(Login); export default withRouter(Login)
...@@ -15,10 +15,11 @@ ...@@ -15,10 +15,11 @@
background-color: #f0f2f5; background-color: #f0f2f5;
overflow-x: scroll; overflow-x: scroll;
z-index: 1; z-index: 1;
&.right-container-vertical{ height: calc(~'100% - 50px');
left:@xm-left-min-width; &.right-container-vertical {
.page{ left: @xm-left-min-width;
.page{ .page {
.page {
left: @xm-left-min-width; left: @xm-left-min-width;
} }
} }
...@@ -41,7 +42,6 @@ ...@@ -41,7 +42,6 @@
} }
} }
.right-container { .right-container {
// min-width: 1186px; // min-width: 1186px;
&:before { &:before {
...@@ -111,7 +111,7 @@ ...@@ -111,7 +111,7 @@
} }
&.multiple { &.multiple {
h1{ h1 {
&.on { &.on {
border-left: 1px solid @border; border-left: 1px solid @border;
} }
......
.wechatLoginBox {
.wechatLoginBox{
height: 320px; height: 320px;
text-align: center; text-align: center;
.text{ .text {
margin-top: 12px; margin-top: 12px;
font-size: 14px; font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC; font-family: PingFangSC-Regular, PingFang SC;
color: #999999; color: #999999;
line-height: 20px; line-height: 20px;
} }
.rwm{ .rwm {
position: relative; position: relative;
width: 160px; width: 160px;
height: 160px; height: 160px;
text-align: center; text-align: center;
display: inline-block; display: inline-block;
margin-top: 24px; margin-top: 24px;
.error{ .error {
position: absolute; position: absolute;
width: 170px; width: 100%;
height: 170px; height: 100%;
background: rgba(255, 255, 255, 0.95); background: rgba(255, 255, 255, 0.95);
display: flex; display: flex;
align-items:center; align-items: center;
justify-content:center; justify-content: center;
left:-5px; left: 0px;
top:-5px; top: 0px;
div{ div {
margin: 0 10px; margin: 0 10px;
font-size: 14px; font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC; font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400; font-weight: 400;
color: #333333; color: #333333;
line-height: 20px; line-height: 20px;
} }
.ope{ .ope {
cursor: pointer; cursor: pointer;
color:rgba(82, 137, 250, 1); color: rgba(82, 137, 250, 1);
} }
} }
} }
.ant-tabs-tab-active{ .ant-tabs-tab-active {
.ant-tabs-tab-btn{ .ant-tabs-tab-btn {
color: #333333; color: #333333;
} }
} }
.ant-tabs-tab-btn{ .ant-tabs-tab-btn {
color: #999999; color: #999999;
font-size: 16px; font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC; font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500; font-weight: 500;
line-height: 25px; line-height: 25px;
} }
.ant-tabs-nav::before{ .ant-tabs-nav::before {
display: none; display: none;
} }
.ant-tabs-tab{ .ant-tabs-tab {
width: 105px; width: 105px;
text-align: center; text-align: center;
} }
.ant-tabs > .ant-tabs-nav .ant-tabs-nav-list{ .ant-tabs > .ant-tabs-nav .ant-tabs-nav-list {
margin: 0 auto; margin: 0 auto;
} }
.ant-tabs-top > .ant-tabs-nav .ant-tabs-ink-bar{ .ant-tabs-top > .ant-tabs-nav .ant-tabs-ink-bar {
width: 24px !important; width: 24px !important;
height: 4px; height: 4px;
margin-left: 30px; margin-left: 30px;
......
/* /*
* @Author: yuananting * @Author: yuananting
* @Date: 2021-02-23 18:28:50 * @Date: 2021-02-23 18:28:50
* @LastEditors: yuananting * @LastEditors: fusanqiasng
* @LastEditTime: 2021-04-15 21:53:13 * @LastEditTime: 2021-05-21 17:57:59
* @Description: 助学工具-课程分类 * @Description: 助学工具-课程分类
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React, { Component } from "react"; import React, { Component } from 'react'
import Breadcrumbs from "@/components/Breadcrumbs"; import Breadcrumbs from '@/components/Breadcrumbs'
import "./CourseCategoryManage.less"; import './CourseCategoryManage.less'
import OpearteCourseCategoryModal from "../modal/OpearteCourseCategoryModal"; import OpearteCourseCategoryModal from '../modal/OpearteCourseCategoryModal'
import AidToolService from "@/domains/aid-tool-domain/AidToolService"; import AidToolService from '@/domains/aid-tool-domain/AidToolService'
import User from "@/common/js/user"; import User from '@/common/js/user'
import { import { Tree, Input, Space, Button, Menu, Dropdown, message, Modal } from 'antd'
Tree, import ShowTips from '@/components/ShowTips'
Input, const { DirectoryTree } = Tree
Space, const { Search } = Input
Button, const { confirm } = Modal
Menu,
Dropdown,
message,
Modal,
} from "antd";
import ShowTips from "@/components/ShowTips";
const { DirectoryTree } = Tree;
const { Search } = Input;
const { confirm } = Modal;
class CourseCategoryManage extends Component { class CourseCategoryManage extends Component {
constructor(props) { constructor(props) {
super(props); super(props)
this.state = { this.state = {
operateCourseCategoryModal: null, //新增或编辑分类模态框 operateCourseCategoryModal: null, //新增或编辑分类模态框
treeData: [], treeData: [],
originTreeData: [], originTreeData: [],
treeMap: {}, treeMap: {},
selectedKeys: ["null"], selectedKeys: ['null'],
autoExpandParent: true, autoExpandParent: true
}; }
} }
componentDidMount() { componentDidMount() {
this.queryCategoryTree("init"); this.queryCategoryTree('init')
} }
getWholeTree = () => { getWholeTree = () => {
let query = { let query = {
bizType: "QUESTION", bizType: 'QUESTION',
count: false, count: false,
source: 0, source: 0,
userId: User.getStoreUserId(), userId: User.getStoreUserId(),
tenantId: User.getStoreId(), tenantId: User.getStoreId()
}; }
AidToolService.queryCategoryTree(query).then((res) => { AidToolService.queryCategoryTree(query).then((res) => {
const { categoryList = [] } = res.result; const { categoryList = [] } = res.result
this.setState({ originTreeData: categoryList }, () => { this.setState({ originTreeData: categoryList }, () => {
let map = {}; let map = {}
let topItem = []; let topItem = []
categoryList.forEach((item) => { categoryList.forEach((item) => {
topItem.push(item); topItem.push(item)
}); })
this.setState({ this.setState({
treeMap: Object.assign(this.getTreeMap(categoryList, map), { treeMap: Object.assign(this.getTreeMap(categoryList, map), {
0: { 0: {
sonCategoryList: topItem, sonCategoryList: topItem
}, }
}), })
}); })
}); })
}); })
}; }
// 查询分类树 // 查询分类树
queryCategoryTree = (operateType, categoryName) => { queryCategoryTree = (operateType, categoryName) => {
this.getWholeTree(); this.getWholeTree()
this.setState({ categoryName }); this.setState({ categoryName })
let query = { let query = {
bizType: "QUESTION", bizType: 'QUESTION',
count: false, count: false,
source: 0, source: 0,
categoryName, categoryName,
userId: User.getStoreUserId(), userId: User.getStoreUserId(),
tenantId: User.getStoreId(), tenantId: User.getStoreId()
}; }
AidToolService.queryCategoryTree(query).then((res) => { AidToolService.queryCategoryTree(query).then((res) => {
const { categoryList = [] } = res.result; const { categoryList = [] } = res.result
let str = "未分类"; let str = '未分类'
if (categoryName) { if (categoryName) {
this.setState({ autoExpandParent: true }); this.setState({ autoExpandParent: true })
if (str.indexOf(categoryName) < 0) { if (str.indexOf(categoryName) < 0) {
this.setState({ this.setState({
treeData: this.renderTreeNodes(categoryList, categoryName), treeData: this.renderTreeNodes(categoryList, categoryName)
}); })
let nodeId = []; let nodeId = []
Object.keys(this.state.treeMap).forEach((item) => { Object.keys(this.state.treeMap).forEach((item) => {
nodeId.push(item); nodeId.push(item)
}); })
this.setState({ expandedKeys: nodeId }); this.setState({ expandedKeys: nodeId })
} else { } else {
const defaultNode = { const defaultNode = {
id: "null", id: 'null',
categoryName: "未分类", categoryName: '未分类',
categoryCount: 0, categoryCount: 0,
parentId: "0", parentId: '0',
categoryLevel: 0, categoryLevel: 0
}; }
categoryList.unshift(defaultNode); categoryList.unshift(defaultNode)
this.setState({ this.setState({
treeData: this.renderTreeNodes(categoryList, categoryName), treeData: this.renderTreeNodes(categoryList, categoryName)
}); })
let nodeId = []; let nodeId = []
Object.keys(this.state.treeMap).forEach((item) => { Object.keys(this.state.treeMap).forEach((item) => {
nodeId.push(item); nodeId.push(item)
}); })
if (operateType === "init") { if (operateType === 'init') {
this.setState({ expandedKeys: nodeId }); this.setState({ expandedKeys: nodeId })
} }
} }
} else { } else {
this.setState({ autoExpandParent: false }); this.setState({ autoExpandParent: false })
const defaultNode = { const defaultNode = {
id: "null", id: 'null',
categoryName: "未分类", categoryName: '未分类',
categoryCount: 0, categoryCount: 0,
parentId: "0", parentId: '0',
categoryLevel: 0, categoryLevel: 0
}; }
categoryList.unshift(defaultNode); categoryList.unshift(defaultNode)
this.setState({ treeData: this.renderTreeNodes(categoryList, categoryName) }); this.setState({ treeData: this.renderTreeNodes(categoryList, categoryName) })
if (operateType === "init") { if (operateType === 'init') {
this.setState({ expandedKeys: [] }); this.setState({ expandedKeys: [] })
} }
} }
}); })
}; }
// 树节点渲染-内容处理 // 树节点渲染-内容处理
renderTreeNodes = (data, value) => { renderTreeNodes = (data, value) => {
let newTreeData = data.map((item) => { let newTreeData = data.map((item) => {
item.title = item.categoryName; item.title = item.categoryName
item.key = item.id; item.key = item.id
item.title = ( item.title = (
<div <div
style={{ style={{
opacity: opacity: !value || (value && item.categoryName.indexOf(value) > -1) ? 1 : 0.5
!value || (value && item.categoryName.indexOf(value) > -1)
? 1
: 0.5,
}} }}
className="node-title-div" className='node-title-div'
onMouseOver={(e) => { onMouseOver={(e) => {
let mouseNodeOpts = e.currentTarget.getElementsByTagName("div")[0]; let mouseNodeOpts = e.currentTarget.getElementsByTagName('div')[0]
if (mouseNodeOpts) { if (mouseNodeOpts) {
mouseNodeOpts.style.visibility = "visible"; mouseNodeOpts.style.visibility = 'visible'
} }
}} }}
onMouseOut={(e) => { onMouseOut={(e) => {
let mouseNodeOpts = e.currentTarget.getElementsByTagName("div")[0]; let mouseNodeOpts = e.currentTarget.getElementsByTagName('div')[0]
if (mouseNodeOpts) { if (mouseNodeOpts) {
mouseNodeOpts.style.visibility = "hidden"; mouseNodeOpts.style.visibility = 'hidden'
} }
}} }}>
>
<span>{item.categoryName}</span> <span>{item.categoryName}</span>
{item.categoryName !== "未分类" && ( {item.categoryName !== '未分类' && (
<Space className="title-opts" size={16}> <Space className='title-opts' size={16}>
<span <span
onClick={() => { onClick={() => {
let nodesCount = 0; let nodesCount = 0
const { originTreeData } = this.state; const { originTreeData } = this.state
console.log("orororo", originTreeData); console.log('orororo', originTreeData)
if ( if (
(item.categoryLevel === 0 && originTreeData.length >= 29) || (item.categoryLevel === 0 && originTreeData.length >= 29) ||
(item.categoryLevel > 0 && (item.categoryLevel > 0 && this.getRelatedNodes(item.parentId).length >= 30)
this.getRelatedNodes(item.parentId).length >= 30)
) { ) {
return message.info("最多只能添加30个分类"); return message.info('最多只能添加30个分类')
} }
this.newEditCourseCategory( this.newEditCourseCategory('newEqualLevelCategory', 'equal', 'new', item)
"newEqualLevelCategory", }}>
"equal", <span className='icon iconfont' style={{ color: '#BFBFBF' }}>
"new", &#xe7f5;{' '}
item
);
}}
>
<span className="icon iconfont" style={{ color: "#BFBFBF" }}>
&#xe7f5;{" "}
</span> </span>
<span>同级</span> <span>同级</span>
</span> </span>
{item.categoryLevel < 4 && ( {item.categoryLevel < 4 && (
<span <span
onClick={() => { onClick={() => {
if ( if (this.getRelatedNodes(item.id) && this.getRelatedNodes(item.id).length >= 30) {
this.getRelatedNodes(item.id) && message.info('最多只能添加30个子分类')
this.getRelatedNodes(item.id).length >= 30 return
) { }
message.info("最多只能添加30个子分类"); this.newEditCourseCategory('newChildLevelCategory', 'child', 'new', item)
return; }}>
} <span className='icon iconfont' style={{ color: '#BFBFBF' }}>
this.newEditCourseCategory( &#xe7f8;{' '}
"newChildLevelCategory",
"child",
"new",
item
);
}}
>
<span className="icon iconfont" style={{ color: "#BFBFBF" }}>
&#xe7f8;{" "}
</span> </span>
<span>子级</span> <span>子级</span>
</span> </span>
)} )}
<Dropdown overlay={this.initDropMenu(item)}> <Dropdown overlay={this.initDropMenu(item)}>
<span> <span>
<span className="icon iconfont" style={{ color: "#BFBFBF" }}> <span className='icon iconfont' style={{ color: '#BFBFBF' }}>
&#xe7f7;{" "} &#xe7f7;{' '}
</span> </span>
<span>更多</span> <span>更多</span>
</span> </span>
...@@ -224,77 +195,71 @@ class CourseCategoryManage extends Component { ...@@ -224,77 +195,71 @@ class CourseCategoryManage extends Component {
</Space> </Space>
)} )}
</div> </div>
); )
item.icon = item.icon =
item.categoryName === "未分类" ? ( item.categoryName === '未分类' ? (
<img <img
style={{ style={{
width: "24px", width: '24px',
height: "24px", height: '24px',
opacity: opacity: !value || (value && item.categoryName.indexOf(value) > -1) ? 1 : 0.5
!value || (value && item.categoryName.indexOf(value) > -1)
? 1
: 0.5,
}} }}
src="https://image.xiaomaiketang.com/xm/defaultCategory.png" src='https://image.xiaomaiketang.com/xm/defaultCategory.png'
alt="" alt=''
/> />
) : ( ) : (
<img <img
style={{ style={{
width: "24px", width: '24px',
height: "24px", height: '24px',
opacity: opacity: !value || (value && item.categoryName.indexOf(value) > -1) ? 1 : 0.5
!value || (value && item.categoryName.indexOf(value) > -1)
? 1
: 0.5,
}} }}
src="https://image.xiaomaiketang.com/xm/hasCategory.png" src='https://image.xiaomaiketang.com/xm/hasCategory.png'
alt="" alt=''
/> />
); )
if (item.sonCategoryList) { if (item.sonCategoryList) {
item.children = this.renderTreeNodes(item.sonCategoryList, value); item.children = this.renderTreeNodes(item.sonCategoryList, value)
}
return item
})
return newTreeData
} }
return item;
});
return newTreeData;
};
// 树结构平铺 // 树结构平铺
getTreeMap = (data, map) => { getTreeMap = (data, map) => {
data.forEach((item) => { data.forEach((item) => {
map[item.id] = item; map[item.id] = item
if (item.sonCategoryList && item.sonCategoryList.length > 0) { if (item.sonCategoryList && item.sonCategoryList.length > 0) {
this.getTreeMap(item.sonCategoryList, map); this.getTreeMap(item.sonCategoryList, map)
} }
}); })
return map; return map
}; }
// 新增或编辑分类 // 新增或编辑分类
newEditCourseCategory = (categoryType, addLevelType, type, node) => { newEditCourseCategory = (categoryType, addLevelType, type, node) => {
let title = ""; let title = ''
let label = ""; let label = ''
switch (categoryType) { switch (categoryType) {
case "newEqualLevelCategory": case 'newEqualLevelCategory':
title = "新增分类"; title = '新增分类'
label = "分类名称"; label = '分类名称'
break; break
case "newChildLevelCategory": case 'newChildLevelCategory':
title = "新增子分类"; title = '新增子分类'
label = "子分类名称"; label = '子分类名称'
break; break
case "editEqualLevelCategory": case 'editEqualLevelCategory':
title = "编辑分类"; title = '编辑分类'
label = "分类名称"; label = '分类名称'
break; break
case "editChildLevelCategory": case 'editChildLevelCategory':
title = "编辑子分类"; title = '编辑子分类'
label = "子分类名称"; label = '子分类名称'
break; break
} }
const m = ( const m = (
<OpearteCourseCategoryModal <OpearteCourseCategoryModal
...@@ -304,336 +269,273 @@ class CourseCategoryManage extends Component { ...@@ -304,336 +269,273 @@ class CourseCategoryManage extends Component {
title={title} title={title}
label={label} label={label}
close={() => { close={() => {
this.queryCategoryTree("remain", this.state.categoryName); this.queryCategoryTree('remain', this.state.categoryName)
this.setState({ this.setState({
operateCourseCategoryModal: null, operateCourseCategoryModal: null
}); })
}} }}
/> />
); )
this.setState({ operateCourseCategoryModal: m }); this.setState({ operateCourseCategoryModal: m })
}; }
// 删除分类 // 删除分类
delCategory = (item) => { delCategory = (item) => {
return confirm({ return confirm({
title: "确认删除该分类吗?", title: '确认删除该分类吗?',
content: "删除后,分类下的所有内容将自动转入“未分类”中。", content: '删除后,分类下的所有内容将自动转入“未分类”中。',
icon: ( icon: <span className='icon iconfont default-confirm-icon'>&#xe839; </span>,
<span className="icon iconfont default-confirm-icon">&#xe839; </span> okText: '删除',
), okType: 'danger',
okText: "删除", cancelText: '取消',
okType: "danger",
cancelText: "取消",
onOk: () => { onOk: () => {
let params = { let params = {
categoryId: item.id, categoryId: item.id,
source: 0, source: 0,
tenantId: User.getStoreId(), tenantId: User.getStoreId(),
userId: User.getStoreUserId(), userId: User.getStoreUserId()
}; }
AidToolService.delCategory(params).then((res) => { AidToolService.delCategory(params).then((res) => {
if (res.success) { if (res.success) {
message.success("删除分类成功"); message.success('删除分类成功')
this.queryCategoryTree("remain", this.state.categoryName); this.queryCategoryTree('remain', this.state.categoryName)
}
})
}
})
} }
});
},
});
};
// 更多操作-【重命名 删除】 // 更多操作-【重命名 删除】
initDropMenu = (item) => { initDropMenu = (item) => {
return ( return (
<Menu> <Menu>
<Menu.Item key="0"> <Menu.Item key='0'>
<span <span
onClick={() => { onClick={() => {
let categoryType = let categoryType = item.categoryLevel === 0 ? 'editEqualLevelCategory' : 'editChildLevelCategory'
item.categoryLevel === 0 this.newEditCourseCategory(categoryType, 'equal', 'edit', item)
? "editEqualLevelCategory" }}>
: "editChildLevelCategory";
this.newEditCourseCategory(categoryType, "equal", "edit", item);
}}
>
重命名 重命名
</span> </span>
</Menu.Item> </Menu.Item>
<Menu.Item key="1"> <Menu.Item key='1'>
<span <span
onClick={() => { onClick={() => {
this.delCategory(item); this.delCategory(item)
}} }}>
>
删除 删除
</span> </span>
</Menu.Item> </Menu.Item>
</Menu> </Menu>
); )
}; }
// 获取相关节点 // 获取相关节点
getRelatedNodes = (parentId) => { getRelatedNodes = (parentId) => {
return this.state.treeMap[parentId] return this.state.treeMap[parentId] ? this.state.treeMap[parentId].sonCategoryList : []
? this.state.treeMap[parentId].sonCategoryList }
: [];
};
// 获取拖拽目标父节点层级 // 获取拖拽目标父节点层级
getParentDragNodesLevel = (dragNode) => { getParentDragNodesLevel = (dragNode) => {
if (!dragNode) { if (!dragNode) {
return []; return []
} }
let dragNodes = []; let dragNodes = []
dragNodes.push(dragNode.id); dragNodes.push(dragNode.id)
if (dragNode.parentId != 0) { if (dragNode.parentId != 0) {
dragNodes = dragNodes.concat( dragNodes = dragNodes.concat(this.getParentDragNodesLevel(this.state.treeMap[dragNode.parentId]))
this.getParentDragNodesLevel(this.state.treeMap[dragNode.parentId]) }
); return dragNodes
} }
return dragNodes;
};
// 获取拖拽节点层级 // 获取拖拽节点层级
getDragNodesLevel = (dragNode) => { getDragNodesLevel = (dragNode) => {
let dragNodes = []; let dragNodes = []
if (dragNode.sonCategoryList && dragNode.sonCategoryList.length > 0) { if (dragNode.sonCategoryList && dragNode.sonCategoryList.length > 0) {
dragNode.sonCategoryList.forEach((item) => { dragNode.sonCategoryList.forEach((item) => {
dragNodes.push(item.categoryLevel); dragNodes.push(item.categoryLevel)
if (item.sonCategoryList && item.sonCategoryList.length > 0) { if (item.sonCategoryList && item.sonCategoryList.length > 0) {
dragNodes = dragNodes.concat(this.getDragNodesLevel(item)); dragNodes = dragNodes.concat(this.getDragNodesLevel(item))
} }
}); })
}
return [...new Set(dragNodes)]
} }
return [...new Set(dragNodes)];
};
// 拖拽 // 拖拽
onDrop = (info) => { onDrop = (info) => {
if (this.state.categoryName) { if (this.state.categoryName) {
return; return
} }
// 未分类不可以拖拽 // 未分类不可以拖拽
if ( if (info.dragNode.categoryName === '未分类' && info.dragNode.categoryLevel === 0) return message.info('未分类”为默认分类暂不支持移动')
info.dragNode.categoryName === "未分类" &&
info.dragNode.categoryLevel === 0
)
return message.info("未分类”为默认分类暂不支持移动");
// 不允许其他节点拖拽到未分类之前 // 不允许其他节点拖拽到未分类之前
if ( if (info.node.categoryName === '未分类' && info.dropToGap && info.dropPosition === -1) return
info.node.categoryName === "未分类" &&
info.dropToGap &&
info.dropPosition === -1
)
return;
let targetParentId = info.dropToGap ? info.node.parentId : info.node.id; let targetParentId = info.dropToGap ? info.node.parentId : info.node.id
let relatedNodes = this.getRelatedNodes(targetParentId); let relatedNodes = this.getRelatedNodes(targetParentId)
if ( if (!((info.dropToGap && info.node.parentId === info.dragNode.parentId) || (!info.dropToGap && info.node.id === info.dragNode.parentId))) {
!(
(info.dropToGap && info.node.parentId === info.dragNode.parentId) ||
(!info.dropToGap && info.node.id === info.dragNode.parentId)
)
) {
if (this.state.treeMap[targetParentId].categoryLevel === 4) { if (this.state.treeMap[targetParentId].categoryLevel === 4) {
return message.info("最多支持5级分类"); return message.info('最多支持5级分类')
} else { } else {
let nodesArr = this.getDragNodesLevel( let nodesArr = this.getDragNodesLevel(this.state.treeMap[info.dragNode.id])
this.state.treeMap[info.dragNode.id] let parentArr = this.getParentDragNodesLevel(this.state.treeMap[targetParentId])
);
let parentArr = this.getParentDragNodesLevel(
this.state.treeMap[targetParentId]
);
if (nodesArr.length + parentArr.length > 4) { if (nodesArr.length + parentArr.length > 4) {
console.log(nodesArr.length, parentArr.length); console.log(nodesArr.length, parentArr.length)
return message.info("最多支持5级分类"); return message.info('最多支持5级分类')
} }
} }
if (relatedNodes && relatedNodes.length >= 30) { if (relatedNodes && relatedNodes.length >= 30) {
return message.info("最多只能添加30个分类"); return message.info('最多只能添加30个分类')
} }
} }
const dropKey = info.node.key; const dropKey = info.node.key
const dragKey = info.dragNode.key; const dragKey = info.dragNode.key
const dropPos = info.node.pos.split("-"); const dropPos = info.node.pos.split('-')
const dropPosition = const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1])
info.dropPosition - Number(dropPos[dropPos.length - 1]);
const loop = (data, key, callback) => { const loop = (data, key, callback) => {
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
if (data[i].key === key) { if (data[i].key === key) {
return callback(data[i], i, data); return callback(data[i], i, data)
} }
if (data[i].sonCategoryList) { if (data[i].sonCategoryList) {
loop(data[i].sonCategoryList, key, callback); loop(data[i].sonCategoryList, key, callback)
}
} }
} }
}; const data = [...this.state.treeData]
const data = [...this.state.treeData];
let getSuf = function (name, originCategoryName, sufIndex) { let getSuf = function (name, originCategoryName, sufIndex) {
if (relatedNodes && relatedNodes.length > 0) { if (relatedNodes && relatedNodes.length > 0) {
let sameNameNodes = []; let sameNameNodes = []
relatedNodes.forEach((item) => { relatedNodes.forEach((item) => {
if (item.id === info.dragNode.id) return true; if (item.id === info.dragNode.id) return true
if (item.categoryName === name) { if (item.categoryName === name) {
sameNameNodes.push(item); sameNameNodes.push(item)
} }
}); })
if (sameNameNodes.length > 0) { if (sameNameNodes.length > 0) {
sufIndex++; sufIndex++
return getSuf( return getSuf(originCategoryName + `(${sufIndex})`, originCategoryName, sufIndex)
originCategoryName + `(${sufIndex})`, }
originCategoryName,
sufIndex
);
} }
return sufIndex
} }
return sufIndex;
};
let dragObj; let dragObj
loop(data, dragKey, (item, index, arr) => { loop(data, dragKey, (item, index, arr) => {
arr.splice(index, 1); arr.splice(index, 1)
item.parentId = targetParentId; item.parentId = targetParentId
if (item.originCategoryName) { if (item.originCategoryName) {
item.categoryName = item.originCategoryName; item.categoryName = item.originCategoryName
} else { } else {
item.originCategoryName = item.categoryName; item.originCategoryName = item.categoryName
} }
info.dragNode.categoryName = item.originCategoryName; info.dragNode.categoryName = item.originCategoryName
let sufIndex = getSuf( let sufIndex = getSuf(info.dragNode.categoryName, item.originCategoryName, 0)
info.dragNode.categoryName, item.categoryName = item.categoryName + (sufIndex ? `(${sufIndex})` : '')
item.originCategoryName, item.categoryName = item.originCategoryName + (sufIndex ? `(${sufIndex})` : '')
0 dragObj = item
); })
item.categoryName = item.categoryName + (sufIndex ? `(${sufIndex})` : "");
item.categoryName =
item.originCategoryName + (sufIndex ? `(${sufIndex})` : "");
dragObj = item;
});
if (!info.dropToGap) { if (!info.dropToGap) {
loop(data, dropKey, (item) => { loop(data, dropKey, (item) => {
item.sonCategoryList = item.sonCategoryList || []; item.sonCategoryList = item.sonCategoryList || []
item.sonCategoryList.unshift(dragObj); item.sonCategoryList.unshift(dragObj)
}); })
} else if ( } else if ((info.node.props.sonCategoryList || []).length > 0 && info.node.props.expanded && dropPosition === 1) {
(info.node.props.sonCategoryList || []).length > 0 &&
info.node.props.expanded &&
dropPosition === 1
) {
loop(data, dropKey, (item) => { loop(data, dropKey, (item) => {
item.sonCategoryList = item.children || []; item.sonCategoryList = item.children || []
item.sonCategoryList.unshift(dragObj); item.sonCategoryList.unshift(dragObj)
}); })
} else { } else {
let ar; let ar
let i; let i
loop(data, dropKey, (item, index, arr) => { loop(data, dropKey, (item, index, arr) => {
ar = arr; ar = arr
i = index; i = index
}); })
if (dropPosition === -1) { if (dropPosition === -1) {
ar.splice(i, 0, dragObj); ar.splice(i, 0, dragObj)
} else { } else {
ar.splice(i + 1, 0, dragObj); ar.splice(i + 1, 0, dragObj)
} }
} }
data.shift(); data.shift()
let newTreeData = this.renderTreeNodes(this.handleLoop(data, 0)); let newTreeData = this.renderTreeNodes(this.handleLoop(data, 0))
this.setState({ treeData: newTreeData }); this.setState({ treeData: newTreeData })
let params = { let params = {
categoryList: newTreeData, categoryList: newTreeData,
source: 0, source: 0,
tenantId: User.getStoreId(), tenantId: User.getStoreId(),
userId: User.getStoreUserId(), userId: User.getStoreUserId()
}; }
AidToolService.editCategoryTree(params).then((res) => { AidToolService.editCategoryTree(params).then((res) => {
this.queryCategoryTree("remain"); this.queryCategoryTree('remain')
}); })
}; }
handleLoop = (data, level) => { handleLoop = (data, level) => {
data.map((item, index) => { data.map((item, index) => {
item.sort = index; item.sort = index
item.categoryLevel = level; item.categoryLevel = level
if (item.sonCategoryList) { if (item.sonCategoryList) {
item.children = this.handleLoop(item.sonCategoryList, level + 1); item.children = this.handleLoop(item.sonCategoryList, level + 1)
item.sonCategoryList = this.handleLoop(item.sonCategoryList, level + 1); item.sonCategoryList = this.handleLoop(item.sonCategoryList, level + 1)
}
return item
})
return data
} }
return item;
});
return data;
};
// 树状展开事件 // 树状展开事件
onExpand = (expandedKeys) => { onExpand = (expandedKeys) => {
this.setState({ expandedKeys }); this.setState({ expandedKeys })
}; }
// 树状选中事件 // 树状选中事件
onSelect = (selectedKeys) => { onSelect = (selectedKeys) => {
this.setState({ selectedKeys }); this.setState({ selectedKeys })
}; }
render() { render() {
const { const { treeData, originTreeData, expandedKeys, selectedKeys, autoExpandParent, operateCourseCategoryModal } = this.state
treeData,
originTreeData,
expandedKeys,
selectedKeys,
autoExpandParent,
operateCourseCategoryModal,
} = this.state;
return ( return (
<div className="page course-category-manage"> <div className='page course-category-manage'>
{["aid", "knowledge"].includes(getParameterByName("from")) ? ( {['aid', 'knowledge'].includes(getParameterByName('from')) ? (
<Breadcrumbs <Breadcrumbs navList='课程分类' goBack={() => window.RCHistory.goBack()} />
navList="课程分类"
goBack={() =>
window.RCHistory.goBack()
}
/>
) : ( ) : (
<div className="content-header">课程分类</div> <div className='content-header'>课程分类</div>
)} )}
<div className="box"> <div className='box'>
<div className="search-condition"> <div className='search-condition'>
<span className="search-label">搜索名称:</span> <span className='search-label'>搜索名称:</span>
<Search <Search
placeholder="请输入名称" placeholder='请输入名称'
style={{ width: "300px" }} style={{ width: '300px' }}
onSearch={(value) => this.queryCategoryTree("init", value)} onSearch={(value) => this.queryCategoryTree('init', value)}
className="search-input" className='search-input'
enterButton={<span className="icon iconfont">&#xe832;</span>} enterButton={<span className='icon iconfont'>&#xe832;</span>}
/> />
</div> </div>
<Button <Button
type="primary" type='primary'
onClick={() => { onClick={() => {
if (originTreeData.length >= 29) { if (originTreeData.length >= 29) {
message.info("最多只能添加30个分类"); message.info('最多只能添加30个分类')
return; return
} }
this.newEditCourseCategory( this.newEditCourseCategory('newEqualLevelCategory', 'equal', 'new')
"newEqualLevelCategory", }}>
"equal",
"new"
);
}}
>
新增一级分类 新增一级分类
</Button> </Button>
<div <div className='show-tips' style={{ marginTop: '12px', width: '900px' }}>
className="show-tips" <ShowTips message='为方便管理,该分类用于课程、培训计划、题库、知识库等模块,改动将同步各模块更新' />
style={{ marginTop: "12px", width: "900px" }}
>
<ShowTips message="为方便管理,该分类用于课程、培训计划、题库、知识库等模块,改动将同步各模块更新" />
</div> </div>
<div className="course-category-tree"> <div className='course-category-tree'>
<DirectoryTree <DirectoryTree
expandedKeys={expandedKeys} expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent} autoExpandParent={autoExpandParent}
...@@ -643,14 +545,13 @@ class CourseCategoryManage extends Component { ...@@ -643,14 +545,13 @@ class CourseCategoryManage extends Component {
draggable draggable
blockNode blockNode
onDrop={this.onDrop} onDrop={this.onDrop}
treeData={treeData} treeData={treeData}></DirectoryTree>
></DirectoryTree>
</div> </div>
</div> </div>
{operateCourseCategoryModal} {operateCourseCategoryModal}
</div> </div>
); )
} }
} }
export default CourseCategoryManage; export default CourseCategoryManage
This source diff could not be displayed because it is too large. You can view the blob instead.
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