Commit 8d29826f by guomingpang

feat:外部课程相关模块提交

parent 83cbc02d
#ignore obj and lib file
dist
rev
node_modules
coverage
*/vendor.js
*/vendor.*.*
*/vendor-manifest.json
coverage
npm-shrinkwrap.json
### OSX template
.DS_Store
node_modules/
dist/
.AppleDouble
.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
*.zip
build/vendor.js.map
package-lock.json
.vscode/*
demo.js
debug.log
'use strict';
'use strict'
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const resolve = require('resolve');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const safePostCssParser = require('postcss-safe-parser');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const paths = require('./paths');
const modules = require('./modules');
const vConsolePlugin = require('vconsole-webpack-plugin');
const getClientEnvironment = require('./env');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin');
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
const fs = require('fs')
const path = require('path')
const webpack = require('webpack')
const resolve = require('resolve')
const PnpWebpackPlugin = require('pnp-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin')
const TerserPlugin = require('terser-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const safePostCssParser = require('postcss-safe-parser')
const ManifestPlugin = require('webpack-manifest-plugin')
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin')
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin')
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin')
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent')
const paths = require('./paths')
const modules = require('./modules')
const vConsolePlugin = require('vconsole-webpack-plugin')
const getClientEnvironment = require('./env')
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin')
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin')
const typescriptFormatter = require('react-dev-utils/typescriptFormatter')
const postcssNormalize = require('postcss-normalize');
const 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.
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
// makes for a smoother build process.
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false'
const isExtendingEslintConfig = process.env.EXTEND_ESLINT === 'true';
const isExtendingEslintConfig = process.env.EXTEND_ESLINT === 'true'
const imageInlineSizeLimit = parseInt(
process.env.IMAGE_INLINE_SIZE_LIMIT || '10000'
);
const imageInlineSizeLimit = parseInt(process.env.IMAGE_INLINE_SIZE_LIMIT || '10000')
// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig);
const useTypeScript = fs.existsSync(paths.appTsConfig)
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;
const cssRegex = /\.css$/
const cssModuleRegex = /\.module\.css$/
const lessRegex = /\.less$/
const lessModuleRegex = /\.module\.less$/
// This is the production and development configuration.
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
module.exports = function (webpackEnv) {
const isEnvDevelopment = webpackEnv === 'development';
const isEnvProduction = webpackEnv === 'production';
const isEnvDevelopment = webpackEnv === 'development'
const isEnvProduction = webpackEnv === 'production'
// Variable used for enabling profiling in Production
// passed into alias object. Uses a flag if passed into the build command
const isEnvProductionProfile =
isEnvProduction && process.argv.includes('--profile');
const isEnvProductionProfile = isEnvProduction && process.argv.includes('--profile')
// We will provide `paths.publicUrlOrPath` to our app
// 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.
// 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
const getStyleLoaders = (cssOptions, preProcessor) => {
......@@ -76,13 +73,11 @@ module.exports = function (webpackEnv) {
loader: MiniCssExtractPlugin.loader,
// css is located in `static/css`, use '../../' to locate index.html folder
// in production `paths.publicUrlOrPath` can be a relative path
options: paths.publicUrlOrPath.startsWith('.')
? { publicPath: '../../' }
: {},
options: paths.publicUrlOrPath.startsWith('.') ? { publicPath: '../../' } : {}
},
{
loader: require.resolve('css-loader'),
options: cssOptions,
options: cssOptions
},
{
// Options for PostCSS as we reference these options twice
......@@ -97,32 +92,32 @@ module.exports = function (webpackEnv) {
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
flexbox: 'no-2009'
},
stage: 3,
stage: 3
}),
// Adds PostCSS Normalize as the reset css with default options,
// so that it honors browserslist config in package.json
// which in turn let's users customize the target behavior as per their needs.
postcssNormalize(),
postcssNormalize()
],
sourceMap: isEnvProduction && shouldUseSourceMap,
},
},
].filter(Boolean);
sourceMap: isEnvProduction && shouldUseSourceMap
}
}
].filter(Boolean)
if (preProcessor) {
loaders.push(
{
loader: require.resolve('resolve-url-loader'),
options: {
sourceMap: isEnvProduction && shouldUseSourceMap,
},
sourceMap: isEnvProduction && shouldUseSourceMap
}
},
{
loader: require.resolve(preProcessor),
options: {
sourceMap: true,
},
sourceMap: true
}
},
{
loader: require.resolve('less-loader'),
......@@ -130,26 +125,22 @@ module.exports = function (webpackEnv) {
lessOptions: {
modifyVars: {
'primary-color': 'rgba(255, 183, 20, 1)',
'border-radius-base': '4px',
},
javascriptEnabled: true,
'border-radius-base': '4px'
},
javascriptEnabled: true
}
}
}
);
)
}
return loaders
}
return loaders;
};
return {
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
// Stop compilation early in production
bail: isEnvProduction,
devtool: isEnvProduction
? shouldUseSourceMap
? 'source-map'
: false
: isEnvDevelopment && 'cheap-module-source-map',
devtool: isEnvProduction ? (shouldUseSourceMap ? 'source-map' : false) : isEnvDevelopment && 'cheap-module-source-map',
// These are the "entry points" to our application.
// This means they will be the "root" imports that are included in JS bundle.
entry: [
......@@ -163,10 +154,9 @@ module.exports = function (webpackEnv) {
// the line below with these two lines if you prefer the stock client:
// require.resolve('webpack-dev-server/client') + '?/',
isEnvDevelopment && require.resolve('webpack/hot/dev-server'),
isEnvDevelopment &&
require.resolve('react-dev-utils/webpackHotDevClient'),
isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient'),
// Finally, this is your app's code:
paths.appIndexJs,
paths.appIndexJs
// We include the app code last so that if there is a runtime error during
// initialization, it doesn't blow up the WebpackDevServer client, and
// changing JS code would still trigger a refresh.
......@@ -178,27 +168,19 @@ module.exports = function (webpackEnv) {
pathinfo: isEnvDevelopment,
// There will be one main bundle, and one file per asynchronous chunk.
// In development, it does not produce real files.
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js'
: isEnvDevelopment && 'static/js/bundle.js',
filename: isEnvProduction ? 'static/js/[name].[contenthash:8].js' : isEnvDevelopment && 'static/js/bundle.js',
// TODO: remove this when upgrading to webpack 5
futureEmitAssets: true,
// There are also additional JS chunk files if you use code splitting.
chunkFilename: isEnvProduction
? 'static/js/[name].[contenthash:8].chunk.js'
: isEnvDevelopment && 'static/js/[name].chunk.js',
chunkFilename: isEnvProduction ? '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.
// 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.
publicPath: isEnvDevelopment ? '/' : './',
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: isEnvProduction
? info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/')
: isEnvDevelopment &&
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
? (info) => path.relative(paths.appSrc, info.absoluteResourcePath).replace(/\\/g, '/')
: isEnvDevelopment && ((info) => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
// Prevents conflicts when multiple webpack runtimes (from different apps)
// are used on the same page.
jsonpFunction: `webpackJsonp${appPackageJson.name}`,
......@@ -206,9 +188,8 @@ module.exports = function (webpackEnv) {
// module chunks which are built will work in web workers as well.
globalObject: 'this',
library: appPackageJson.name,
libraryTarget: "umd",
jsonpFunction: `webpackJsonp_${appPackageJson.name}`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${appPackageJson.name}`
},
optimization: {
minimize: isEnvProduction,
......@@ -222,7 +203,7 @@ module.exports = function (webpackEnv) {
// into invalid ecma 5 code. This is why the 'compress' and 'output'
// sections only apply transformations that are ecma 5 safe
// https://github.com/facebook/create-react-app/pull/4234
ecma: 8,
ecma: 8
},
compress: {
ecma: 5,
......@@ -236,10 +217,10 @@ module.exports = function (webpackEnv) {
// https://github.com/facebook/create-react-app/issues/5250
// Pending further investigation:
// https://github.com/terser-js/terser/issues/120
inline: 2,
inline: 2
},
mangle: {
safari10: true,
safari10: true
},
// Added for profiling in devtools
keep_classnames: isEnvProductionProfile,
......@@ -249,10 +230,10 @@ module.exports = function (webpackEnv) {
comments: false,
// Turned on because emoji and regex is not minified properly using default
// 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
new OptimizeCSSAssetsPlugin({
......@@ -265,57 +246,53 @@ module.exports = function (webpackEnv) {
inline: false,
// `annotation: true` appends the sourceMappingURL to the end of
// the css file, helping the browser find the sourcemap
annotation: true,
annotation: true
}
: false,
: false
},
cssProcessorPluginOptions: {
preset: ['default', { minifyFontValues: { removeQuotes: false } }],
},
}),
preset: ['default', { minifyFontValues: { removeQuotes: false } }]
}
})
],
// Automatically split vendor and commons
// https://twitter.com/wSokra/status/969633336732905474
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
splitChunks: {
chunks: 'all',
name: false,
name: false
},
// Keep the runtime chunk separated to enable long term caching
// https://twitter.com/wSokra/status/969679223278505985
// https://github.com/facebook/create-react-app/issues/5358
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`,
},
name: (entrypoint) => `runtime-${entrypoint.name}`
}
},
resolve: {
// This allows you to set a fallback for where webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebook/create-react-app/issues/253
modules: ['node_modules', paths.appNodeModules].concat(
modules.additionalModulePaths || []
),
modules: ['node_modules', paths.appNodeModules].concat(modules.additionalModulePaths || []),
// These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see:
// https://github.com/facebook/create-react-app/issues/290
// `web` extension prefixes have been added for better support
// for React Native Web.
extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
extensions: paths.moduleFileExtensions.map((ext) => `.${ext}`).filter((ext) => useTypeScript || !ext.includes('ts')),
alias: {
"@": path.resolve(__dirname, '../src'),
'@': path.resolve(__dirname, '../src'),
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
// Allows for better profiling with ReactDevTools
...(isEnvProductionProfile && {
'react-dom$': 'react-dom/profiling',
'scheduler/tracing': 'scheduler/tracing-profiling',
'scheduler/tracing': 'scheduler/tracing-profiling'
}),
...(modules.webpackAliases || {}),
...(modules.webpackAliases || {})
},
plugins: [
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
......@@ -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,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson])
]
},
resolveLoader: {
plugins: [
// Also related to Plug'n'Play, but this time it tells webpack to load its loaders
// from the current package.
PnpWebpackPlugin.moduleLoader(module),
],
PnpWebpackPlugin.moduleLoader(module)
]
},
module: {
strictExportPresence: true,
......@@ -353,13 +330,12 @@ module.exports = function (webpackEnv) {
cache: true,
formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
resolvePluginsRelativeTo: __dirname,
},
loader: require.resolve('eslint-loader'),
resolvePluginsRelativeTo: __dirname
},
loader: require.resolve('eslint-loader')
}
],
include: paths.appSrc,
include: paths.appSrc
},
{
// "oneOf" will traverse all following loaders until one will
......@@ -374,8 +350,8 @@ module.exports = function (webpackEnv) {
loader: require.resolve('url-loader'),
options: {
limit: imageInlineSizeLimit,
name: 'static/media/[name].[hash:8].[ext]',
},
name: 'static/media/[name].[hash:8].[ext]'
}
},
// Process application JS with Babel.
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
......@@ -384,9 +360,7 @@ module.exports = function (webpackEnv) {
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
customize: require.resolve('babel-preset-react-app/webpack-overrides'),
plugins: [
[
......@@ -394,12 +368,11 @@ module.exports = function (webpackEnv) {
{
loaderMap: {
svg: {
ReactComponent:
'@svgr/webpack?-svgo,+titleProp,+ref![path]',
},
},
},
],
ReactComponent: '@svgr/webpack?-svgo,+titleProp,+ref![path]'
}
}
}
]
],
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
......@@ -407,8 +380,8 @@ module.exports = function (webpackEnv) {
cacheDirectory: true,
// See #6846 for context on why cacheCompression is disabled
cacheCompression: false,
compact: isEnvProduction,
},
compact: isEnvProduction
}
},
// Process any JS outside of the app with Babel.
// Unlike the application JS, we only compile the standard ES features.
......@@ -420,12 +393,7 @@ module.exports = function (webpackEnv) {
babelrc: false,
configFile: false,
compact: false,
presets: [
[
require.resolve('babel-preset-react-app/dependencies'),
{ helpers: true },
],
],
presets: [[require.resolve('babel-preset-react-app/dependencies'), { helpers: true }]],
cacheDirectory: true,
// See #6846 for context on why cacheCompression is disabled
cacheCompression: false,
......@@ -434,8 +402,8 @@ module.exports = function (webpackEnv) {
// code. Without the options below, debuggers like VSCode
// show incorrect code and set breakpoints on the wrong lines.
sourceMaps: shouldUseSourceMap,
inputSourceMap: shouldUseSourceMap,
},
inputSourceMap: shouldUseSourceMap
}
},
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
......@@ -449,13 +417,13 @@ module.exports = function (webpackEnv) {
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
sourceMap: isEnvProduction && shouldUseSourceMap
}),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
sideEffects: true
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
......@@ -465,9 +433,9 @@ module.exports = function (webpackEnv) {
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: {
getLocalIdent: getCSSModuleLocalIdent,
},
}),
getLocalIdent: getCSSModuleLocalIdent
}
})
},
// Opt-in support for SASS (using .scss or .sass extensions).
// By default we support SASS Modules with the
......@@ -478,11 +446,11 @@ module.exports = function (webpackEnv) {
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
sourceMap: isEnvProduction && shouldUseSourceMap
},
'less-loader'
),
sideEffects: true,
sideEffects: true
},
{
test: lessModuleRegex,
......@@ -491,7 +459,7 @@ module.exports = function (webpackEnv) {
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
getLocalIdent: getCSSModuleLocalIdent
},
'less-loader'
)
......@@ -509,14 +477,14 @@ module.exports = function (webpackEnv) {
// by webpacks internal loaders.
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
name: 'static/media/[name].[hash:8].[ext]'
}
}
// ** STOP ** Are you adding a new loader?
// Make sure to add the new loader(s) before the "file" loader.
],
},
],
]
}
]
},
plugins: [
// Generates an `index.html` file with the <script> injected.
......@@ -525,7 +493,7 @@ module.exports = function (webpackEnv) {
{},
{
inject: true,
template: paths.appHtml,
template: paths.appHtml
},
isEnvProduction
? {
......@@ -539,8 +507,8 @@ module.exports = function (webpackEnv) {
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
minifyURLs: true
}
}
: undefined
)
......@@ -548,9 +516,7 @@ module.exports = function (webpackEnv) {
// Inlines the webpack runtime script. This script is too small to warrant
// a network request.
// https://github.com/facebook/create-react-app/issues/5358
isEnvProduction &&
shouldInlineRuntimeChunk &&
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
isEnvProduction && shouldInlineRuntimeChunk && new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
// Makes some environment variables available in index.html.
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
......@@ -576,14 +542,13 @@ module.exports = function (webpackEnv) {
// to restart the development server for webpack to discover it. This plugin
// makes the discovery automatic so you don't have to restart.
// See https://github.com/facebook/create-react-app/issues/186
isEnvDevelopment &&
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
isEnvDevelopment && new WatchMissingNodeModulesPlugin(paths.appNodeModules),
isEnvProduction &&
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
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:
// - "files" key: Mapping of all asset filenames to their corresponding
......@@ -596,22 +561,18 @@ module.exports = function (webpackEnv) {
publicPath: paths.publicUrlOrPath,
generate: (seed, files, entrypoints) => {
const manifestFiles = files.reduce((manifest, file) => {
manifest[file.name] = file.path;
return manifest;
}, seed);
const entrypointFiles = entrypoints.main.filter(
fileName => !fileName.endsWith('.map')
);
manifest[file.name] = file.path
return manifest
}, seed)
const entrypointFiles = entrypoints.main.filter((fileName) => !fileName.endsWith('.map'))
return {
files: manifestFiles,
entrypoints: entrypointFiles,
};
},
entrypoints: entrypointFiles
}
}
}),
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
......@@ -633,36 +594,26 @@ module.exports = function (webpackEnv) {
// as they're likely a resource and not a SPA route.
// URLs containing a "?" character won't be blacklisted as they're likely
// a route with query params (e.g. auth callbacks).
new RegExp('/[^/?]+\\.[^/]+$'),
],
new RegExp('/[^/?]+\\.[^/]+$')
]
}),
// TypeScript type checking
useTypeScript &&
new ForkTsCheckerWebpackPlugin({
typescript: resolve.sync('typescript', {
basedir: paths.appNodeModules,
basedir: paths.appNodeModules
}),
async: isEnvDevelopment,
useTypescriptIncrementalApi: true,
checkSyntacticErrors: true,
resolveModuleNameModule: process.versions.pnp
? `${__dirname}/pnpTs.js`
: undefined,
resolveTypeReferenceDirectiveModule: process.versions.pnp
? `${__dirname}/pnpTs.js`
: undefined,
resolveModuleNameModule: process.versions.pnp ? `${__dirname}/pnpTs.js` : undefined,
resolveTypeReferenceDirectiveModule: process.versions.pnp ? `${__dirname}/pnpTs.js` : undefined,
tsconfig: paths.appTsConfig,
reportFiles: [
'**',
'!**/__tests__/**',
'!**/?(*.)(spec|test).*',
'!**/src/setupProxy.*',
'!**/src/setupTests.*',
],
reportFiles: ['**', '!**/__tests__/**', '!**/?(*.)(spec|test).*', '!**/src/setupProxy.*', '!**/src/setupTests.*'],
silent: true,
// The formatter is invoked directly in WebpackDevServerUtils during development
formatter: isEnvProduction ? typescriptFormatter : undefined,
}),
formatter: isEnvProduction ? typescriptFormatter : undefined
})
// new vConsolePlugin({
// enable: (process.env.DEPLOY_ENV === 'prod' || process.env.DEPLOY_ENV === 'beta') ? false : true
// })
......@@ -677,10 +628,10 @@ module.exports = function (webpackEnv) {
http2: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
child_process: 'empty'
},
// Turn off performance processing because we utilize
// our own hints via the FileSizeReporter
performance: false,
};
};
performance: false
}
}
......@@ -36,7 +36,7 @@
"case-sensitive-paths-webpack-plugin": "2.3.0",
"classnames": "^2.2.6",
"cropper": "^3.1.4",
"cross-env": "^7.0.2",
"cross-env": "^7.0.3",
"css-loader": "3.4.2",
"dom-to-image": "^2.6.0",
"dotenv": "8.2.0",
......
/*
* @Author: 吴文洁
* @Date: 2020-07-23 14:54:16
* @LastEditors: wufan
* @LastEditTime: 2021-02-02 19:32:21
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-24 21:04:42
* @Description: 大班直播课预览弹窗
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
import { Modal } from 'antd';
import moment from 'moment';
import React from 'react'
import { Modal } from 'antd'
import moment from 'moment'
import './PreviewCourseModal.less';
import './PreviewCourseModal.less'
class PreviewCourseModal extends React.Component {
constructor(props) {
super(props);
this.state = {
}
super(props)
this.state = {}
}
dealWithTime = (startTime, endTime) => {
const startDate = new Date(Number(startTime));
const endDate = new Date(Number(endTime));
const startDate = new Date(Number(startTime))
const endDate = new Date(Number(endTime))
const year = startDate.getFullYear();
const month = (startDate.getMonth() + 1) < 10 ? `0${startDate.getMonth() + 1}` : startDate.getMonth() + 1;
const day = startDate.getDate() < 10 ? `0${startDate.getDate()}` : startDate.getDate();
const year = startDate.getFullYear()
const month = startDate.getMonth() + 1 < 10 ? `0${startDate.getMonth() + 1}` : startDate.getMonth() + 1
const day = startDate.getDate() < 10 ? `0${startDate.getDate()}` : startDate.getDate()
const startHour = startDate.getHours() < 10 ? `0${startDate.getHours()}` : startDate.getHours();
const startMinute = startDate.getMinutes() < 10 ? `0${startDate.getMinutes()}` : startDate.getMinutes();
const startHour = startDate.getHours() < 10 ? `0${startDate.getHours()}` : startDate.getHours()
const startMinute = startDate.getMinutes() < 10 ? `0${startDate.getMinutes()}` : startDate.getMinutes()
const endHour = endDate.getHours() < 10 ? `0${endDate.getHours()}` : endDate.getHours();
const endMinute = endDate.getMinutes() < 10 ? `0${endDate.getMinutes()}` : endDate.getMinutes();
const endHour = endDate.getHours() < 10 ? `0${endDate.getHours()}` : endDate.getHours()
const endMinute = endDate.getMinutes() < 10 ? `0${endDate.getMinutes()}` : endDate.getMinutes()
const liveDateStr = `${year}-${month}-${day}`;
const startTimeStr = `${startHour}:${startMinute}`;
const endTimeStr = `${endHour}:${endMinute}`;
const liveDateStr = `${year}-${month}-${day}`
const startTimeStr = `${startHour}:${startMinute}`
const endTimeStr = `${endHour}:${endMinute}`
return {
liveDateStr,
startTimeStr,
endTimeStr,
};
endTimeStr
}
}
render() {
const { courseBasinInfo, courseClassInfo = {}, courseIntroInfo, type } = this.props;
const { coverUrl, courseName, scheduleVideoUrl } = courseBasinInfo;
const { liveDate, timeHorizonStart, timeHorizonEnd, nickname } = courseClassInfo;
const { liveCourseMediaRequests } = courseIntroInfo;
const { courseBasinInfo, courseClassInfo = {}, courseIntroInfo, type } = this.props
const { coverUrl, courseName, scheduleVideoUrl } = courseBasinInfo
const { liveDate, timeHorizonStart, timeHorizonEnd, nickname } = courseClassInfo
const { liveCourseMediaRequests } = courseIntroInfo
let liveDateStr, startTimeStr, endTimeStr;
let liveDateStr, startTimeStr, endTimeStr
if (liveDate) {
const _liveDate = moment(liveDate).format("YYYY-MM-DD");
const _timeHorizonStart = moment(timeHorizonStart).format('HH:mm');
const _timeHorizonEnd = moment(timeHorizonEnd).format('HH:mm');
const startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x');
const endTime = moment(_liveDate + ' ' + _timeHorizonEnd).format('x');
const {
liveDateStr: _liveDateStr,
startTimeStr: _startTimeStr,
endTimeStr: _endTimeStr
} = this.dealWithTime(startTime, endTime);
liveDateStr = _liveDateStr;
startTimeStr = _startTimeStr;
endTimeStr = _endTimeStr;
const _liveDate = moment(liveDate).format('YYYY-MM-DD')
const _timeHorizonStart = moment(timeHorizonStart).format('HH:mm')
const _timeHorizonEnd = moment(timeHorizonEnd).format('HH:mm')
const startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x')
const endTime = moment(_liveDate + ' ' + _timeHorizonEnd).format('x')
const { liveDateStr: _liveDateStr, startTimeStr: _startTimeStr, endTimeStr: _endTimeStr } = this.dealWithTime(startTime, endTime)
liveDateStr = _liveDateStr
startTimeStr = _startTimeStr
endTimeStr = _endTimeStr
}
return (
<Modal
title="预览"
title='预览'
visible={true}
width={680}
onCancel={this.props.close}
footer={null}
maskClosable={false}
className="preview-live-course-modal"
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
>
<div className="container__wrap">
<div className="container">
<div className="container__header">
className='preview-live-course-modal'
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}>
<div className='container__wrap'>
<div className='container'>
<div className='container__header'>
<Choose>
<When condition={type === 'videoCourse'}>
<video
controls
src={scheduleVideoUrl}
poster={coverUrl ? coverUrl : `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`}
className="course-url"
className='course-url'
/>
</When>
<Otherwise>
<img src={coverUrl} className="course-cover" alt="course-cover" />
<img src={coverUrl} className='course-cover' alt='course-cover' />
</Otherwise>
</Choose>
</div>
<Choose>
<When condition={type === 'videoCourse'}>
<div className="container__body">
<div className="title__name">{courseName}</div>
<div className="title__inst-name">{window.currentUserInstInfo.name}</div>
<div className='container__body'>
<div className='title__name'>{courseName}</div>
<div className='title__inst-name'>{window.currentUserInstInfo.name}</div>
</div>
</When>
<Otherwise>
<div className="container__body">
<div className="container__body__title">
<div className="title__name">{courseName}</div>
<div className="title__state">待开课</div>
<div className='container__body'>
<div className='container__body__title'>
<div className='title__name'>{courseName}</div>
<div className='title__state'>待开课</div>
</div>
<div className="container__body__time">
<span className="time__label">上课时间:</span>
<span className="time__value">
{
liveDate && timeHorizonStart && timeHorizonEnd &&
[
<div className='container__body__time'>
<span className='time__label'>上课时间:</span>
<span className='time__value'>
{liveDate &&
timeHorizonStart &&
timeHorizonEnd && [
<span>{liveDateStr}&nbsp;</span>,
<span>{startTimeStr}~{endTimeStr }</span>
]
}
<span>
{startTimeStr}~{endTimeStr}
</span>
]}
</span>
</div>
<div className="container__body__teacher">
<span className="teacher__label">上课老师:</span>
<span className="teacher__value">{nickname}</span>
<div className='container__body__teacher'>
<span className='teacher__label'>上课老师:</span>
<span className='teacher__value'>{nickname}</span>
</div>
</div>
</Otherwise>
</Choose>
<div className="container__introduction">
<div className="container__introduction__title">直播课简介</div>
<div className="container__introduction__list editor-box">
{
liveCourseMediaRequests.map((item, index) => {
<div className='container__introduction'>
<div className='container__introduction__title'>直播课简介</div>
<div className='container__introduction__list editor-box'>
{liveCourseMediaRequests.map((item, index) => {
if (item.mediaType === 'TEXT') {
return (
<div
className="intro-item text"
className='intro-item text'
dangerouslySetInnerHTML={{
__html: item.mediaContent
}}
......@@ -153,14 +143,13 @@ class PreviewCourseModal extends React.Component {
if (item.mediaType === 'PICTURE') {
return (
<div className="intro-item picture">
<img src={item.mediaUrl} alt="intro-item__picture" />
<div className='intro-item picture'>
<img src={item.mediaUrl} alt='intro-item__picture' />
</div>
)
}
return item;
})
}
return item
})}
</div>
</div>
</div>
......@@ -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;
color: #989898;
margin-top:10px;
margin-top: 10px;
margin-bottom: 10px;
+.statistic-tips{
+ .statistic-tips {
position: relative;
top: -8px;
margin-bottom: 0px !important;
......@@ -20,26 +20,25 @@
font-size: @xm-font-size-m;
line-height: 25px;
}
.ant-breadcrumb{
.ant-breadcrumb {
display: inline-block;
span{
span {
color: #333;
font-weight: 400;
}
}
.divide{
.divide {
display: inline-block;
position: relative;
height: 12px;
width: 25px;
&:after{
content:'';
&:after {
content: "";
position: absolute;
left: 12px;
height: 16px;
top: 0px;
border-left:1px solid #989898;
border-left: 1px solid #989898;
}
}
}
......@@ -2,28 +2,26 @@
* @Description:
* @Author: zangsuyun
* @Date: 2021-03-13 10:57:14
* @LastEditors: zangsuyun
* @LastEditTime: 2021-03-13 17:16:44
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-24 22:10:50
* @Copyright: © 2020 杭州杰竞科技有限公司 版权所有
*/
import PropTypes from 'prop-types';
import PropTypes from 'prop-types'
import React from 'react'
// import { Modal } from 'antd';
import './TableSelectedData.less';
import './TableSelectedData.less'
class TableSelectedData extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className={this.props.className+' selected-data-box'}>
<span className="icon iconfont">&#xe61d;</span>
<span className="selected-text">{'已选择'+this.props.selectedNum+'项'}</span>
<span className="click-clear" onClick={this.props.clearSelectedData}>清空</span>
<div className={this.props.className + ' selected-data-box'}>
<span className='icon iconfont'>&#xe61d;</span>
<span className='selected-text'>{'已选择' + this.props.selectedNum + '项'}</span>
<span className='click-clear' onClick={this.props.clearSelectedData}>
清空
</span>
</div>
)
}
......@@ -32,14 +30,13 @@ class TableSelectedData extends React.Component {
TableSelectedData.propTypes = {
className: PropTypes.string, // class
selectedNum: PropTypes.number, // 已选择人数
clearSelectedData: PropTypes.func, // 取消全部选择
};
clearSelectedData: PropTypes.func // 取消全部选择
}
TableSelectedData.defaultProps = {
className: '',
selectedNum: 0,
clearSelectedData: function () {
}
clearSelectedData: function () {}
}
export default TableSelectedData
......@@ -10,7 +10,7 @@
// 消息提示框
.ant-message {
&>span {
& > span {
display: block !important;
}
}
......@@ -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%;
}
.ant-calendar-month-panel {
.ant-calendar-month-panel-header {
.ant-calendar-month-panel-next-year-btn,
.ant-calendar-month-panel-prev-year-btn {
height: auto;
......@@ -35,12 +34,10 @@
}
}
.ant-form-item-children {
display: block;
}
.ant-switch-small:after {
top: 0.3px;
}
......@@ -88,7 +85,7 @@
}
.ant-dropdown-link {
color: #5D5D5E !important;
color: #5d5d5e !important;
}
.ant-table-fixed-header {
......@@ -135,7 +132,7 @@
}
.ant-input[disabled] {
background-color: #F0F2F5 !important;
background-color: #f0f2f5 !important;
}
.ant-dropdown-link {
......@@ -171,7 +168,6 @@ background-color: #F0F2F5 !important;
td {
background: @xm-table-body-active !important;
}
}
tr {
......@@ -189,18 +185,17 @@ background-color: #F0F2F5 !important;
td {
background: @xm-table-body-active !important;
}
}
}
}
}
.ant-table-bordered {
.ant-table-body {
border: 1px solid #e8e8e8;
}
.ant-table-header>table,.ant-table-body>table {
.ant-table-header > table,
.ant-table-body > table {
border: none !important;
border-radius: 3px;
.icon {
......@@ -217,14 +212,10 @@ background-color: #F0F2F5 !important;
border-right: 1px solid #e8e8e8 !important;
}
.ant-input-search-button {
height: 32px !important;
}
.ant-avatar {
width: 35px;
height: 35px;
......@@ -237,7 +228,7 @@ background-color: #F0F2F5 !important;
padding: 8px 20px 12px !important;
font-size: 16px !important;
>span {
> span {
font-size: 16px !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;
}
.ant-checkbox-inner {
......@@ -310,8 +301,6 @@ background-color: #F0F2F5 !important;
left: 0px;
}
.ml16 {
margin-left: 16px !important;
}
......@@ -322,7 +311,6 @@ background-color: #F0F2F5 !important;
ml0 {
margin-left: 0 !important;
}
mr0 {
......@@ -339,28 +327,24 @@ mr0 {
transform: translate(-8px, -8px) scale(0.7) !important;
}
.ant-select-selection {
border-color: @xm-color-border !important;
}
.ant-select-dropdown-menu-item-active:not(.ant-select-dropdown-menu-item-disabled) {
background: #F3F6FA;
background: #f3f6fa;
.ant-select-selected-icon {
color:#ffb000 !important;
color: #ffb000 !important;
}
}
.ant-select-dropdown-menu-item-selected {
background: none;
font-weight: 400 !important;
color:#ffb000;
color: #ffb000;
}
.ant-select-open .ant-select-selection {
box-shadow: none;
}
......@@ -391,19 +375,17 @@ mr0 {
}
}
::-webkit-scrollbar {
::-webkit-scrollbar {
width: 6px;
height: 6px;
display: block;
}
}
::-webkit-scrollbar-thumb {
::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
border-radius: 3px; // background:rgba(204,204,204,1);
background: rgba(204, 204, 204, 1);
}
}
.ant-avatar {
background: #e8e8e8;
......@@ -413,7 +395,7 @@ mr0 {
border-radius: 0 !important;
}
.ant-table-small>.ant-table-content>.ant-table-body {
.ant-table-small > .ant-table-content > .ant-table-body {
margin: 0px;
}
......@@ -444,14 +426,13 @@ mr0 {
}
}
.table-no-scrollbar {
::-webkit-scrollbar {
width: 0;
}
}
.ant-table-small>.ant-table-content>.ant-table-body {
.ant-table-small > .ant-table-content > .ant-table-body {
margin: 0 0px !important;
}
......@@ -488,10 +469,8 @@ mr0 {
margin: 0 12px;
}
.ant-modal-confirm-info .ant-modal-confirm-body > .anticon {
color: #ffb000!important;
color: #ffb000 !important;
}
.ant-breadcrumb > span:last-child .ant-breadcrumb-separator {
......@@ -511,18 +490,18 @@ mr0 {
text-align: inherit !important;
}
.ant-modal-confirm-info .ant-modal-confirm-body>.anticon {
color: #FFBB54 !important;
.ant-modal-confirm-info .ant-modal-confirm-body > .anticon {
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;
line-height: 22px !important;
float: left !important;
color:#ffb000 !important;
color: #ffb000 !important;
margin-right: 16px !important;
&.blue {
color: #5CBAFF !important;
color: #5cbaff !important;
}
}
......@@ -537,19 +516,19 @@ mr0 {
// confirm弹窗自定义图标 相关样式调整
.ant-modal-body .ant-modal-confirm-body-wrapper .ant-modal-confirm-body {
>.solid-icon {
> .solid-icon {
color: #faad14;
font-size: 22px;
line-height: 22px;
float: left;
margin-right: 16px;
}
>.confirm-icon{
> .confirm-icon {
color: #ffb000;
}
.ant-modal-confirm-content {
margin-left: 38px;
>.solid-icon-content {
> .solid-icon-content {
margin-left: 40px;
}
}
......@@ -592,47 +571,43 @@ mr0 {
}
//滚动条的公共样式处理
.ant-select-dropdown .rc-virtual-list-scrollbar .rc-virtual-list-scrollbar-thumb{
background: #D6D6D6 !important;
width:6px !important;
height:80px !important;
.ant-select-dropdown .rc-virtual-list-scrollbar .rc-virtual-list-scrollbar-thumb {
background: #d6d6d6 !important;
width: 6px !important;
height: 80px !important;
}
.ant-select-dropdown .rc-virtual-list-scrollbar .rc-virtual-list-scrollbar-thumb:hover{
background: #ADADAD !important;
.ant-select-dropdown .rc-virtual-list-scrollbar .rc-virtual-list-scrollbar-thumb:hover {
background: #adadad !important;
}
//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 > tbody > tr > .ant-table-cell-fix-right-first::after
{
border-right:none !important;
.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;
}
// .ant-table tbody tr:nth-child(2n) td{
// background: #FAFAFA !important;
// }
.ant-table-thead > tr > th{
font-weight:normal !important;
.ant-table-thead > tr > th {
font-weight: normal !important;
}
td.ant-table-column-sort{
td.ant-table-column-sort {
background: none;
}
.ant-modal-content .ant-table-thead > tr > th{
padding:9px 24px;
.ant-modal-content .ant-table-thead > tr > th {
padding: 9px 24px;
}
.ant-modal-content tr > td{
padding:14px 24px !important;
.ant-modal-content tr > td {
padding: 14px 24px !important;
}
//弹框里的table样式的处理
//按钮的样式的公共处理
.ant-btn-loading {
padding-left:18px !important;
padding-left: 18px !important;
}
.ant-input,
......@@ -640,27 +615,25 @@ td.ant-table-column-sort{
border-color: @xm-color-border;
}
//普通按钮样式
.ant-btn {
height:28px !important;
height: 28px !important;
font-weight: normal !important;
border: 1px solid #e8e8e8;
box-shadow: none !important;
padding:0px 12px !important;
color:#666 !important;
.span{
line-height:1;
padding: 0px 12px !important;
color: #666 !important;
.span {
line-height: 1;
}
&:hover {
border:1px solid rgba(232, 232, 232, 1) !important;
border: 1px solid rgba(232, 232, 232, 1) !important;
opacity: 0.7;
}
&:focus,
&:active{
color:#666 !important;
border:1px solid #C6C6C6 !important;
&:active {
color: #666 !important;
border: 1px solid #c6c6c6 !important;
}
}
......@@ -677,62 +650,66 @@ td.ant-table-column-sort{
}
background-color: @xm-color-text-select-primary !important;
border-color: @xm-color-text-select-primary !important;
border:none !important;
border: none !important;
&:hover {
background-color: #FFB714 !important;
background-color: #ffb714 !important;
opacity: 0.8 !important;
}
&:focus,
&:active{
background-color:#FFA100 !important;
&:active {
background-color: #ffa100 !important;
}
}
//警示按钮样式
.ant-btn-dangerous{
background:rgba(255, 79, 79, 1) !important;
color:#FFF !important;
border:none !important;
.ant-btn-dangerous {
background: rgba(255, 79, 79, 1) !important;
color: #fff !important;
border: none !important;
}
.ant-btn-dangerous:hover{
background:rgba(255, 79, 79, 0.7) !important;
color:#FFF !important;
border:none !important;
.ant-btn-dangerous:hover {
background: rgba(255, 79, 79, 0.7) !important;
color: #fff !important;
border: none !important;
}
.ant-btn-dangerous:focus,
.ant-btn-dangerous:active{
background:rgba(255, 79, 79,1) !important;
color:#FFF !important;
border:none !important;
.ant-btn-dangerous:active {
background: rgba(255, 79, 79, 1) !important;
color: #fff !important;
border: none !important;
}
//disabled的按钮的样式
.ant-btn-primary[disabled], .ant-btn-primary[disabled]:hover, .ant-btn-primary[disabled]:focus, .ant-btn-primary[disabled]:active{
background: #F7F8F9 !important;
border: 1px solid #E8E8E8 !important;
color: #CCCCCC !important;
span{
color: #CCCCCC !important;
.ant-btn-primary[disabled],
.ant-btn-primary[disabled]:hover,
.ant-btn-primary[disabled]:focus,
.ant-btn-primary[disabled]:active {
background: #f7f8f9 !important;
border: 1px solid #e8e8e8 !important;
color: #cccccc !important;
span {
color: #cccccc !important;
}
}
//时间选择器后的按钮样式
.ant-picker-ok{
.ant-btn{
padding:0 8px !important;
.ant-picker-ok {
.ant-btn {
padding: 0 8px !important;
}
}
//搜索框后面的按钮样式
.ant-input-search-button{
height:32px !important;
.ant-input-search-button {
height: 32px !important;
}
.ant-input-search > .ant-input-group > .ant-input-group-addon:last-child .ant-input-search-button{
background: #FFF !important;
border-top:1px solid #dfdfdf !important;
border-right:1px solid #dfdfdf !important;
border-bottom:1px solid #dfdfdf !important;
.ant-input-search > .ant-input-group > .ant-input-group-addon:last-child .ant-input-search-button {
background: #fff !important;
border-top: 1px solid #dfdfdf !important;
border-right: 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: 1px solid #d9d9d9 !important;
box-shadow: none !important;
......@@ -743,9 +720,9 @@ td.ant-table-column-sort{
// border-right:1px solid #FFA100 !important;
// 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-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 {
......@@ -756,44 +733,44 @@ td.ant-table-column-sort{
//模态框的样式的公共处理
//删除模态框的样式的的处理
.ant-modal-confirm{
.ant-modal-body{
padding:32px 32px 24px 32px !important;
.ant-modal-confirm-title{
color: rgba(0, 0, 0, 0.85)
.ant-modal-confirm {
.ant-modal-body {
padding: 32px 32px 24px 32px !important;
.ant-modal-confirm-title {
color: rgba(0, 0, 0, 0.85);
}
.ant-modal-confirm-content{
min-height:44px;
color: rgba(0, 0, 0, 0.65)
.ant-modal-confirm-content {
min-height: 44px;
color: rgba(0, 0, 0, 0.65);
}
}
}
//普通模态框的样式的处理
.ant-modal{
.ant-modal-title{
color:#333;
font-weight:400;
.ant-modal {
.ant-modal-title {
color: #333;
font-weight: 400;
}
.ant-modal-close-x{
.modal-close-icon{
color:#999;
font-size:12px;
margin-right:6px;
font-weight:400;
.ant-modal-close-x {
.modal-close-icon {
color: #999;
font-size: 12px;
margin-right: 6px;
font-weight: 400;
}
}
.ant-modal-footer{
padding:10px 24px;
.ant-modal-footer {
display: flex;
padding: 10px 24px;
}
}
// 排序小三角
.ant-table-column-sorter {
margin-left: 2px!important;
margin-left: 2px !important;
}
.ant-table-column-sorter-full{
margin-top:-0.32rem !important;
.ant-table-column-sorter-full {
margin-top: -0.32rem !important;
}
......@@ -8,7 +8,7 @@
right: 0;
bottom: 0;
z-index: 101;
background-color: #F0F2F5;
background-color: #f0f2f5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
.page {
......@@ -19,14 +19,14 @@
bottom: 0;
z-index: 102;
overflow: auto;
.box{
&:first-child{
.box {
&:first-child {
margin-bottom: 8px;
}
&+.box{
& + .box {
margin: 8px 16px;
}
&:last-child{
&:last-child {
margin-bottom: 16px;
}
}
......@@ -42,38 +42,40 @@
animation: all 0.75;
padding-bottom: 16px;
}
.content-sub-header{
.content-sub-header {
padding: 0px 16px 0;
line-height: 30px;
}
.content-header {
padding: 10px 16px;
padding: 16px 16px;
line-height: 30px;
background: #fff;
margin: 16px 16px 0;
font-size: 24px;
color: #333;
font-weight: 500;
h1 {
font-size: 14px;
color: #898989;
font-weight: normal;
display: inline-block;
}
}
.box{
.box {
padding: 16px;
margin: 0 16px 16px;
margin-bottom: 8px;
margin: 0 16px;
background: #ffffff;
// min-height: 400px;
.box-header {
line-height: 30px;
padding-bottom: 12px;
&.searchOnly{
&.searchOnly {
padding-bottom: 0px;
}
.filter-row {
min-height: 30px;
margin-bottom: 10px;
>* {
> * {
float: left;
margin-left: 16px;
height: 40px;
......@@ -89,16 +91,12 @@
.box-footer {
margin-top: 16px;
}
&:first-child{
&:first-child {
margin-bottom: 8px;
}
&+.box{
& + .box {
margin: 8px 16px;
}
&:last-child{
margin-bottom: 13px;
}
}
// .ant-calendar-picker{
// top:-1px;
......@@ -108,26 +106,25 @@
.box-header {
.ant-row-flex {
padding-top: 2px;
>div:nth-child(1) {
> div:nth-child(1) {
display: flex;
flex-wrap: wrap;
align-items:space-between;
align-items: space-between;
.flex(1);
>*{
> * {
flex-basis: 30%;
margin-right: 3%;
margin-bottom: 16px;
}
}
&.lastRow{
>div:nth-child(1) {
>*{
&.lastRow {
> div:nth-child(1) {
> * {
margin-bottom: 0px;
}
}
}
}
}
.full-screen-page {
......@@ -139,5 +136,5 @@
bottom: 0;
right: 0;
user-select: none;
background-color: #F0F2F5;
background-color: #f0f2f5;
}
/*
* @Author: yuananting
* @Date: 2021-03-03 15:13:12
* @LastEditors: yuananting
* @LastEditTime: 2021-04-13 13:58:40
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-25 10:07:03
* @Description: 助学工具接口
* @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) {
return Service.Hades("public/hades/queryCategoryTree", params);
return Service.Hades('public/hades/queryCategoryTree', params)
}
export function addCategory(params: object) {
return Service.Hades("public/hades/addCategory", params);
return Service.Hades('public/hades/addCategory', params)
}
export function delCategory(params: object) {
return Service.Hades("public/hades/delCategory", params);
return Service.Hades('public/hades/delCategory', params)
}
export function editCategory(params: object) {
return Service.Hades("public/hades/editCategory", params);
return Service.Hades('public/hades/editCategory', params)
}
export function editCategoryTree(params: object) {
return Service.Hades("public/hades/editCategoryTree", params);
return Service.Hades('public/hades/editCategoryTree', params)
}
export function queryQuestionPageList(params: object) {
return Service.Hades("public/hades/queryQuestionPageList", params);
return Service.Hades('public/hades/queryQuestionPageList', params)
}
export function addQuestion(params: object) {
return Service.Hades("public/hades/addQuestion", params);
return Service.Hades('public/hades/addQuestion', params)
}
export function deleteQuestion(params: object) {
return Service.Hades("public/hades/deleteQuestion", params);
return Service.Hades('public/hades/deleteQuestion', params)
}
export function queryQuestionDetails(params: object) {
return Service.Hades("public/hades/queryQuestionDetails", params);
return Service.Hades('public/hades/queryQuestionDetails', params)
}
export function editQuestion(params: object) {
return Service.Hades("public/hades/editQuestion", params);
return Service.Hades('public/hades/editQuestion', params)
}
export function batchImport(params: object) {
return Service.Hades("public/hades/batchImport", params);
return Service.Hades('public/hades/batchImport', params)
}
export function createPaper(params: object) {
return Service.Hades("public/hades/createPaper", params);
return Service.Hades('public/hades/createPaper', params)
}
export function queryPaperPageList(params: object) {
return Service.Hades("public/hades/queryPaperPageList", params);
return Service.Hades('public/hades/queryPaperPageList', params)
}
export function deletePaper(params: object) {
return Service.Hades("public/hades/deletePaper", params);
return Service.Hades('public/hades/deletePaper', params)
}
export function queryPaperDetail(params: object) {
return Service.Hades("public/hades/queryPaperDetail", params);
return Service.Hades('public/hades/queryPaperDetail', params)
}
export function viewPaper(params: object) {
return Service.Hades("public/hades/viewPaper", params);
return Service.Hades('public/hades/viewPaper', params)
}
export function editPaper(params: object) {
return Service.Hades("public/hades/editPaper", params);
return Service.Hades('public/hades/editPaper', params)
}
export function batchQueryQuestionDetails(params: object) {
return Service.Hades("public/hades/batchQueryQuestionDetails", params);
return Service.Hades('public/hades/batchQueryQuestionDetails', params)
}
export function queryQuestionPageListWithContent(params: object) {
return Service.Hades("public/hades/queryQuestionPageListWithContent", params);
return Service.Hades('public/hades/queryQuestionPageListWithContent', params)
}
/*
* @Author: yuananting
* @Date: 2021-03-11 11:34:37
* @LastEditors: yuananting
* @LastEditTime: 2021-04-13 13:58:11
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-24 23:44:39
* @Description: 助学工具接口
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import {
queryExternalCategoryTree,
queryCategoryTree,
addCategory,
delCategory,
......@@ -25,101 +26,110 @@ import {
viewPaper,
editPaper,
batchQueryQuestionDetails,
queryQuestionPageListWithContent,
} from '@/data-source/aidTool/request-apis';
queryQuestionPageListWithContent
} from '@/data-source/aidTool/request-apis'
export default class AidToolService {
/**
* 查询运营端分类书
* @param parmas
* @returns
*/
static queryExternalCategoryTree(parmas: any) {
return queryExternalCategoryTree(parmas)
}
// 获取题目分类树
static queryCategoryTree(params: any) {
return queryCategoryTree(params);
return queryCategoryTree(params)
}
// 新增题目分类
static addCategory(params: any) {
return addCategory(params);
return addCategory(params)
}
// 删除分类
static delCategory(params: any) {
return delCategory(params);
return delCategory(params)
}
// 编辑分类
static editCategory(params: any) {
return editCategory(params);
return editCategory(params)
}
// 编辑分类树(拖拽)
static editCategoryTree(params: any) {
return editCategoryTree(params);
return editCategoryTree(params)
}
// 查询题目列表
static queryQuestionPageList(params: any) {
return queryQuestionPageList(params);
return queryQuestionPageList(params)
}
// 添加题目
static addQuestion(params: any) {
return addQuestion(params);
return addQuestion(params)
}
// 删除题目
static deleteQuestion(params: any) {
return deleteQuestion(params);
return deleteQuestion(params)
}
// 预览题目
static queryQuestionDetails(params: any) {
return queryQuestionDetails(params);
return queryQuestionDetails(params)
}
// 编辑题目
static editQuestion(params: any) {
return editQuestion(params);
return editQuestion(params)
}
// 批量导入
static batchImport(params: any) {
return batchImport(params);
return batchImport(params)
}
// 创建试卷
static createPaper(params: any) {
return createPaper(params);
return createPaper(params)
}
// 查询试卷列表
static queryPaperPageList(params: any) {
return queryPaperPageList(params);
return queryPaperPageList(params)
}
// 删除试卷
static deletePaper(params: any) {
return deletePaper(params);
return deletePaper(params)
}
// 编辑前查询试卷信息
static queryPaperDetail(params: any) {
return queryPaperDetail(params);
return queryPaperDetail(params)
}
// 预览试卷
static viewPaper(params: any) {
return viewPaper(params);
return viewPaper(params)
}
// 编辑试卷
static editPaper(params: any) {
return editPaper(params);
return editPaper(params)
}
// 操作试卷-预览查询多题目信息
static batchQueryQuestionDetails(params: any) {
return batchQueryQuestionDetails(params);
return batchQueryQuestionDetails(params)
}
// 操作试卷-选择题目列表带题目详情
static queryQuestionPageListWithContent(params: any) {
return queryQuestionPageListWithContent(params);
return queryQuestionPageListWithContent(params)
}
}
/*
* @Author: 吴文洁
* @Date: 2020-07-23 14:54:16
* @LastEditors: wufan
* @LastEditTime: 2021-02-01 16:33:18
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-24 21:02:21
* @Description: 大班直播课预览弹窗
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
import { Modal } from 'antd';
import moment from 'moment';
import './PreviewCourseModal.less';
import React from 'react'
import { Modal } from 'antd'
import moment from 'moment'
import './PreviewCourseModal.less'
const courseStateShow = {
UN_START: {
title: "待开课",
title: '待开课'
},
STARTING: {
title: "上课中",
title: '上课中'
},
FINISH: {
title: "已完成",
title: '已完成'
},
EXPIRED: {
code: 4,
title: "未成功开课",
color: "#CCCCCC",
},
};
title: '未成功开课',
color: '#CCCCCC'
}
}
class PreviewCourseModal extends React.Component {
constructor(props) {
super(props);
this.state = {
}
super(props)
this.state = {}
}
dealTimeDuration = (time) => {
const diff = Math.floor(time % 3600);
let hours = Math.floor(time / 3600);
let mins = Math.floor(diff / 60);
let seconds = Math.floor(time % 60);
hours = hours < 10 ? ("0" + hours) : hours;
mins = mins < 10 ? ("0" + mins) : mins;
seconds = seconds < 10 ? ("0" + seconds) : seconds;
return hours + ":" + mins + ":" + seconds;
const diff = Math.floor(time % 3600)
let hours = Math.floor(time / 3600)
let mins = Math.floor(diff / 60)
let seconds = Math.floor(time % 60)
hours = hours < 10 ? '0' + hours : hours
mins = mins < 10 ? '0' + mins : mins
seconds = seconds < 10 ? '0' + seconds : seconds
return hours + ':' + mins + ':' + seconds
}
dealWithTime = (startTime, endTime) => {
const startDate = new Date(Number(startTime));
const endDate = new Date(Number(endTime));
const startDate = new Date(Number(startTime))
const endDate = new Date(Number(endTime))
const year = startDate.getFullYear();
const month = (startDate.getMonth() + 1) < 10 ? `0${startDate.getMonth() + 1}` : startDate.getMonth() + 1;
const day = startDate.getDate() < 10 ? `0${startDate.getDate()}` : startDate.getDate();
const year = startDate.getFullYear()
const month = startDate.getMonth() + 1 < 10 ? `0${startDate.getMonth() + 1}` : startDate.getMonth() + 1
const day = startDate.getDate() < 10 ? `0${startDate.getDate()}` : startDate.getDate()
const startHour = startDate.getHours() < 10 ? `0${startDate.getHours()}` : startDate.getHours();
const startMinute = startDate.getMinutes() < 10 ? `0${startDate.getMinutes()}` : startDate.getMinutes();
const startHour = startDate.getHours() < 10 ? `0${startDate.getHours()}` : startDate.getHours()
const startMinute = startDate.getMinutes() < 10 ? `0${startDate.getMinutes()}` : startDate.getMinutes()
const endHour = endDate.getHours() < 10 ? `0${endDate.getHours()}` : endDate.getHours();
const endMinute = endDate.getMinutes() < 10 ? `0${endDate.getMinutes()}` : endDate.getMinutes();
const endHour = endDate.getHours() < 10 ? `0${endDate.getHours()}` : endDate.getHours()
const endMinute = endDate.getMinutes() < 10 ? `0${endDate.getMinutes()}` : endDate.getMinutes()
const liveDateStr = `${year}-${month}-${day}`;
const startTimeStr = `${startHour}:${startMinute}`;
const endTimeStr = `${endHour}:${endMinute}`;
const liveDateStr = `${year}-${month}-${day}`
const startTimeStr = `${startHour}:${startMinute}`
const endTimeStr = `${endHour}:${endMinute}`
return {
liveDateStr,
startTimeStr,
endTimeStr,
};
}
endTimeStr
}
}
render() {
const { courseBasicInfo, courseClassInfo = {}, courseIntroInfo, type,courseState,origin} = this.props;
const { coverUrl, courseName, scheduleVideoUrl,videoDuration} = courseBasicInfo;
const { liveDate, calendarTime,startTime,endTime,timeHorizonStart, timeHorizonEnd, teacherName } = courseClassInfo;
const { introduce } = courseIntroInfo;
const { courseBasicInfo, courseClassInfo = {}, courseIntroInfo, type, courseState, origin } = this.props
const { coverUrl, courseName, scheduleVideoUrl, videoDuration } = courseBasicInfo
const { liveDate, calendarTime, startTime, endTime, timeHorizonStart, timeHorizonEnd, teacherName } = courseClassInfo
const { introduce } = courseIntroInfo
let liveDateStr, startTimeStr, endTimeStr;
let liveDateStr, startTimeStr, endTimeStr
if (type === 'add') {
const _liveDate = moment(calendarTime[0]).format("YYYY-MM-DD");
console.log("_liveDate",_liveDate);
const _timeHorizonStart = moment(startTime).format('HH:mm');
const _timeHorizonEnd = moment(endTime).format('HH:mm');
const _startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x');
const _endTime = moment(_liveDate + ' ' + _timeHorizonEnd).format('x');
const {
liveDateStr: _liveDateStr,
startTimeStr: _startTimeStr,
endTimeStr: _endTimeStr
} = this.dealWithTime(_startTime, _endTime);
liveDateStr = _liveDateStr;
startTimeStr = _startTimeStr;
endTimeStr = _endTimeStr;
}else{
const _liveDate = moment(liveDate).format("YYYY-MM-DD");
const _timeHorizonStart = moment(timeHorizonStart).format('HH:mm');
const _timeHorizonEnd = moment(timeHorizonEnd).format('HH:mm');
const startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x');
const endTime = moment(_liveDate + ' ' + _timeHorizonEnd).format('x');
const {
liveDateStr: _liveDateStr,
startTimeStr: _startTimeStr,
endTimeStr: _endTimeStr
} = this.dealWithTime(startTime, endTime);
liveDateStr = _liveDateStr;
startTimeStr = _startTimeStr;
endTimeStr = _endTimeStr;
const _liveDate = moment(calendarTime[0]).format('YYYY-MM-DD')
console.log('_liveDate', _liveDate)
const _timeHorizonStart = moment(startTime).format('HH:mm')
const _timeHorizonEnd = moment(endTime).format('HH:mm')
const _startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x')
const _endTime = moment(_liveDate + ' ' + _timeHorizonEnd).format('x')
const { liveDateStr: _liveDateStr, startTimeStr: _startTimeStr, endTimeStr: _endTimeStr } = this.dealWithTime(_startTime, _endTime)
liveDateStr = _liveDateStr
startTimeStr = _startTimeStr
endTimeStr = _endTimeStr
} else {
const _liveDate = moment(liveDate).format('YYYY-MM-DD')
const _timeHorizonStart = moment(timeHorizonStart).format('HH:mm')
const _timeHorizonEnd = moment(timeHorizonEnd).format('HH:mm')
const startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x')
const endTime = moment(_liveDate + ' ' + _timeHorizonEnd).format('x')
const { liveDateStr: _liveDateStr, startTimeStr: _startTimeStr, endTimeStr: _endTimeStr } = this.dealWithTime(startTime, endTime)
liveDateStr = _liveDateStr
startTimeStr = _startTimeStr
endTimeStr = _endTimeStr
}
return (
<Modal
title="预览"
title='预览'
visible={true}
width={680}
onCancel={this.props.close}
footer={null}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
className="preview-live-course-modal"
>
<div className="container__wrap">
<div className="container">
<div className="container__header">
{
type === 'videoCourse' ?
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
className='preview-live-course-modal'>
<div className='container__wrap'>
<div className='container'>
<div className='container__header'>
{type === 'videoCourse' ? (
<video
controls
src={scheduleVideoUrl}
poster={coverUrl ? coverUrl : `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`}
className="course-url"
/> :
<img src={coverUrl} className="course-cover" />
}
className='course-url'
/>
) : (
<img src={coverUrl} className='course-cover' />
)}
</div>
{
type === 'videoCourse' ?
<div className="container__body">
<div className="title__name">{courseName}</div>
{videoDuration &&
<div>视频时长:{this.dealTimeDuration(videoDuration)}</div>
}
{type === 'videoCourse' ? (
<div className='container__body'>
<div className='title__name'>{courseName}</div>
{videoDuration && <div>视频时长:{this.dealTimeDuration(videoDuration)}</div>}
</div>
:
<div className="container__body">
<div className="container__body__title">
<div className="title__name">{courseName}</div>
<div className="title__state">{courseStateShow[courseState].title}</div>
) : (
<div className='container__body'>
<div className='container__body__title'>
<div className='title__name'>{courseName}</div>
<div className='title__state'>{courseStateShow[courseState].title}</div>
</div>
<div className="container__body__time">
<span className="time__label">上课时间:</span>
<span className="time__value">
{
[
<div className='container__body__time'>
<span className='time__label'>上课时间:</span>
<span className='time__value'>
{[
<span>{liveDateStr}&nbsp;</span>,
<span>{startTimeStr}~{endTimeStr }</span>
]
}
<span>
{startTimeStr}~{endTimeStr}
</span>
]}
</span>
</div>
<div className="container__body__teacher">
<span className="teacher__label">上课老师:</span>
<span className="teacher__value">{teacherName}</span>
<div className='container__body__teacher'>
<span className='teacher__label'>上课老师:</span>
<span className='teacher__value'>{teacherName}</span>
</div>
</div>
}
<div className="container__introduction">
{ type === 'videoCourse' ?
<div className="container__introduction__title">视频课简介</div>
:
<div className="container__introduction__title">直播课简介</div>
}
<div className="container__introduction__list editor-box">
)}
<div className='container__introduction'>
{type === 'videoCourse' ? (
<div className='container__introduction__title'>视频课简介</div>
) : (
<div className='container__introduction__title'>直播课简介</div>
)}
<div className='container__introduction__list editor-box'>
<div
className="intro-item text"
className='intro-item text'
dangerouslySetInnerHTML={{
__html: introduce
}}
......@@ -192,4 +176,4 @@ class PreviewCourseModal extends React.Component {
}
}
export default PreviewCourseModal;
export default PreviewCourseModal
import React from 'react';
import {Table, Modal,Input} from 'antd';
import { PageControl } from "@/components";
import CourseService from "@/domains/course-domain/CourseService";
import PlanService from "@/domains/plan-domain/planService";
import React from 'react'
import { Table, Modal, Input } from 'antd'
import { PageControl } from '@/components'
import CourseService from '@/domains/course-domain/CourseService'
import PlanService from '@/domains/plan-domain/planService'
import User from '@/common/js/user'
import './RelatedPlanModal.less';
import _ from "underscore";
const { Search } = Input;
import './RelatedPlanModal.less'
import _ from 'underscore'
const { Search } = Input
class RelatedPlanModal extends React.Component {
constructor(props) {
super(props);
super(props)
this.state = {
dataSource:[],
size:10,
dataSource: [],
size: 10,
query: {
current: 1,
current: 1
},
totalCount:0,
selectPlanList:{},
};
totalCount: 0,
selectPlanList: {}
}
}
componentDidMount() {
this.handleFetchDataList();
this.handleFetchDataList()
}
// 获取培训计划列表
handleFetchDataList = () => {
const {query,size,totalCount} = this.state
const params ={
const { query, size } = this.state
const params = {
...query,
size,
storeId:User.getStoreId()
storeId: User.getStoreId()
}
PlanService.getStorePlanAll(params).then((res) => {
const { result = {} } = res ;
const { records = [], total = 0 } = result;
const { result = {} } = res
const { records = [], total = 0 } = result
this.setState({
dataSource: records,
totalCount: Number(total)
});
});
})
})
}
handleChangePlanName = (value)=>{
const {query} = this.state;
query.planName = value;
query.current = 1;
handleChangePlanName = (value) => {
const { query } = this.state
query.planName = value
query.current = 1
this.setState({
query
})
......@@ -54,9 +54,14 @@ class RelatedPlanModal extends React.Component {
if (current == size) {
return
}
this.setState({
this.setState(
{
size
},()=>{this.handleFetchDataList()})
},
() => {
this.handleFetchDataList()
}
)
}
// 请求表头
......@@ -66,14 +71,12 @@ class RelatedPlanModal extends React.Component {
title: '培训计划',
key: 'planName',
dataIndex: 'planName',
render:(val,record)=>{
return (
<span>{val}</span>
)
render: (val, record) => {
return <span>{val}</span>
}
}
];
return columns;
]
return columns
}
parseTaskColumns = (parentIndex) => {
......@@ -82,173 +85,189 @@ class RelatedPlanModal extends React.Component {
title: '任务名称',
key: 'taskName',
dataIndex: 'taskName',
render:(val,record)=>{
return (
<span>{val}</span>
)
render: (val, record) => {
return <span>{val}</span>
}
}
];
return columns;
]
return columns
}
selectPlanList = (record,selected,planId) =>{
const { selectPlanList } = this.props;
let _selectPlanList = {...selectPlanList};
selectPlanList = (record, selected, planId) => {
const { selectPlanList } = this.props
let _selectPlanList = { ...selectPlanList }
if (selected) {
if(!_selectPlanList[planId]){
if (!_selectPlanList[planId]) {
_selectPlanList[planId] = {}
}
_selectPlanList[planId].taskBaseVOList = [];
_selectPlanList[planId].planId = planId;
_selectPlanList[planId].taskBaseVOList.push(record);
_selectPlanList[planId].taskBaseVOList = []
_selectPlanList[planId].planId = planId
_selectPlanList[planId].taskBaseVOList.push(record)
} else {
if(!_selectPlanList[planId]){
if (!_selectPlanList[planId]) {
_selectPlanList[planId] = {}
}
_selectPlanList[planId].taskBaseVOList = [];
_selectPlanList[planId].planId = planId;
_selectPlanList[planId].taskBaseVOList = []
_selectPlanList[planId].planId = planId
}
this.props.onChange(_selectPlanList);
this.props.onChange(_selectPlanList)
// this.setState({selectPlanList:_selectPlanList});
}
handleSelectPlanListData(selectPlanList){
let _selectPlanList = [];
for(let key in selectPlanList ){
let item = {};
if(selectPlanList[key].taskBaseVOList){
item.planId = selectPlanList[key].planId;
if(selectPlanList[key].taskBaseVOList[0]){
item.taskId = selectPlanList[key].taskBaseVOList[0].taskId;
handleSelectPlanListData(selectPlanList) {
let _selectPlanList = []
for (let key in selectPlanList) {
let item = {}
if (selectPlanList[key].taskBaseVOList) {
item.planId = selectPlanList[key].planId
if (selectPlanList[key].taskBaseVOList[0]) {
item.taskId = selectPlanList[key].taskBaseVOList[0].taskId
}
}
if(item.taskId){
if (item.taskId) {
_selectPlanList.push(item)
}
}
return _selectPlanList;
return _selectPlanList
}
confirmRelatedPlan =()=>{
const {selectPlanList } = this.props;
confirmRelatedPlan = () => {
const { selectPlanList } = this.props
const params = {
courseId:this.props.selectCourseId,
relatedPlanList:this.handleSelectPlanListData(selectPlanList),
storeId:User.getStoreId(),
courseId: this.props.selectCourseId,
relatedPlanList: this.handleSelectPlanListData(selectPlanList),
storeId: User.getStoreId()
}
CourseService.relatedCourseToPlan(params).then((res) => {
this.props.onConfirm();
});
this.props.onConfirm()
})
}
getSelectLength = (selectList)=>{
let num = 0;
for(let key in selectList ){
if(selectList[key].taskBaseVOList.length > 0){
getSelectLength = (selectList) => {
let num = 0
for (let key in selectList) {
if (selectList[key].taskBaseVOList.length > 0) {
num = num + 1
}
}
return num;
return num
}
clearSelect = ()=>{
const _selectPlanList = {};
this.props.onChange(_selectPlanList);
clearSelect = () => {
const _selectPlanList = {}
this.props.onChange(_selectPlanList)
}
render() {
const { size,dataSource,totalCount,query} = this.state;
const { visible,selectPlanList} = this.props;
const { size, dataSource, totalCount, query } = this.state
const { visible, selectPlanList } = this.props
return (
<Modal
title="关联培训计划"
title='关联培训计划'
onCancel={this.props.onClose}
maskClosable={false}
visible={visible}
className="related-plan-modal"
className='related-plan-modal'
closable={true}
width={800}
onOk={() => this.confirmRelatedPlan() }
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
>
<div className="search-container">
<Search placeholder="搜索培训计划名称"
onOk={() => this.confirmRelatedPlan()}
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}>
<div className='search-container'>
<Search
placeholder='搜索培训计划名称'
style={{ width: 207 }}
onChange={(e) => { this.handleChangePlanName(e.target.value)}}
onSearch={ () => { this.handleFetchDataList()}}
enterButton={<span className="icon iconfont">&#xe832;</span>}
onChange={(e) => {
this.handleChangePlanName(e.target.value)
}}
onSearch={() => {
this.handleFetchDataList()
}}
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/>
</div>
<div className="select-container">
<span className="con">
<div className='select-container'>
<span className='con'>
<div>
<span className="icon iconfont tip">&#xe6f2;</span>
<span className="text">已选择{this.getSelectLength(selectPlanList)}个任务</span>
<span className='icon iconfont tip'>&#xe6f2;</span>
<span className='text'>已选择{this.getSelectLength(selectPlanList)}个任务</span>
</div>
<div>
<span className="clear" onClick={this.clearSelect}>清空</span>
<span className='clear' onClick={this.clearSelect}>
清空
</span>
</div>
</span>
</div>
<div>
<Table
rowKey={record => record.planId}
className="plan-table"
rowKey={(record) => record.planId}
className='plan-table'
dataSource={dataSource}
columns={this.parsePlanColumns()}
pagination={false}
expandedRowRender={(_record,index) => {
if(!_record.taskBaseVOList){
expandedRowRender={(_record, index) => {
if (!_record.taskBaseVOList) {
return
}
if (_record.taskBaseVOList.length !== 0 ){
if (_record.taskBaseVOList.length !== 0) {
const selectPlan = selectPlanList[_record.planId]
let taskBaseVOList = [];
if(selectPlan){
taskBaseVOList = selectPlan.taskBaseVOList;
let taskBaseVOList = []
if (selectPlan) {
taskBaseVOList = selectPlan.taskBaseVOList
}
console.log('taskBaseVOList',taskBaseVOList);
console.log('taskBaseVOList', taskBaseVOList)
return <div>
return (
<div>
<Table
rowKey={record => record.taskId}
rowKey={(record) => record.taskId}
pagination={false}
dataSource={_record.taskBaseVOList}
columns={this.parseTaskColumns(index)}
className="child-table"
className='child-table'
rowSelection={{
type: 'checkbox',
selectedRowKeys: _.pluck(taskBaseVOList, 'taskId'),
onSelect: (record, selected) => {
this.selectPlanList(record,selected,_record.planId);
},
onSelectAll: (selected, _selectedRows, changeRows) => {
this.selectPlanList(record, selected, _record.planId)
},
onSelectAll: (selected, _selectedRows, changeRows) => {}
}}
/>
</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 &&
<div className="box-footer">
{dataSource.length > 0 && (
<div className='box-footer'>
<PageControl
current={query.current - 1}
pageSize={size}
total={totalCount}
size="small"
size='small'
toPage={(page) => {
const _query = {...query, current: page + 1};
this.setState({
query:_query
},()=>{ this.handleFetchDataList()})
const _query = { ...query, current: page + 1 }
this.setState(
{
query: _query
},
() => {
this.handleFetchDataList()
}
)
}}
onShowSizeChange={this.onShowSizeChange}
/>
</div>
}
)}
</div>
</Modal>
)
}
}
export default RelatedPlanModal;
\ No newline at end of file
export default RelatedPlanModal
/*
* @Author: 吴文洁
* @Date: 2020-08-05 10:07:47
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-05-10 10:15:46
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-24 21:04:03
* @Description: 视频课新增/编辑页
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
import { Button, Input, Radio, message, Modal,Cascader} from 'antd';
import { DISK_MAP, FileTypeIcon, FileVerifyMap } from '@/common/constants/academic/lessonEnum';
import { ImgCutModalNew } from '@/components';
import ShowTips from "@/components/ShowTips";
import Breadcrumbs from "@/components/Breadcrumbs";
import AddVideoIntro from './components/AddVideoIntro';
import SelectStudent from '../modal/select-student';
import SelectPrepareFileModal from '../../prepare-lesson/modal/SelectPrepareFileModal';
import PreviewCourseModal from '../modal/PreviewCourseModal';
import StoreService from "@/domains/store-domain/storeService";
import CourseService from "@/domains/course-domain/CourseService";
import Service from '@/common/js/service';
import User from '@/common/js/user';
import _ from "underscore";
import Upload from '@/core/upload';
import { randomString } from '@/domains/basic-domain/utils';
import $ from 'jquery';
import React from 'react'
import { Button, Input, Radio, message, Modal, Cascader } from 'antd'
import { DISK_MAP, FileTypeIcon, FileVerifyMap } from '@/common/constants/academic/lessonEnum'
import { ImgCutModalNew } from '@/components'
import ShowTips from '@/components/ShowTips'
import Breadcrumbs from '@/components/Breadcrumbs'
import AddVideoIntro from './components/AddVideoIntro'
import SelectStudent from '../modal/select-student'
import SelectPrepareFileModal from '../../prepare-lesson/modal/SelectPrepareFileModal'
import PreviewCourseModal from '../modal/PreviewCourseModal'
import StoreService from '@/domains/store-domain/storeService'
import CourseService from '@/domains/course-domain/CourseService'
import Service from '@/common/js/service'
import User from '@/common/js/user'
import _ from 'underscore'
import Upload from '@/core/upload'
import { randomString } from '@/domains/basic-domain/utils'
import $ from 'jquery'
// import PhotoClip from 'photoclip';
import './AddVideoCourse.less';
import './AddVideoCourse.less'
const EDIT_BOX_KEY = Math.random();
const fieldNames = { label: 'categoryName', value: 'id', children: 'sonCategoryList' };
const EDIT_BOX_KEY = Math.random()
const fieldNames = { label: 'categoryName', value: 'id', children: 'sonCategoryList' }
//添加课程时课程默认的一些值
const defaultShelfState = 'YES';
const defaultScheduleMedia = [{
contentType:'INTRO',
const defaultShelfState = 'YES'
const defaultScheduleMedia = [
{
contentType: 'INTRO',
mediaType: 'TEXT',
mediaContent: '',
key: EDIT_BOX_KEY
}]
}
]
const whetherVisitorsJoin = 'NO'
let cutFlag = false;
let cutFlag = false
class AddVideoCourse extends React.Component {
constructor(props) {
super(props);
super(props)
const id = getParameterByName("id");
const pageType = getParameterByName("type");
const id = getParameterByName('id')
const pageType = getParameterByName('type')
this.state = {
id, // 视频课ID,编辑的时候从URL上带过来
......@@ -61,57 +62,60 @@ class AddVideoCourse extends React.Component {
coverId: null, // 视频封面的recourceId
coverUrl: null, // 视频课封面
studentList: [], // 上课学员列表
shelfState:'YES', //是否开启学院展示
scheduleMedia: [{ // 视频课媒体资源
contentType:"INTRO",
shelfState: 'YES', //是否开启学院展示
scheduleMedia: [
{
// 视频课媒体资源
contentType: 'INTRO',
mediaType: 'TEXT',
mediaContent: '',
key: EDIT_BOX_KEY
}],
}
],
diskList: [], // 机构可见磁盘目录
selectedFileList: [], // 已经从资料云盘中勾选的文件
showCutModal: false, // 是否显示截图弹窗
showSelectFileModal: false,
studentModal: false,
categoryName:null, //分类名称
courseCatalogList:[], //分类列表
categoryId:null, //分类的Id值
whetherVisitorsJoin:'NO', // 是否允许游客加入
showSelectCoverModal:false,
categoryName: null, //分类名称
courseCatalogList: [], //分类列表
categoryId: null, //分类的Id值
whetherVisitorsJoin: 'NO', // 是否允许游客加入
showSelectCoverModal: false,
cutImageBlob: null,
introduce: '',
introduce: ''
}
}
componentWillMount() {
const { id, pageType } = this.state;
this.getCourseCatalogList();
const { id, pageType } = this.state
this.getCourseCatalogList()
if (pageType === 'edit') {
this.handleFetchScheudleDetail(id);
this.handleFetchScheudleDetail(id)
}
}
//获取分类列表
getCourseCatalogList = ()=>{
StoreService.getCourseCatalogList({current:1,size:1000}).then((res) => {
getCourseCatalogList = () => {
StoreService.getCourseCatalogList({ current: 1, size: 1000 }).then((res) => {
this.setState({
courseCatalogList:res.result.records
courseCatalogList: res.result.records
})
})
});
}
catalogChange= (value) => {
const changeValueLength = value.length;
switch (changeValueLength){
catalogChange = (value) => {
const changeValueLength = value.length
switch (changeValueLength) {
case 1:
this.setState({categoryId:value[0]});
break;
this.setState({ categoryId: value[0] })
break
case 2:
this.setState({categoryId:value[1]});
break;
this.setState({ categoryId: value[1] })
break
default:
this.setState({categoryId:null});
break;
this.setState({ categoryId: null })
break
}
}
// 获取视频课详情
......@@ -119,54 +123,46 @@ class AddVideoCourse extends React.Component {
CourseService.videoScheduleDetail({
courseId
}).then((res) => {
const { result = {} } = res || {};
const {
courseName,
shelfState,
whetherVisitorsJoin,
courseMediaVOS,
categoryOneName,
categoryTwoName,
categoryId
} = result;
let coverId;
let coverUrl;
let videoType;
let videoDuration;
let videoName;
let scheduleMedia = [];
let scheduleVideoId;
let scheduleVideoUrl;
let hasIntro;
const { result = {} } = res || {}
const { courseName, shelfState, whetherVisitorsJoin, courseMediaVOS, categoryOneName, categoryTwoName, categoryId } = result
let coverId
let coverUrl
let videoType
let videoDuration
let videoName
let scheduleMedia = []
let scheduleVideoId
let scheduleVideoUrl
let hasIntro
courseMediaVOS.map((item) => {
switch (item.contentType){
case "COVER":
coverId = item.mediaContent;
coverUrl = item.mediaUrl;
break;
case "SCHEDULE":
videoDuration = item.videoDuration;
videoName = item.mediaName;
scheduleVideoId = item.mediaContent;
scheduleVideoUrl = item.mediaUrl;
videoType = item.mediaType;
break;
case "INTRO":
hasIntro = true;
this.getTextDetail('introduce', item.mediaUrl);
break;
switch (item.contentType) {
case 'COVER':
coverId = item.mediaContent
coverUrl = item.mediaUrl
break
case 'SCHEDULE':
videoDuration = item.videoDuration
videoName = item.mediaName
scheduleVideoId = item.mediaContent
scheduleVideoUrl = item.mediaUrl
videoType = item.mediaType
break
case 'INTRO':
hasIntro = true
this.getTextDetail('introduce', item.mediaUrl)
break
default:
break;
break
}
return item;
return item
})
let categoryName;
if( categoryTwoName ){
categoryName = `${categoryOneName}-${categoryTwoName}`;
}else{
categoryName = `${categoryOneName}`;
let categoryName
if (categoryTwoName) {
categoryName = `${categoryOneName}-${categoryTwoName}`
} else {
categoryName = `${categoryOneName}`
}
this.setState({
loadintroduce: !hasIntro,
......@@ -183,7 +179,7 @@ class AddVideoCourse extends React.Component {
whetherVisitorsJoin,
categoryName,
categoryId
});
})
})
}
......@@ -192,43 +188,42 @@ class AddVideoCourse extends React.Component {
data: {},
type: 'GET',
url,
contentType:'application/x-www-form-urlencoded; charset=UTF-8',
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
success: (res) => {
this.setState({ [key]: res, [`load${key}`]: true });
this.setState({ [key]: res, [`load${key}`]: true })
}
})
}
handleGoBack = () => {
const {
coverId,
videoName,
videoDuration,
courseName,
scheduleMedia,
scheduleVideoId,
categoryId,
shelfState,
whetherVisitorsJoin
} = this.state;
if(videoName || videoDuration || scheduleVideoId || !_.isEqual(scheduleMedia, defaultScheduleMedia) || categoryId || courseName || coverId || shelfState !== defaultShelfState || whetherVisitorsJoin !== whetherVisitorsJoin ){
const { coverId, videoName, videoDuration, courseName, scheduleMedia, scheduleVideoId, categoryId, shelfState, whetherVisitorsJoin } = this.state
if (
videoName ||
videoDuration ||
scheduleVideoId ||
!_.isEqual(scheduleMedia, defaultScheduleMedia) ||
categoryId ||
courseName ||
coverId ||
shelfState !== defaultShelfState ||
whetherVisitorsJoin !== whetherVisitorsJoin
) {
Modal.confirm({
title: '确认要返回吗?',
content: '返回后,本次编辑的内容将不被保存。',
okText: '确认返回',
cancelText: '留在本页',
icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>,
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
onOk: () => {
window.RCHistory.push({
pathname: `/video-course`,
});
pathname: `/video-course`
})
}
});
}else{
})
} else {
window.RCHistory.push({
pathname: `/video-course`,
});
pathname: `/video-course`
})
}
}
......@@ -237,51 +232,44 @@ class AddVideoCourse extends React.Component {
this.setState({
[field]: value,
coverUrl: coverUrl ? coverUrl : this.state.coverUrl
});
})
}
// 显示选择学员弹窗
handleShowSelectStuModal = () => {
this.setState({ studentModal : true });
this.setState({ studentModal: true })
const { studentList, selectedStuList } = this.state;
const { studentList, selectedStuList } = this.state
const studentModal = (
<SelectStudent
showTabs={true}
type="videoCourse"
type='videoCourse'
onSelect={this.handleSelectStudent}
after={true} //表明是不是上课后的状态
studentList={studentList}
close={() => {
this.setState({
studentModal: null,
});
studentModal: null
})
}}
/>
)
this.setState({ studentModal });
this.setState({ studentModal })
}
handleSelectStudent = (studentIds) => {
let studentList = [];
let studentList = []
_.each(studentIds, (item) => {
studentList.push({ studentId: item });
});
studentList.push({ studentId: item })
})
// this.setState({ studentModal: null });
this.setState({ studentList });
this.setState({ studentModal : false });
this.setState({ studentList })
this.setState({ studentModal: false })
}
// 显示预览弹窗
handleShowPreviewModal = () => {
const {
coverUrl,
scheduleVideoUrl,
courseName,
scheduleMedia,
videoDuration,
introduce,
} = this.state;
const { coverUrl, scheduleVideoUrl, courseName, scheduleMedia, videoDuration, introduce } = this.state
const courseBasinInfo = {
coverUrl,
......@@ -291,12 +279,12 @@ class AddVideoCourse extends React.Component {
}
const courseIntroInfo = {
liveCourseMediaRequests: scheduleMedia,
introduce,
introduce
}
const previewCourseModal = (
<PreviewCourseModal
type="videoCourse"
type='videoCourse'
courseBasicInfo={courseBasinInfo}
courseIntroInfo={courseIntroInfo}
close={() => {
......@@ -305,9 +293,9 @@ class AddVideoCourse extends React.Component {
})
}}
/>
);
)
this.setState({ previewCourseModal });
this.setState({ previewCourseModal })
}
// 选择视频
......@@ -315,25 +303,25 @@ class AddVideoCourse extends React.Component {
this.setState({
showSelectFileModal: false
})
const { ossUrl, resourceId, folderName, folderFormat, folderSize } = file;
const { ossUrl, resourceId, folderName, folderFormat, folderSize } = file
const videoDom = document.createElement('video');
videoDom.src = ossUrl;
const videoDom = document.createElement('video')
videoDom.src = ossUrl
videoDom.onloadedmetadata = () => {
this.setState({
size: folderSize,
videoName: folderName,
videoType: folderFormat,
scheduleVideoUrl: ossUrl,
scheduleVideoId : resourceId,
scheduleVideoId: resourceId,
videoDuration: videoDom.duration
});
})
}
}
// 保存
handleSubmit = () => {
const { instId, adminId } = window.currentUserInstInfo;
const { instId, adminId } = window.currentUserInstInfo
const {
id,
......@@ -352,113 +340,113 @@ class AddVideoCourse extends React.Component {
categoryId,
shelfState,
whetherVisitorsJoin,
introduce,
} = this.state;
introduce
} = this.state
const commonParams = {
videoName,
videoDuration,
courseMediaId: scheduleVideoId,
scheduleMedia: scheduleMedia.filter(item => !!item.mediaContent),
scheduleMedia: scheduleMedia.filter((item) => !!item.mediaContent),
categoryId,
courseName,
coverId,
operatorId:User.getStoreUserId(),
storeId:User.getStoreId(),
operatorId: User.getStoreUserId(),
storeId: User.getStoreId(),
shelfState,
whetherVisitorsJoin,
courseType: 'VOICE',
};
courseType: 'VOICE'
}
// 校验必填字段:课程名称, 课程视频
this.handleValidate(courseName, scheduleVideoId,categoryId, scheduleMedia).then((res) => {
if (!res) return;
this.handleValidate(courseName, scheduleVideoId, categoryId, scheduleMedia).then((res) => {
if (!res) return
Upload.uploadTextToOSS(introduce, `${randomString()}.txt`, (introduceId) => {
this.submitRemote({ id, pageType, commonParams: { ...commonParams, introduceId } })
});
});
})
})
}
submitRemote = ({ id, pageType, commonParams }) => {
if (pageType === 'add') {
Service.Hades('public/hades/createMediaCourse', commonParams).then((res) => {
if (!res) return;
message.success("新建成功");
if (!res) return
message.success('新建成功')
window.RCHistory.push({
pathname: `/video-course`,
});
pathname: `/video-course`
})
})
} else {
const editParams = {
courseId:id,
...commonParams,
courseId: id,
...commonParams
}
Service.Hades('public/hades/editMediaCourse', editParams).then((res) => {
if (!res) return;
message.success("保存成功");
if (!res) return
message.success('保存成功')
window.RCHistory.push({
pathname: `/video-course`,
});
});
pathname: `/video-course`
})
})
}
}
handleValidate = (courseName, scheduleVideoId,categoryId,scheduleMedia) => {
handleValidate = (courseName, scheduleVideoId, categoryId, scheduleMedia) => {
return new Promise((resolve) => {
if (!courseName) {
message.warning('请输入课程名称');
resolve(false);
message.warning('请输入课程名称')
resolve(false)
return false
}
if (!scheduleVideoId) {
message.warning('请上传视频');
resolve(false);
message.warning('请上传视频')
resolve(false)
return false
}
if(!categoryId){
message.warning('请选择课程分类');
resolve(false);
if (!categoryId) {
message.warning('请选择课程分类')
resolve(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++) {
if (textMedia[i].mediaContentLength && textMedia[i].mediaContentLength.length > 1000) {
message.warning(`第${i+1}个文字简介的字数超过了1000个字`);
resolve(false);
message.warning(`第${i + 1}个文字简介的字数超过了1000个字`)
resolve(false)
return false
}
}
resolve(true);
});
resolve(true)
})
}
handleSelectCover = (file)=> {
this.uploadImage(file);
handleSelectCover = (file) => {
this.uploadImage(file)
}
//上传图片
uploadImage = (imageFile) => {
const { folderName } = imageFile;
const fileName = window.random_string(16) + folderName.slice(folderName.lastIndexOf("."));
const self = this;
const { folderName } = imageFile
const fileName = window.random_string(16) + folderName.slice(folderName.lastIndexOf('.'))
const self = this
this.setState(
{
visible: true,
visible: true
},
() => {
setTimeout(() => {
const okBtnDom = document.querySelector("#headPicModal");
const okBtnDom = document.querySelector('#headPicModal')
const options = {
size: [500, 282],
ok: okBtnDom,
maxZoom: 3,
style: {
jpgFillColor: "transparent",
jpgFillColor: 'transparent'
},
done: function (dataUrl) {
clearTimeout(self.timer);
clearTimeout(self.timer)
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 cutImageBlob = self.convertBase64UrlToBlob(_dataUrl);
const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl)
self.setState({
cutImageBlob,
dataUrl: _dataUrl,
......@@ -466,20 +454,19 @@ class AddVideoCourse extends React.Component {
scale: this.scale()
})
}
}, 500)
const cutImageBlob = self.convertBase64UrlToBlob(dataUrl);
const cutImageBlob = self.convertBase64UrlToBlob(dataUrl)
self.setState({
cutImageBlob,
dataUrl
})
setTimeout(() => {
cutFlag = false;
}, 2000);
cutFlag = false
}, 2000)
},
fail: (failInfo) => {
message.error("图片上传失败了,请重新上传");
message.error('图片上传失败了,请重新上传')
},
loadComplete: function (img) {
setTimeout(() => {
......@@ -489,152 +476,199 @@ class AddVideoCourse extends React.Component {
hasImgReady: true
})
}, 100)
},
};
}
}
const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}`
if (!this.state.photoclip) {
const _photoclip = new PhotoClip("#headPicModal", options);
_photoclip.load(imgUrl);
const _photoclip = new PhotoClip('#headPicModal', options)
_photoclip.load(imgUrl)
this.setState({
photoclip: _photoclip,
});
photoclip: _photoclip
})
} else {
this.state.photoclip.clear();
this.state.photoclip.load(imgUrl);
this.state.photoclip.clear()
this.state.photoclip.load(imgUrl)
}
}, 200);
}, 200)
}
)
}
);
};
//获取resourceId
getSignature = (blob, fileName) => {
Upload.uploadBlobToOSS(blob, 'cover' + (new Date()).valueOf(),null,'signInfo').then((signInfo) => {
this.setState({
coverClicpPath:signInfo.fileUrl,
coverId:signInfo.resourceId,
Upload.uploadBlobToOSS(blob, 'cover' + new Date().valueOf(), null, 'signInfo').then((signInfo) => {
this.setState(
{
coverClicpPath: signInfo.fileUrl,
coverId: signInfo.resourceId,
visible: false
},()=>this.updateCover())
});
};
updateCover = () =>{
const {coverClicpPath,coverId} = this.state
},
() => this.updateCover()
)
})
}
updateCover = () => {
const { coverClicpPath, coverId } = this.state
this.setState({
showSelectCoverModal: false,
coverUrl:coverClicpPath,
coverId:coverId
coverUrl: coverClicpPath,
coverId: coverId
})
}
// base64转换成blob
convertBase64UrlToBlob = (urlData) => {
const bytes = window.atob(urlData.split(",")[1]);
const ab = new ArrayBuffer(bytes.length);
const ia = new Uint8Array(ab);
const bytes = window.atob(urlData.split(',')[1])
const ab = new ArrayBuffer(bytes.length)
const ia = new Uint8Array(ab)
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() {
const {
pageType,courseName, scheduleVideoId, coverId,
coverUrl, scheduleVideoUrl, studentList, scheduleMedia,
showCutModal, showSelectFileModal, diskList,
imageFile, joinType, videoName, videoType,shelfState,
categoryName,courseCatalogList,whetherVisitorsJoin,
visible,showSelectCoverModal,hasImgReady,cutImageBlob,
pageType,
courseName,
scheduleVideoId,
coverId,
coverUrl,
scheduleVideoUrl,
studentList,
scheduleMedia,
showCutModal,
showSelectFileModal,
diskList,
imageFile,
joinType,
videoName,
videoType,
shelfState,
categoryName,
courseCatalogList,
whetherVisitorsJoin,
visible,
showSelectCoverModal,
hasImgReady,
cutImageBlob,
introduce,
loadintroduce,
id,
} = this.state;
id
} = 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 (
<div className="page add-video-course-page">
<Breadcrumbs
navList={pageType === "add" ? "新建视频课" : "编辑视频课"}
goBack={this.handleGoBack}
/>
<div className='page add-video-course-page'>
<Breadcrumbs navList={pageType === 'add' ? '新建视频课' : '编辑视频课'} goBack={this.handleGoBack} />
<div className="box">
<div className="show-tips">
<ShowTips message="请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利" />
<div className='box'>
<div className='show-tips'>
<ShowTips message='请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利' />
</div>
<div className="form">
<div className="course-name required">
<span className="label">课程名称:</span>
<div className='form'>
<div className='course-name required'>
<span className='label'>课程名称:</span>
<Input
value={courseName}
placeholder="请输入视频课的名称(40字以内)"
placeholder='请输入视频课的名称(40字以内)'
maxLength={40}
style={{ width: 240 }}
onChange={(e) => { this.handleChangeForm('courseName', e.target.value)}}
onChange={(e) => {
this.handleChangeForm('courseName', e.target.value)
}}
/>
</div>
<div className="upload-video mt16">
<div className="content flex">
<span className="label required">视频上传:</span>
<div className="value">
{
scheduleVideoId ?
<div className="course-ware">
<img className="course-ware__img" src={courseWareIcon} />
<span className="course-ware__name">{videoName}</span>
</div> :
<div className="course-ware--empty">从资料云盘中选择视频</div>
}
<div className='upload-video mt16'>
<div className='content flex'>
<span className='label required'>视频上传:</span>
<div className='value'>
{scheduleVideoId ? (
<div className='course-ware'>
<img className='course-ware__img' src={courseWareIcon} />
<span className='course-ware__name'>{videoName}</span>
</div>
) : (
<div className='course-ware--empty'>从资料云盘中选择视频</div>
)}
</div>
</div>
<div className="sub-content">
<div className='sub-content'>
<Button
onClick={() => {
this.setState({
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 className="cover-url flex mt16">
<div className="label">视频封面:</div>
<div className="cover-url__wrap">
<div className="img-content">
<div className='cover-url flex mt16'>
<div className='label'>视频封面:</div>
<div className='cover-url__wrap'>
<div className='img-content'>
{/* 如果视频和封面都没有上传的话, 那么就显示缺省, 如果上传了视频, 那么封面图就默认显示视频的第一帧, 如果上传了封面图, 那么就显示上传的封面图 */}
{
(scheduleVideoUrl || coverUrl )?
<img src={coverUrl || `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`} /> :
<div className="empty-img">若不上传<br />系统默认将视频首帧作为封面图</div>
}
{scheduleVideoUrl || coverUrl ? (
<img src={coverUrl || `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`} />
) : (
<div className='empty-img'>
若不上传
<br />
系统默认将视频首帧作为封面图
</div>
)}
</div>
<div className="opt-btns">
<Button onClick={() => {this.setState({ showSelectCoverModal:true })}}>{`${(pageType === 'add' && (!scheduleVideoId && !coverUrl)) ? '上传' : '修改'}封面`}</Button>
<div className="tips">建议尺寸1280*720px或16:9。封面图最大5M,支持jpg、jpeg和png。</div>
<div className='opt-btns'>
<Button
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 className="course-catalog required">
<span className="label">课程分类:</span>
{ (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>}/>
<div className='course-catalog required'>
<span className='label'>课程分类:</span>
{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>
}
{ (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 className="intro-info mt16">
<div className='intro-info mt16'>
<AddVideoIntro
data={{
id,
......@@ -642,7 +676,7 @@ class AddVideoCourse extends React.Component {
shelfState,
whetherVisitorsJoin,
introduce,
loadintroduce,
loadintroduce
}}
onChange={this.handleChangeForm}
/>
......@@ -650,21 +684,23 @@ class AddVideoCourse extends React.Component {
</div>
</div>
<div className="footer">
<div className='footer'>
<Button onClick={this.handleGoBack}>取消</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>
{/* 选择备课文件弹窗 */}
{ showSelectFileModal &&
{showSelectFileModal && (
<SelectPrepareFileModal
operateType="select"
operateType='select'
selectTypeList={['MP4']}
accept="video/mp4"
accept='video/mp4'
confirm={{
title: '文件过大,无法上传',
content: '为保障学员的观看体验,上传的视频大小不能超过2G',
content: '为保障学员的观看体验,上传的视频大小不能超过2G'
}}
tooltip={'格式支持mp4,大小不超过2G'}
isOpen={showSelectFileModal}
......@@ -675,13 +711,13 @@ class AddVideoCourse extends React.Component {
}}
onSelect={this.handleSelectVideo}
/>
}
{showSelectCoverModal &&
)}
{showSelectCoverModal && (
<SelectPrepareFileModal
key="basic"
operateType="select"
key='basic'
operateType='select'
multiple={false}
accept="image/jpeg,image/png,image/jpg"
accept='image/jpeg,image/png,image/jpg'
selectTypeList={['JPG', 'JPEG', 'PNG']}
tooltip='支持文件类型:jpg、jpeg、png'
isOpen={showSelectCoverModal}
......@@ -690,70 +726,66 @@ class AddVideoCourse extends React.Component {
}}
onSelect={this.handleSelectCover}
/>
}
)}
<Modal
title="设置图片"
title='设置图片'
width={1080}
visible={visible}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
onCancel={() => {
this.setState({ visible: false });
this.setState({ visible: false })
}}
zIndex={10001}
footer={[
<Button
key="back"
key='back'
onClick={() => {
this.setState({ visible: false });
}}
>
this.setState({ visible: false })
}}>
重新上传
</Button>,
<Button
key="submit"
type="primary"
key='submit'
type='primary'
disabled={!hasImgReady}
onClick={() => {
if (!cutFlag) {
cutFlag = true;
this.refs.hiddenBtn.click();
cutFlag = true
this.refs.hiddenBtn.click()
}
this.getSignature(cutImageBlob);
}}
>
this.getSignature(cutImageBlob)
}}>
确定
</Button>,
]}
>
<div className="clip-box">
</Button>
]}>
<div className='clip-box'>
<div
id="headPicModal"
ref="headPicModal"
id='headPicModal'
ref='headPicModal'
style={{
width: "500px",
height: "430px",
marginBottom: 0,
}}
></div>
<div id="clipBtn" style={{ display: "none" }} ref="hiddenBtn"></div>
<div className="preview-img">
<div className="title">效果预览</div>
<div id="preview-url-box" style={{width:500,height:282}}>
<img src={this.state.dataUrl} style={{ width: '100%' }} alt="" />
width: '500px',
height: '430px',
marginBottom: 0
}}></div>
<div id='clipBtn' style={{ display: 'none' }} ref='hiddenBtn'></div>
<div className='preview-img'>
<div className='title'>效果预览</div>
<div id='preview-url-box' style={{ width: 500, height: 282 }}>
<img src={this.state.dataUrl} style={{ width: '100%' }} alt='' />
</div>
<div className="tip-box">
<div className="tip">温馨提示</div>
<div className="tip">①预览效果图时可能存在延迟,单击左侧图片刷新即可</div>
<div className="tip">②设置图片时双击可旋转图片,滚动可放大或缩小图片</div>
<div className='tip-box'>
<div className='tip'>温馨提示</div>
<div className='tip'>①预览效果图时可能存在延迟,单击左侧图片刷新即可</div>
<div className='tip'>②设置图片时双击可旋转图片,滚动可放大或缩小图片</div>
</div>
</div>
</div>
</Modal>
{ this.state.previewCourseModal }
{this.state.previewCourseModal}
</div>
)
}
}
export default AddVideoCourse;
export default AddVideoCourse
/*
* @Author: 吴文洁
* @Date: 2020-08-05 10:11:57
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-02-23 16:35:37
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-25 10:53:11
* @Description: 视频课-搜索模块
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
import { Row, Input, Select,Tooltip } from 'antd';
import React from 'react'
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';
import moment from 'moment';
import StoreService from "@/domains/store-domain/storeService";
const { Search } = Input;
const { Option } = Select;
const { Search } = Input
const { Option } = Select
const DEFAULT_QUERY = {
courseName: null, // 课程名称
operatorId: null, // 创建人
beginTime: null, // 开始日期
endTime: null, // 结束日期
shelfState:null,
shelfState: null,
categoryId: null
}
const defaultTeacherQuery = {
size: 10,
current: 1,
nickName:null
nickName: null
}
class VideoCourseFilter extends React.Component {
constructor(props) {
super(props);
super(props)
this.state = {
query: { ...DEFAULT_QUERY }, // 使用扩展运算符,避免浅拷贝
teacherQuery: defaultTeacherQuery,
teacherList:[],
expandFilter:false
teacherList: [],
expandFilter: false,
categoryList: []
}
}
shouldComponentUpdate(nextProps) {
if (nextProps.currentTabKey !== this.props.currentTabKey) {
this.handleReset()
return false
}
return true
}
componentDidMount() {
this.getTeacherList();
this.getTeacherList()
this.queryCategoryTree()
}
// 查询分类树
queryCategoryTree = (categoryName) => {
let query = {
bizType: 'QUESTION',
source: 2,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
count: false
}
AidToolService.queryExternalCategoryTree(query).then((res) => {
const { categoryList = [] } = res.result
this.setState({
categoryList: this.renderTreeNodes(categoryList)
})
})
}
getTeacherList(current = 1, selectList){
const { teacherQuery,teacherList} = this.state;
renderTreeNodes = (data) => {
let newTreeData = data.map((item) => {
item.title = (
<span>
<span className='icon iconfont' style={{ color: '#FBD140' }}>
&#xe7f1;&nbsp;
</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 = {
...teacherQuery,
current,
size:10
};
StoreService.getStoreUserBasicPage( _query).then((res) => {
const { result = {} } = res;
const { records = [], total = 0, hasNext } = result;
const list = current > 1 ? teacherList.concat(records) : records;
size: 10
}
StoreService.getStoreUserBasicPage(_query).then((res) => {
const { result = {} } = res
const { records = [], hasNext } = result
const list = current > 1 ? teacherList.concat(records) : records
this.setState({
hasNext,
teacherList: list,
teacherList: list
})
})
});
}
// 滑动加载更多讲师列表
handleScrollTeacherList = (e) => {
const { hasNext } = this.state;
const container = e.target;
const scrollToBottom = container && container.scrollHeight <= container.clientHeight + container.scrollTop;
const { hasNext } = this.state
const container = e.target
const scrollToBottom = container && container.scrollHeight <= container.clientHeight + container.scrollTop
if (scrollToBottom && hasNext) {
const { teacherQuery } = this.state;
let _teacherQuery = teacherQuery;
const { teacherQuery } = this.state
let _teacherQuery = teacherQuery
_teacherQuery.current = _teacherQuery.current + 1
this.setState({
teacherQuery:{..._teacherQuery}
},()=>{this.getTeacherList(_teacherQuery.current)})
this.setState(
{
teacherQuery: { ..._teacherQuery }
},
() => {
this.getTeacherList(_teacherQuery.current)
}
)
}
}
// 改变搜索条件
handleChangeQuery = (field, value) => {
this.setState({
this.setState(
{
query: {
...this.state.query,
[field]: value,
current: 1,
current: 1
}
}, () => {
if (field === 'courseName') return;
},
() => {
if (field === 'courseName') return
this.props.onChange(this.state.query)
});
}
)
}
handleChangeDates = (dates) => {
const query = _.clone(this.state.query);
const query = _.clone(this.state.query)
if (_.isEmpty(dates)) {
delete query.beginTime;
delete query.endTime;
delete query.beginTime
delete query.endTime
} else {
query.beginTime = dates[0].valueOf();
query.endTime = dates[1].valueOf();
query.beginTime = dates[0].valueOf()
query.endTime = dates[1].valueOf()
}
this.setState({
query:{
this.setState(
{
query: {
...query,
current: 1,
current: 1
}
}, () => {
this.props.onChange(this.state.query);
})
},
() => {
this.props.onChange(this.state.query)
}
)
}
// 重置搜索条件
handleReset = () => {
this.setState({
query: DEFAULT_QUERY,
}, () => {
this.props.onChange(this.state.query);
})
this.setState(
{
query: DEFAULT_QUERY
},
() => {
this.props.onChange(this.state.query)
}
)
}
render() {
const { currentTabKey } = this.props
const {
query: {
courseName,
operator,
beginTime,
endTime,
operatorId,
shelfState
},
query: { courseName, beginTime, endTime, operatorId, shelfState },
expandFilter,
teacherList,
teacherQuery
} = this.state;
teacherQuery,
categoryList
} = this.state
return (
<div className="video-course-filter">
<Row type="flex" justify="space-between" align="top">
<div className="search-condition">
<div className="search-condition__item">
<span className="search-name">视频课名称:</span>
<div className='video-course-filter'>
<Row type='flex' justify='space-between' align='top'>
<div className='search-condition'>
<div className='search-condition__item'>
<span className='search-name'>视频课名称:</span>
<Search
value={courseName}
placeholder="搜索视频课名称"
onChange={(e) => { this.handleChangeQuery('courseName', e.target.value)}}
onSearch={ () => { this.props.onChange(this.state.query) } }
style={{ width: "calc(100% - 84px)" }}
enterButton={<span className="icon iconfont">&#xe832;</span>}
placeholder='搜索视频课名称'
onChange={(e) => {
this.handleChangeQuery('courseName', e.target.value)
}}
onSearch={() => {
this.props.onChange(this.state.query)
}}
style={{ width: 'calc(100% - 84px)' }}
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/>
</div>
<div className="search-condition__item">
<Choose>
<When condition={currentTabKey === 'internal'}>
<div className='search-condition__item'>
<span>创建人:</span>
<Select
placeholder="请选择创建人"
style={{width:"calc(100% - 70px)"}}
placeholder='请选择创建人'
style={{ width: 'calc(100% - 70px)' }}
showSearch
allowClear
filterOption={(input, option) => option}
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}
onChange={(value) => {
this.handleChangeQuery('operatorId', value)
}}
onSearch={(value) => {
teacherQuery.nickName = value
this.setState({
this.setState(
{
teacherQuery
}, () => {
},
() => {
this.getTeacherList()
})
}
)
}}
onClear ={(value)=>{
this.setState({
teacherQuery:{
onClear={(value) => {
this.setState(
{
teacherQuery: {
size: 10,
current: 1,
nickName:null
nickName: null
}
}, () => {
},
() => {
this.getTeacherList()
})
}
}
>
)
}}>
{_.map(teacherList, (item, index) => {
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>
</div>
</When>
<Otherwise>
<div className='search-condition__item'>
<span className='shelf-status'>课程分类:</span>
<TreeSelect
// value={}
style={{ width: 'calc(100% - 75px)' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={categoryList}
placeholder='请选择课程类型'
allowClear
onChange={(value) => {
this.handleChangeQuery('categoryId', value)
}}
/>
</div>
</Otherwise>
</Choose>
<div className="search-condition__item">
<span className="search-date">创建日期:</span>
<div className='search-condition__item'>
<span className='search-date'>创建日期:</span>
<RangePicker
id="course_date_picker"
id='course_date_picker'
allowClear={false}
value={ beginTime ? [moment(beginTime), moment(endTime)] : null }
format={"YYYY-MM-DD"}
onChange={(dates) => { this.handleChangeDates(dates) }}
style={{ width: "calc(100% - 70px)" }}
value={beginTime ? [moment(beginTime), moment(endTime)] : null}
format={'YYYY-MM-DD'}
onChange={(dates) => {
this.handleChangeDates(dates)
}}
style={{ width: 'calc(100% - 70px)' }}
/>
</div>
{ expandFilter &&
<div className="search-condition__item">
<span className="shelf-status">学院展示:</span>
<If condition={expandFilter}>
<div className='search-condition__item'>
<span className='shelf-status'>学院展示:</span>
<Select
style={{ width: "calc(100% - 84px)" }}
placeholder="请选择"
style={{ width: 'calc(100% - 84px)' }}
placeholder='请选择'
allowClear={true}
value={shelfState}
onChange={(value) => { this.handleChangeQuery('shelfState', value) }}
suffixIcon={<span className="icon iconfont" style={{fontSize:'12px',color:'#BFBFBF'}}>&#xe835;</span>}
>
<Option value="YES">开启</Option>
<Option value="NO">关闭</Option>
onChange={(value) => {
this.handleChangeQuery('shelfState', value)
}}
suffixIcon={
<span className='icon iconfont' style={{ fontSize: '12px', color: '#BFBFBF' }}>
&#xe835;
</span>
}>
<Option value='YES'>开启</Option>
<Option value='NO'>关闭</Option>
</Select>
</div>
}
</If>
</div>
<div className="reset-fold-area">
<Tooltip title="清空筛选"><span className="resetBtn iconfont icon" onClick={this.handleReset}>&#xe61b; </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 className='reset-fold-area'>
<Tooltip title='清空筛选'>
<span className='resetBtn iconfont icon' onClick={this.handleReset}>
&#xe61b;{' '}
</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>
</Row>
</div>
......@@ -232,4 +351,4 @@ class VideoCourseFilter extends React.Component {
}
}
export default VideoCourseFilter;
export default VideoCourseFilter
/*
* @Author: 吴文洁
* @Date: 2020-08-05 10:12:45
<<<<<<< HEAD
* @LastEditors: wufan
* @LastEditTime: 2021-05-13 16:34:11
=======
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-23 02:14:30
>>>>>>> 4ed93ca (feat:新增视频课外部视频模块相关功能,准备开始冒烟测试)
* @Description: 视频课-列表模块
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
import { Table, Modal, message , Tooltip,Switch,Dropdown} from 'antd';
import React from 'react'
import { Table, Modal, message, Tooltip, Switch, Dropdown } from 'antd'
import { PageControl } from "@/components";
import { LIVE_SHARE_MAP } from '@/common/constants/academic/cloudClass';
import { appId, shareUrl, LIVE_SHARE } from '@/domains/course-domain/constants';
import { PageControl } from '@/components'
import { LIVE_SHARE } from '@/domains/course-domain/constants'
import ShareLiveModal from '@/modules/course-manage/modal/ShareLiveModal';
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 CourseService from '@/domains/course-domain/CourseService'
import RelatedPlanModal from '../../modal/RelatedPlanModal'
import User from '@/common/js/user'
import './VideoCourseList.less'
import './VideoCourseList.less';
const ENV = process.env.DEPLOY_ENV || 'dev';
const userRole = User.getUserRole();
class VideoCourseList extends React.Component {
constructor(props) {
super(props);
super(props)
this.state = {
id: '', // 视频课ID
studentIds:[],
RelatedPlanModalVisible:false,
selectPlanList:{}
studentIds: [],
RelatedPlanModalVisible: false,
selectPlanList: {}
}
}
componentDidMount() {
const videoCourseItem = localStorage.getItem('videoCourseItem');
const videoCourseItem = localStorage.getItem('videoCourseItem')
if (videoCourseItem) {
const _videoCourseItem = JSON.parse(videoCourseItem);
this.handleShowShareModal(_videoCourseItem, true);
const _videoCourseItem = JSON.parse(videoCourseItem)
this.handleShowShareModal(_videoCourseItem, true)
}
}
// 观看数据弹窗
handleShowWatchDataModal = (record) => {
console.log('111');
console.log("record",record);
console.log('111')
console.log('record', record)
const watchDataModal = (
<WatchDataModal
type='videoCourseList'
......@@ -56,35 +55,39 @@ class VideoCourseList extends React.Component {
close={() => {
this.setState({
watchDataModal: null
});
})
}}
/>
);
this.setState({ watchDataModal });
)
this.setState({ watchDataModal })
}
// 请求表头
parseColumns = () => {
const { type } = this.props
const columns = [
{
title: '视频课',
key: 'scheduleName',
dataIndex: 'scheduleName',
width:321,
width: 321,
fixed: 'left',
render: (val, record) => {
const { coverUrl, scheduleVideoUrl } = record;
const { coverUrl, scheduleVideoUrl } = record
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`} />
{ record.courseName.length > 25?
<img className='course-cover' src={coverUrl || `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`} alt='' />
<Choose>
<When condition={record.courseName.length > 25}>
<Tooltip title={record.courseName}>
<div className="course-name">{record.courseName}</div>
<div className='course-name'>{record.courseName}</div>
</Tooltip>
:
<div className="course-name">{record.courseName}</div>
}
</When>
<Otherwise>
<div className='course-name'>{record.courseName}</div>
</Otherwise>
</Choose>
</div>
)
}
......@@ -96,9 +99,17 @@ class VideoCourseList extends React.Component {
width: 200,
render: (val, record) => {
return (
<div className="record__item">
{record.categoryOneName}{ record.categoryTwoName?`-${record.categoryTwoName}`:''}
<Choose>
<When condition={type === 'internal'}>
<div className='record__item'>
{record.categoryOneName}
{record.categoryTwoName ? `-${record.categoryTwoName}` : ''}
</div>
</When>
<Otherwise>
<div className='record__item'>{record.categoryName}</div>
</Otherwise>
</Choose>
)
}
},
......@@ -110,42 +121,58 @@ class VideoCourseList extends React.Component {
render: (val) => {
return (
<div>
{ val &&
{val && (
<Tooltip title={val}>
<div>
{val.length > 4 ? `${val.slice(0,4)}...` : val}
</div>
<div>{val.length > 4 ? `${val.slice(0, 4)}...` : val}</div>
</Tooltip>
}
)}
</div>
)
}
},
{
<<<<<<< HEAD
title: <span>
<span>学院展示</span>
<Tooltip title={<div>开启后,学员可在学院内查看到此课程。<br/>关闭后,学院内不再展示此课程,但学员仍可通过分享的海报/链接查看此课程。</div>}><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px'}}>&#xe61d;</i></Tooltip>
</span>,
=======
title: (
<span>
<span>学院展示</span>
<Tooltip
title={
<div>
开启后,用户可在学院内查看到此课程。若课程“未成功开课”,则系统会自动“关闭”学院展示。
<br />
关闭后,学院内不再展示此课程,但用户仍可通过分享的海报/链接查看此课程。
</div>
}>
<i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px' }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
>>>>>>> 4ed93ca (feat:新增视频课外部视频模块相关功能,准备开始冒烟测试)
width: 120,
dataIndex: "courseware",
dataIndex: 'courseware',
render: (val, item, index) => {
return (
<Switch defaultChecked={item.shelfState==="YES"?true:false} onChange={()=>this.changeShelfState(item)}/>
)
},
return <Switch defaultChecked={item.shelfState === 'YES' ? true : false} onChange={() => this.changeShelfState(item)} />
}
},
{
<<<<<<< HEAD
title: "观看学员数",
=======
title: '观看用户数',
>>>>>>> 4ed93ca (feat:新增视频课外部视频模块相关功能,准备开始冒烟测试)
width: 110,
key: "watchUserCount",
dataIndex: "watchUserCount",
key: 'watchUserCount',
dataIndex: 'watchUserCount',
render: (val, item) => {
return (
<div className="watchUserCount">{val}</div>
)
},
return <div className='watchUserCount'>{val}</div>
}
},
{
title: '创建时间',
......@@ -170,21 +197,27 @@ class VideoCourseList extends React.Component {
{
title: '关联项',
width: 200,
key: "planList",
dataIndex: "planList",
key: 'planList',
dataIndex: 'planList',
render: (val, record) => {
return (
<div className="related-task">
{ record.relatedPlanList ?
<Tooltip title={this.handlePlanName(record.relatedPlanList)} placement="top" arrowPointAtCenter>
{ record.relatedPlanList.map((item,index)=>{
return <span>{item.planName} { (index < record.relatedPlanList.length-1)&&(<span></span>)} </span>
})
}
<div className='related-task'>
<Choose>
<When condition={record.relatedPlanList}>
<Tooltip title={this.handlePlanName(record.relatedPlanList)} placement='top' arrowPointAtCenter>
{record.relatedPlanList.map((item, index) => {
return (
<span>
{item.planName} {index < record.relatedPlanList.length - 1 && <span></span>}
</span>
)
})}
</Tooltip>
:
</When>
<Otherwise>
<span></span>
}
</Otherwise>
</Choose>
</div>
)
}
......@@ -197,18 +230,19 @@ class VideoCourseList extends React.Component {
fixed: 'right',
render: (val, record) => {
return (
<div className="operate">
<div className="operate__item" onClick={()=>this.handleShowWatchDataModal(record)}>观看数据</div>
<span className="operate__item split"> | </span>
<div className="operate__item" onClick={() => this.handleShowShareModal(record)}>分享</div>
<span className="operate__item split"> | </span>
<div className='operate'>
<div className='operate__item' onClick={() => this.handleShowWatchDataModal(record)}>
观看数据
</div>
<span className='operate__item split'> | </span>
<div className='operate__item' onClick={() => this.handleShowShareModal(record)}>
分享
</div>
<span className='operate__item split'> | </span>
<Dropdown overlay={this.renderMoreOperate(record)}>
<span className="more-operate">
<span className="operate-text">更多</span>
<span
className="iconfont icon"
style={{ color: "#5289FA" }}
>
<span className='more-operate'>
<span className='operate-text'>更多</span>
<span className='iconfont icon' style={{ color: '#5289FA' }}>
&#xe824;
</span>
</span>
......@@ -217,64 +251,68 @@ class VideoCourseList extends React.Component {
)
}
}
];
return columns;
]
type !== 'internal' && columns.splice(2, 1)
return columns
}
renderMoreOperate = (item) => {
const { type } = this.props
return (
<div className="live-course-more-menu">
{ (User.getUserRole() === "CloudManager" || User.getUserRole() === "StoreManager") &&
<div
className="operate__item"
onClick={()=>this.handleRelatedModalShow(item)}
>关联培训计划</div>
}
<div className='live-course-more-menu'>
{(User.getUserRole() === 'CloudManager' || User.getUserRole() === 'StoreManager') && (
<div className='operate__item' onClick={() => this.handleRelatedModalShow(item)}>
关联培训计划
</div>
)}
<If condition={type === 'internal'}>
<div
className="operate__item"
className='operate__item'
onClick={() => {
RCHistory.push(`/create-video-course?type=edit&id=${item.id}`);
}}
>编辑</div>
<div
className="operate__item"
onClick={() => this.handleDeleteVideoCourse(item.id)}
>删除</div>
Window.RCHistory.push(`/create-video-course?type=edit&id=${item.id}`)
}}>
编辑
</div>
<div className='operate__item' onClick={() => this.handleDeleteVideoCourse(item.id)}>
删除
</div>
</If>
</div>
)
}
handlePlanName = (planArray)=>{
let planStr = "";
planArray.map((item,index)=>{
if(index < planArray.length-1){
planStr = planStr + item.planName + '、';
}else{
handlePlanName = (planArray) => {
let planStr = ''
planArray.map((item, index) => {
if (index < planArray.length - 1) {
planStr = planStr + item.planName + '、'
} else {
planStr = planStr + item.planName
}
})
return planStr
}
//改变上架状态
changeShelfState = (item) =>{
changeShelfState = (item) => {
let _shelfState = item.shelfState
if(_shelfState==='NO'){
_shelfState = "YES";
item.shelfState = "YES"
}else{
_shelfState = "NO"
item.shelfState = "NO"
}
const params={
if (_shelfState === 'NO') {
_shelfState = 'YES'
item.shelfState = 'YES'
} else {
_shelfState = 'NO'
item.shelfState = 'NO'
}
const params = {
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((res) => {
if (res.success) {
if (_shelfState === 'YES') {
message.success('已开启展示')
} else {
message.success('已取消展示')
}
}
})
......@@ -285,140 +323,146 @@ class VideoCourseList extends React.Component {
Modal.confirm({
title: '你确定要删除此视频课吗?',
content: '删除后,学员将不能进行观看。',
icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>,
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
okText: '确定',
okType: 'danger',
cancelText: '取消',
onOk: () => {
const param ={
courseId:scheduleId,
storeId:User.getStoreId()
}
CourseService.delVideoSchedule(
param
).then(() => {
message.success('删除成功');
this.props.onChange();
const param = {
courseId: scheduleId,
storeId: User.getStoreId()
}
CourseService.delVideoSchedule(param).then(() => {
message.success('删除成功')
this.props.onChange()
})
}
});
})
}
// 显示分享弹窗
handleShowShareModal = (record, needStr = false) => {
const { id, scheduleVideoUrl } = record;
const _appId = appId;
const htmlUrl = `${LIVE_SHARE}video_detail/${id}?id=${User.getStoreId()}`;
const longUrl = htmlUrl;
const { coverUrl, courseName } = record;
const { id, scheduleVideoUrl } = record
const htmlUrl = `${LIVE_SHARE}video_detail/${id}?id=${User.getStoreId()}`
const longUrl = htmlUrl
const { coverUrl, courseName } = record
const shareData = {
longUrl,
coverUrl,
scheduleVideoUrl,
courseName,
};
courseName
}
const shareLiveModal = (
<ShareLiveModal
needStr={needStr}
data={shareData}
type="videoClass"
title="视频课"
type='videoClass'
title='视频课'
close={() => {
this.setState({
shareLiveModal: null
});
localStorage.setItem('videoCourseItem', '');
})
localStorage.setItem('videoCourseItem', '')
}}
/>
);
)
this.setState({ shareLiveModal });
this.setState({ shareLiveModal })
}
handleChangeTable = (pagination, filters, sorter) => {
const { columnKey, order } = sorter;
const { query } = this.props;
let { order: _order } =query;
const { columnKey, order } = sorter
const { query } = this.props
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 = {
...query,
orderEnum: _order
};
this.props.onChange(_query);
}
handleRelatedModalShow = (item)=>{
const selectPlanList = {};
if(item.relatedPlanList){
item.relatedPlanList.map((item,index)=>{
this.props.onChange(_query)
}
handleRelatedModalShow = (item) => {
const selectPlanList = {}
if (item.relatedPlanList) {
item.relatedPlanList.map((item, index) => {
selectPlanList[item.planId] = {}
selectPlanList[item.planId].planId = item.planId;
selectPlanList[item.planId].taskBaseVOList = [{taskId:item.taskId}];
selectPlanList[item.planId].planId = item.planId
selectPlanList[item.planId].taskBaseVOList = [{ taskId: item.taskId }]
return item
})
}
this.setState({
RelatedPlanModalVisible:true,
selectCourseId:item.id,
selectPlanList:selectPlanList
RelatedPlanModalVisible: true,
selectCourseId: item.id,
selectPlanList: selectPlanList
})
}
closeRelatedPlanModalVisible = ()=>{
closeRelatedPlanModalVisible = () => {
this.setState({
RelatedPlanModalVisible:false
RelatedPlanModalVisible: false
})
}
onChangeSelectPlanList = (selectPlanList)=>{
onChangeSelectPlanList = (selectPlanList) => {
this.setState({
selectPlanList:selectPlanList
selectPlanList: selectPlanList
})
}
onConfirmSelectPlanList = ()=>{
this.setState({
RelatedPlanModalVisible:false
},()=>{this.props.onChange();})
onConfirmSelectPlanList = () => {
this.setState(
{
RelatedPlanModalVisible: false
},
() => {
this.props.onChange()
}
)
}
render() {
const { dataSource = [], totalCount, query } = this.props;
const { current, size } = query;
const {RelatedPlanModalVisible,selectPlanList,selectCourseId} = this.state;
const { dataSource = [], totalCount, query } = this.props
const { current, size } = query
const { RelatedPlanModalVisible, selectPlanList, selectCourseId } = this.state
return (
<div className="video-course-list">
<div className='video-course-list'>
<Table
rowKey={record => record.id}
rowKey={(record) => record.id}
dataSource={dataSource}
columns={this.parseColumns()}
onChange={this.handleChangeTable}
pagination={false}
scroll={{ x: 1500}}
scroll={{ x: 1500 }}
bordered
className="video-list-table"
className='video-list-table'
/>
<div className="box-footer">
<div className='box-footer'>
<PageControl
current={current - 1}
pageSize={size}
total={totalCount}
toPage={(page) => {
const _query = {...query, current: page + 1};
const _query = { ...query, current: page + 1 }
this.props.onChange(_query)
}}
/>
</div>
{ RelatedPlanModalVisible &&
{RelatedPlanModalVisible && (
<RelatedPlanModal
onClose={this.closeRelatedPlanModalVisible}
visible={RelatedPlanModalVisible}
......@@ -427,12 +471,12 @@ class VideoCourseList extends React.Component {
onChange={this.onChangeSelectPlanList}
onConfirm={this.onConfirmSelectPlanList}
/>
}
{ this.state.shareLiveModal }
{ this.state.watchDataModal }
)}
{this.state.shareLiveModal}
{this.state.watchDataModal}
</div>
)
}
}
export default VideoCourseList;
export default VideoCourseList
.video-course-list {
margin-top: 12px;
.video-list-table{
.video-list-table {
tbody {
tr{
&:nth-child(even){
tr {
&:nth-child(even) {
background: transparent !important;
td{
background:#FFF !important;
td {
background: #fff !important;
}
}
&:nth-child(odd){
background: #FAFAFA !important;
td{
background: #FAFAFA !important;
&:nth-child(odd) {
background: #fafafa !important;
td {
background: #fafafa !important;
}
}
&:hover{
td{
background:#F3f6fa !important;
&:hover {
td {
background: #f3f6fa !important;
}
}
}
}
}
.watchUserCount{
text-align:right;
padding:16px;
.watchUserCount {
text-align: right;
padding: 16px;
}
.operate-text {
color: #5289FA;
color: #5289fa;
cursor: pointer;
}
.operate {
display: flex;
&__item {
color: #5289FA;
color: #5289fa;
cursor: pointer;
&.split {
margin: 0 8px;
color: #BFBFBF;
color: #bfbfbf;
}
}
}
.more-operate{
line-height:20px;
.more-operate {
line-height: 20px;
}
.record__item {
display: flex;
......@@ -61,16 +61,16 @@
.course-name {
color: #666;
width:188px;
width: 188px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
height:48px;
height: 48px;
}
}
.related-task{
.related-task {
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
......
/*
* @Author: 吴文洁
* @Date: 2020-08-05 10:08:06
* @LastEditors: zhangleyuan
* @LastEditTime: 2020-12-26 15:56:49
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-25 12:00:56
* @Description: 云课堂-视频课入口页面
* @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 React from 'react'
import { Tabs } from 'antd'
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'
const { TabPane } = Tabs
class VideoCourse extends React.Component {
constructor(props) {
super(props);
super(props)
this.state = {
query: {
size: 10,
current: 1,
storeId:User.getStoreId()
storeId: User.getStoreId()
},
dataSource: [], // 视频课列表
totalCount: 0, // 视频课数据总条数
currentTabKey: 'internal'
}
}
componentWillMount() {
// 获取视频课列表
this.handleFetchScheduleList();
this.handleFetchScheduleList()
}
// 获取视频课列表
handleFetchScheduleList = (_query = {}) => {
const { currentTabKey } = this.state
const query = {
...this.state.query,
..._query
};
..._query,
courseDivision: currentTabKey === 'external' ? 1 : null
}
// 更新请求参数
this.setState({ query });
this.setState({ query })
CourseService.videoSchedulePage(query).then((res) => {
const { result = {} } = res || {};
const { records = [], total = 0 } = result;
const { result = {} } = res || {}
const { records = [], total = 0 } = result
this.setState({
dataSource: records,
totalCount: Number(total)
});
});
})
})
}
currenTabChange = (currentTabKey) => {
this.setState(
{
currentTabKey
},
() => {
this.handleFetchScheduleList()
}
)
}
render() {
const { dataSource, totalCount, query } = this.state;
const { dataSource, totalCount, query, currentTabKey } = this.state
return (
<div className="page video-course-page">
<div className="content-header">视频课</div>
<div className='page video-course-page'>
<div className='content-header'>视频课</div>
<div className="box">
{/* 搜索模块 */}
<VideoCourseFilter
onChange={this.handleFetchScheduleList}
/>
<div className='box'>
<Tabs onChange={this.currenTabChange} activeKey={currentTabKey}>
<TabPane key='internal' tab='内部课程'></TabPane>
<TabPane key='external' tab='外部课程'></TabPane>
</Tabs>
{/* 搜索模块 */}
<VideoCourseFilter currentTabKey={currentTabKey} onChange={this.handleFetchScheduleList} />
{/* 操作模块 */}
<If condition={currentTabKey === 'internal'}>
<VideoCourseOpt />
</If>
{/* 视频课列表模块 */}
<VideoCourseList
query={query}
dataSource={dataSource}
totalCount={totalCount}
onChange={this.handleFetchScheduleList}
/>
<VideoCourseList type={currentTabKey} query={query} dataSource={dataSource} totalCount={totalCount} onChange={this.handleFetchScheduleList} />
</div>
</div>
)
}
}
export default VideoCourse;
export default VideoCourse
......@@ -2,243 +2,293 @@
* @Description:
* @Author: zangsuyun
* @Date: 2021-03-13 09:54:26
* @LastEditors: zangsuyun
* @LastEditTime: 2021-04-07 11:56:10
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-25 11:49:22
* @Copyright: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React from "react";
import {
Row,
Modal,
Button,
message,
Table,
Select,
Radio,
Input,
Tabs,
Tooltip,
TreeSelect,
} from "antd";
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;
import React from 'react'
import { Row, Modal, Button, message, Radio, Table, Input, Tabs, Tooltip, TreeSelect } from 'antd'
import { PageControl } from '@/components'
import TableSelectedData from '@/components/TableSelectedData'
import KnowledgeAPI from '@/data-source/knowledge/request-api'
import AidToolService from '@/domains/aid-tool-domain/AidToolService'
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 = {
UN_START: {
code: 1,
title: "待开课",
color: "#FFB714",
title: '待开课',
color: '#FFB714'
},
STARTING: {
code: 2,
title: "上课中",
color: "#238FFF",
title: '上课中',
color: '#238FFF'
},
FINISH: {
code: 3,
title: "已完成",
color: "#3BBDAA",
title: '已完成',
color: '#3BBDAA'
},
EXPIRED: {
code: 4,
title: "未成功开课",
color: "#999",
},
};
title: '未成功开课',
color: '#999'
}
}
class AddCourse extends React.Component {
constructor(props) {
super(props);
super(props)
this.state = {
liveDataSource: [],
liveSize: 10,
liveQuery: {
current: 1,
excludeUsed: true,
courseType: "LIVE",
courseType: 'LIVE',
storeId: User.getStoreId(),
toRefKnowledgeCategoryId: this.props.categoryId,
toRefKnowledgeCategoryId: this.props.categoryId
},
liveTotalCount: 0,
selectLive: [], //弹窗内已选择的直播课程
videoDataSource: [],
videoSize: 10,
videoCourseDivision: 'internal',
videoDataSource: {
external: [],
internal: []
},
videoSize: {
external: 10,
internal: 10
},
videoSearchDefalt: {
external: {
categoryId: '',
courseName: ''
},
internal: {
categoryId: '',
courseName: ''
}
},
videoQuery: {
external: {
categoryId: '',
courseName: '',
current: 1,
courseType: "VOICE",
courseType: 'VOICE',
excludeUsed: true,
storeId: User.getStoreId(),
toRefKnowledgeCategoryId: this.props.categoryId,
toRefKnowledgeCategoryId: this.props.categoryId
},
videoTotalCount: 0,
selectVideo: [], //弹窗内已选择的视频课程
internal: {
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: [],
pictureSize: 10,
pictureQuery: {
current: 1,
excludeUsed: true,
courseType: "PICTURE",
courseType: 'PICTURE',
storeId: User.getStoreId(),
toRefKnowledgeCategoryId: this.props.categoryId,
toRefKnowledgeCategoryId: this.props.categoryId
},
pictureTotalCount: 0,
selectPicture: [], //弹窗内已选择的图文课程
categoryList: [],
};
categoryList: [], //内部分类列表
categoryListExternal: [] //外部分类列表
}
}
componentDidMount() {
this.handleFetchLiveList();
this.handleFetchVideoList();
this.handleFetchPictureList();
this.queryCategoryTree();
this.handleFetchLiveList()
this.handleFetchVideoList()
this.handleFetchPictureList()
this.queryCategoryTree()
}
// 查询分类树
queryCategoryTree = (categoryName) => {
let query = {
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) => {
const { categoryList = [] } = res.result;
const { categoryList = [] } = res.result
this.setState({
categoryList: this.renderTreeNodes(categoryList),
});
});
};
categoryList: this.renderTreeNodes(categoryList)
})
})
}
renderTreeNodes = (data) => {
let newTreeData = data.map((item) => {
item.title = (
<span>
<span className="icon iconfont" style={{ color: "#FBD140" }}>
<span className='icon iconfont' style={{ color: '#FBD140' }}>
&#xe7f1;&nbsp;
</span>
{item.categoryName}
</span>
);
item.key = item.id;
)
item.key = item.id
if (item.sonCategoryList) {
item.children = this.renderTreeNodes(item.sonCategoryList);
item.children = this.renderTreeNodes(item.sonCategoryList)
}
return item
})
return newTreeData
}
return item;
});
return newTreeData;
};
// 获取直播课列表
handleFetchLiveList = () => {
const { liveQuery, liveSize } = this.state;
const { liveQuery, liveSize } = this.state
const params = {
...liveQuery,
size: liveSize,
};
size: liveSize
}
// CourseService.getLiveCloudCoursePage(params).then((res) => {
KnowledgeAPI.knowledgeLiveCoursePage(params).then((res) => {
const { result = {} } = res;
const { records = [], total = 0 } = result;
const { result = {} } = res
const { records = [], total = 0 } = result
this.setState({
liveDataSource: records,
liveTotalCount: Number(total),
});
});
};
liveTotalCount: Number(total)
})
})
}
// 获取视频课列表
handleFetchVideoList = () => {
const { videoQuery, videoSize } = this.state;
const { videoQuery, videoSize, videoCourseDivision, videoDataSource, videoTotalCount } = this.state
const params = {
...videoQuery,
size: videoSize,
};
...videoQuery[videoCourseDivision],
size: videoSize[videoCourseDivision],
courseDivision: videoCourseDivision === 'internal' ? 'INTERNAL' : 'EXTERNAL'
}
// CourseService.videoSchedulePage(query).then((res) => {
KnowledgeAPI.knowledgeMediaCoursePage(params).then((res) => {
const { result = {} } = res || {};
const { records = [], total = 0 } = result;
const { result = {} } = res || {}
const { records = [], total = 0 } = result
this.setState({
videoDataSource: records,
videoTotalCount: Number(total),
});
});
};
videoDataSource: {
...videoDataSource,
[videoCourseDivision]: records
},
videoTotalCount: {
...videoTotalCount,
[videoCourseDivision]: Number(total)
}
})
})
}
// 获取图文课列表
handleFetchPictureList = () => {
const { pictureQuery, pictureSize } = this.state;
const { pictureQuery, pictureSize } = this.state
const params = {
...pictureQuery,
size: pictureSize,
};
size: pictureSize
}
// CourseService.pictureSchedulePage(query).then((res) => {
KnowledgeAPI.knowledgeMediaCoursePage(params).then((res) => {
const { result = {} } = res || {};
const { records = [], total = 0 } = result;
const { result = {} } = res || {}
const { records = [], total = 0 } = result
this.setState({
pictureDataSource: records,
pictureTotalCount: Number(total),
});
});
};
pictureTotalCount: Number(total)
})
})
}
onShowLiveSizeChange = (current, size) => {
if (current == size) {
return;
if (current === size) {
return
}
this.setState(
{
liveSize: size,
liveSize: size
},
() => {
this.handleFetchLiveList();
this.handleFetchLiveList()
}
)
}
);
};
onShowVideoSizeChange = (current, size) => {
if (current == size) {
return;
if (current === size) {
return
}
this.setState(
{
videoSize: size,
videoSize: size
},
() => {
this.handleFetchVideoList();
this.handleFetchVideoList()
}
)
}
);
};
onShowPictureSizeChange = (current, size) => {
if (current == size) {
return;
if (current === size) {
return
}
this.setState(
{
pictureSize: size,
pictureSize: size
},
() => {
this.handleFetchPictureList();
this.handleFetchPictureList()
}
)
}
);
};
liveColumns = () => {
const columns = [
......@@ -246,102 +296,93 @@ class AddCourse extends React.Component {
title: (
<span>
<span>课程信息</span>
<Tooltip
title={<div>已加入该分类的课程不支持重复选择,因此不显示。</div>}
>
<Tooltip title={<div>已加入该分类的课程不支持重复选择,因此不显示。</div>}>
<i
className="icon iconfont"
className='icon iconfont'
style={{
marginLeft: "5px",
cursor: "pointer",
color: "#bfbfbf",
fontSize: "14px",
}}
>
marginLeft: '5px',
cursor: 'pointer',
color: '#bfbfbf',
fontSize: '14px'
}}>
&#xe61d;
</i>
</Tooltip>
</span>
),
width: 371,
key: "course",
dataIndex: "courseName",
key: 'course',
dataIndex: 'courseName',
render: (val, record) => {
let hasCover = false;
let hasCover = false
return (
<div className="record__item">
{record.courseMediaVOS.map((item, index) => {
if (item.contentType === "COVER") {
hasCover = true;
return <img className="course-cover" src={item.mediaUrl} />;
<div className='record__item'>
{record.courseMediaVOS.map((item) => {
if (item.contentType === 'COVER') {
hasCover = true
return <img className='course-cover' src={item.mediaUrl} alt='' />
}
})}
{!hasCover && (
<img
className="course-cover"
src={"https://image.xiaomaiketang.com/xm/YNfi45JwFA.png"}
/>
)}
{!hasCover && <img className='course-cover' src={'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'} alt='' />}
<div>
{record.courseName.length > 17 ? (
<Choose>
<When condition={record.courseName.length > 17}>
<Tooltip title={record.courseName}>
<div className="course-name">{record.courseName}</div>
<div className='course-name'>{record.courseName}</div>
</Tooltip>
) : (
<div className="course-name">{record.courseName}</div>
)}
</When>
<Otherwise>
<div className='course-name'>{record.courseName}</div>
</Otherwise>
</Choose>
<div>
<span
className="course-status"
className='course-status'
style={{
color: courseStateShow[record.courseState].color,
border: `1px solid ${
courseStateShow[record.courseState].color
}`,
}}
>
border: `1px solid ${courseStateShow[record.courseState].color}`
}}>
{courseStateShow[record.courseState].title}
</span>
</div>
</div>
</div>
);
},
)
}
},
{
title: "上课时间",
title: '上课时间',
width: 110,
key: "couseCatalog",
dataIndex: "couseCatalog",
key: 'couseCatalog',
dataIndex: 'couseCatalog',
render: (val, item) => {
return (
<span className="course-time">
{formatDate("YYYY-MM-DD", parseInt(item.startTime))} <br></br>
{formatDate("H:i", parseInt(item.startTime))}~
{formatDate("H:i", parseInt(item.endTime))}
<span className='course-time'>
{formatDate('YYYY-MM-DD', parseInt(item.startTime))} <br></br>
{formatDate('H:i', parseInt(item.startTime))}~{formatDate('H:i', parseInt(item.endTime))}
</span>
);
},
)
}
},
{
title: "课程分类",
title: '课程分类',
// width: "10%",
key: "couseCatalog",
dataIndex: "couseCatalog",
key: 'couseCatalog',
dataIndex: 'couseCatalog',
render: (val, record) => {
return (
<div className="categoryName">
<div className='categoryName'>
{record.categoryOneName}
{record.categoryTwoName ? `-${record.categoryTwoName}` : ""}
{record.categoryTwoName ? `-${record.categoryTwoName}` : ''}
</div>
);
},
},
];
)
}
}
]
return columns;
};
return columns
}
videoColumns = () => {
const columns = [
......@@ -349,74 +390,69 @@ class AddCourse extends React.Component {
title: (
<span>
<span>课程信息</span>
<Tooltip
title={<div>已加入该分类的课程不支持重复选择,因此不显示。</div>}
>
<Tooltip title={<div>已加入该分类的课程不支持重复选择,因此不显示。</div>}>
<i
className="icon iconfont"
className='icon iconfont'
style={{
marginLeft: "5px",
cursor: "pointer",
color: "#bfbfbf",
fontSize: "14px",
}}
>
marginLeft: '5px',
cursor: 'pointer',
color: '#bfbfbf',
fontSize: '14px'
}}>
&#xe61d;
</i>
</Tooltip>
</span>
),
key: "scheduleName",
dataIndex: "scheduleName",
width: 371,
key: 'scheduleName',
dataIndex: 'scheduleName',
width: 300,
render: (val, record) => {
const { coverUrl, mediaCourseUrl } = record;
const { coverUrl, mediaCourseUrl } = record
return (
<div className="record__item">
<div className='record__item'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
<img
className="course-cover"
src={
coverUrl ||
`${mediaCourseUrl}?x-oss-process=video/snapshot,t_0,m_fast`
}
/>
{record.courseName.length > 25 ? (
<img className='course-cover' src={coverUrl || `${mediaCourseUrl}?x-oss-process=video/snapshot,t_0,m_fast`} alt='' />
<Choose>
<When condition={record.courseName.length > 25}>
<Tooltip title={record.courseName}>
<div className="course-name">{record.courseName}</div>
<div className='course-name'>{record.courseName}</div>
</Tooltip>
) : (
<div className="course-name">{record.courseName}</div>
)}
</When>
<Otherwise>
<div className='course-name'>{record.courseName}</div>
</Otherwise>
</Choose>
</div>
);
},
)
}
},
{
title: "课程时长",
key: "videoDuration",
title: '课程时长',
key: 'videoDuration',
width: 80,
dataIndex: "videoDuration",
dataIndex: 'videoDuration',
render: (text, item) => {
return <span>{text ? dealTimeDuration(text) : "-"}</span>;
},
return <span>{text ? dealTimeDuration(text) : '-'}</span>
}
},
{
title: "课程分类",
key: "categoryName",
dataIndex: "categoryName",
title: '课程分类',
key: 'categoryName',
dataIndex: 'categoryName',
render: (val, record) => {
return (
<div className="record__item">
<div className='record__item'>
{record.categoryOneName}
{record.categoryTwoName ? `-${record.categoryTwoName}` : ""}
{record.categoryTwoName ? `-${record.categoryTwoName}` : ''}
</div>
);
},
},
];
return columns;
};
)
}
}
]
return columns
}
pictureColumns = () => {
const columns = [
......@@ -424,208 +460,228 @@ class AddCourse extends React.Component {
title: (
<span>
<span>课程信息</span>
<Tooltip
title={<div>已加入该分类的课程不支持重复选择,因此不显示。</div>}
>
<Tooltip title={<div>已加入该分类的课程不支持重复选择,因此不显示。</div>}>
<i
className="icon iconfont"
className='icon iconfont'
style={{
marginLeft: "5px",
cursor: "pointer",
color: "#bfbfbf",
fontSize: "14px",
}}
>
marginLeft: '5px',
cursor: 'pointer',
color: '#bfbfbf',
fontSize: '14px'
}}>
&#xe61d;
</i>
</Tooltip>
</span>
),
key: "scheduleName",
dataIndex: "scheduleName",
key: 'scheduleName',
dataIndex: 'scheduleName',
width: 371,
render: (val, record) => {
const { coverUrl } = record;
const { coverUrl } = record
return (
<div className="record__item">
<div className='record__item'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
<img
className="course-cover"
src={
coverUrl ||
"https://image.xiaomaiketang.com/xm/YNfi45JwFA.png"
}
/>
{record.courseName.length > 25 ? (
<img className='course-cover' src={coverUrl || 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'} alt='' />
<Choose>
<When condition={record.courseName.length > 25}>
<Tooltip title={record.courseName}>
<div className="course-name">{record.courseName}</div>
<div className='course-name'>{record.courseName}</div>
</Tooltip>
) : (
<div className="course-name">{record.courseName}</div>
)}
</When>
<Otherwise>
<div className='course-name'>{record.courseName}</div>
</Otherwise>
</Choose>
</div>
);
},
)
}
},
{
title: "课程分类",
key: "categoryName",
dataIndex: "categoryName",
title: '课程分类',
key: 'categoryName',
dataIndex: 'categoryName',
render: (val, record) => {
return (
<div className="record__item">
<div className='record__item'>
{record.categoryOneName}
{record.categoryTwoName ? `-${record.categoryTwoName}` : ""}
{record.categoryTwoName ? `-${record.categoryTwoName}` : ''}
</div>
);
},
},
];
return columns;
};
)
}
}
]
return columns
}
selectLiveList = (record, selected) => {
let { selectLive } = this.state;
let _list = [];
if (
selected ||
!_.find(selectLive, (item) => item.liveCourseId == record.liveCourseId)
) {
_list = _.uniq(
selectLive.concat([record]),
false,
(item) => item.liveCourseId
);
let { selectLive } = this.state
let _list = []
if (selected || !_.find(selectLive, (item) => item.liveCourseId === record.liveCourseId)) {
_list = _.uniq(selectLive.concat([record]), false, (item) => item.liveCourseId)
} else {
_list = _.reject(
selectLive,
(item) => item.liveCourseId === record.liveCourseId
);
_list = _.reject(selectLive, (item) => item.liveCourseId === record.liveCourseId)
}
this.setState({ selectLive: _list })
}
this.setState({ selectLive: _list });
};
selectVideoList = (record, selected) => {
console.log(record);
let { selectVideo } = this.state;
let _list = [];
if (selected || !_.find(selectVideo, (item) => item.id == record.id)) {
_list = _.uniq(selectVideo.concat([record]), false, (item) => item.id);
const { selectVideo, videoCourseDivision } = this.state
let { [videoCourseDivision]: selectList } = selectVideo
let _list = []
if (selected || !_.find(selectList, (item) => item.id === record.id)) {
_list = _.uniq(selectList.concat([record]), false, (item) => item.id)
} 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) => {
console.log(record);
let { selectPicture } = this.state;
let _list = [];
console.log(record)
let { selectPicture } = this.state
let _list = []
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 {
_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) {
console.log(key);
console.log(key)
}
handleChangVideoFilter = (key, value) => {
const { videoQuery } = this.state;
videoQuery[key] = value;
videoQuery.current = 1;
const { videoQuery, videoCourseDivision, videoSearchDefalt } = this.state
videoQuery[videoCourseDivision][key] = value
videoSearchDefalt[videoCourseDivision][key] = value
videoQuery[videoCourseDivision].current = 1
this.setState(
{
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) => {
const { liveQuery } = this.state;
liveQuery[key] = value;
liveQuery.current = 1;
const { liveQuery } = this.state
liveQuery[key] = value
liveQuery.current = 1
this.setState(
{
liveQuery,
liveQuery
},
() => {
this.handleFetchLiveList();
this.handleFetchLiveList()
}
)
}
);
};
handleChangPictureFilter = (key, value) => {
const { pictureQuery } = this.state;
pictureQuery[key] = value;
pictureQuery.current = 1;
const { pictureQuery } = this.state
pictureQuery[key] = value
pictureQuery.current = 1
this.setState(
{
pictureQuery,
pictureQuery
},
() => {
this.handleFetchPictureList();
this.handleFetchPictureList()
}
)
}
);
};
handAddCourse = () => {
const { selectVideo, selectLive, selectPicture } = this.state;
const batchAddList = [];
const { selectVideo, selectLive, selectPicture } = this.state
const batchAddList = []
if (selectVideo.length) {
batchAddList.push({
categoryId: this.props.categoryId,
refIds: _.pluck(selectVideo, "id"),
refIds: _.pluck(selectVideo, 'id'),
storeId: User.getStoreId(),
type: "VOICE",
createId: User.getStoreUserId(),
});
type: 'VOICE',
createId: User.getStoreUserId()
})
}
if (selectLive.length) {
batchAddList.push({
categoryId: this.props.categoryId,
refIds: _.pluck(selectLive, "liveCourseId"),
refIds: _.pluck(selectLive, 'liveCourseId'),
storeId: User.getStoreId(),
type: "LIVE",
createId: User.getStoreUserId(),
});
type: 'LIVE',
createId: User.getStoreUserId()
})
}
if (selectPicture.length) {
batchAddList.push({
categoryId: this.props.categoryId,
refIds: _.pluck(selectPicture, "id"),
refIds: _.pluck(selectPicture, 'id'),
storeId: User.getStoreId(),
type: "PICTURE",
createId: User.getStoreUserId(),
});
type: 'PICTURE',
createId: User.getStoreUserId()
})
}
KnowledgeAPI.addDifTypeKnowledge({ batchAddList }).then(({ success }) => {
if (success) {
message.success("新增成功");
this.props.onClose();
this.props.onChange();
this.props.updateCategoryTree();
message.success('新增成功')
this.props.onClose()
this.props.onChange()
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 = () => {
const {selectVideo,selectPicture,selectLive} =this.state
const { selectVideo, selectPicture, selectLive } = this.state
return (
<div>
<Button onClick={this.props.onClose}>取消</Button>
<Button disabled={!(selectLive.length || selectVideo.length || selectPicture.length)} type="primary" onClick={this.handAddCourse}>
<Button disabled={!(selectLive.length || selectVideo.length || selectPicture.length)} type='primary' onClick={this.handAddCourse}>
确定
</Button>
</div>
);
};
)
}
render() {
const {
......@@ -638,162 +694,151 @@ class AddCourse extends React.Component {
videoSize,
videoQuery,
videoTotalCount,
videoCourseDivision,
selectVideo,
pictureDataSource,
pictureSize,
pictureQuery,
pictureTotalCount,
selectPicture,
videoSearchDefalt,
categoryList,
} = this.state;
categoryListExternal
} = this.state
const LiveSelection = {
selectedRowKeys: _.pluck(selectLive, "liveCourseId"),
selectedRowKeys: _.pluck(selectLive, 'liveCourseId'),
onSelect: this.selectLiveList,
onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = [];
let _list = []
if (selected) {
_list = _.uniq(
selectLive.concat(changeRows),
false,
(item) => item.liveCourseId
);
_list = _.uniq(selectLive.concat(changeRows), false, (item) => item.liveCourseId)
} else {
_list = _.reject(selectLive, (item) =>
_.find(
changeRows,
(data) => data.liveCourseId === item.liveCourseId
)
);
_list = _.reject(selectLive, (item) => _.find(changeRows, (data) => data.liveCourseId === item.liveCourseId))
}
this.setState({ selectLive: _list })
}
}
this.setState({ selectLive: _list });
},
};
const VideoSelection = {
selectedRowKeys: _.pluck(selectVideo, "id"),
selectedRowKeys: _.pluck(selectVideo[videoCourseDivision], 'id'),
onSelect: this.selectVideoList,
onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = [];
let _list = []
if (selected) {
_list = _.uniq(
selectVideo.concat(changeRows),
false,
(item) => item.id
);
_list = _.uniq(selectVideo[videoCourseDivision].concat(changeRows), false, (item) => item.id)
} else {
_list = _.reject(selectVideo, (item) =>
_.find(changeRows, (data) => data.id === item.id)
);
_list = _.reject(selectVideo[videoCourseDivision], (item) => _.find(changeRows, (data) => data.id === item.id))
}
this.setState({
selectVideo: {
...selectVideo,
[videoCourseDivision]: _list
}
})
}
}
this.setState({ selectVideo: _list });
},
};
const PictureSelection = {
selectedRowKeys: _.pluck(selectPicture, "id"),
selectedRowKeys: _.pluck(selectPicture, 'id'),
onSelect: this.selectPictureList,
onSelectAll: (selected, _selectedRows, changeRows) => {
console.log(changeRows);
let _list = [];
console.log(changeRows)
let _list = []
if (selected) {
_list = _.uniq(
selectPicture.concat(changeRows),
false,
(item) => item.id
);
_list = _.uniq(selectPicture.concat(changeRows), false, (item) => item.id)
} else {
_list = _.reject(selectPicture, (item) =>
_.find(changeRows, (data) => data.id === item.id)
);
_list = _.reject(selectPicture, (item) => _.find(changeRows, (data) => data.id === item.id))
}
this.setState({ selectPicture: _list })
}
}
this.setState({ selectPicture: _list });
},
};
return (
<Modal
visible={true}
width={720}
title="新增课程"
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">
<Modal visible={true} width={800} title='新增课程' footer={this.renderFooter()} onCancel={this.props.onClose} className='add-course-modal'>
<Tabs defaultActiveKey='VIDEO'>
<TabPane tab='视频课' key='VIDEO'>
<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>
<Row type="flex" justify="space-between" align="top">
<Row type='flex' justify='space-between' align='top'>
<div>
<span style={{ lineHeight: "32px" }}>课程名称:</span>
<span style={{ lineHeight: '32px' }}>课程名称:</span>
<Search
// value={courseName}
style={{ width: "calc(100% - 75px)" }}
placeholder="搜索课程名称"
value={videoSearchDefalt[videoCourseDivision].courseName}
style={{ width: 'calc(100% - 75px)' }}
placeholder='搜索课程名称'
onChange={this.handleChangVideoCourseName}
onSearch={(value) => {
this.handleChangVideoFilter("courseName", value);
this.handleChangVideoFilter('courseName', value)
}}
enterButton={
<span className="icon iconfont">&#xe832;</span>
}
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/>
</div>
<div style={{ width: "50%" }}>
<span className="shelf-status">课程分类:</span>
<div style={{ width: '50%' }}>
<span className='shelf-status'>课程分类:</span>
<TreeSelect
style={{ width: "calc(100% - 75px)" }}
dropdownStyle={{ maxHeight: 400, overflow: "auto" }}
treeData={categoryList}
placeholder="请选择课程类型"
value={videoSearchDefalt[videoCourseDivision].categoryId}
style={{ width: 'calc(100% - 75px)' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={videoCourseDivision === 'internal' ? categoryList : categoryListExternal}
placeholder='请选择课程类型'
allowClear
onChange={(value) => {
this.handleChangVideoFilter("categoryId", value);
this.handleChangVideoFilter('categoryId', value)
}}
/>
</div>
</Row>
</div>
<TableSelectedData
selectedNum={
selectVideo.length + selectLive.length + selectPicture.length
}
selectedNum={selectVideo.internal.length + selectVideo.external.length + selectLive.length + selectPicture.length}
clearSelectedData={() => {
this.setState({
selectVideo: [],
selectVideo: {
internal: [],
external: []
},
selectLive: [],
selectPicture: [],
});
selectPicture: []
})
}}
/>
<Table
rowKey={(record) => record.id}
dataSource={videoDataSource}
dataSource={videoDataSource[videoCourseDivision]}
columns={this.videoColumns()}
size="middle"
size='middle'
rowSelection={VideoSelection}
pagination={false}
bordered
className="video-list-table"
style={{ maxHeight: 359, overflow: "scroll" }}
className='video-list-table'
style={{ maxHeight: 359, overflow: 'scroll' }}
/>
<div className="box-footer">
{videoTotalCount > 0 && (
<div className='box-footer'>
{videoDataSource[videoCourseDivision].length > 0 && (
<PageControl
current={videoQuery.current - 1}
pageSize={videoSize}
total={videoTotalCount}
current={videoQuery[videoCourseDivision].current - 1}
pageSize={videoSize[videoCourseDivision]}
total={videoTotalCount[videoCourseDivision]}
toPage={(page) => {
const _query = { ...videoQuery, current: page + 1 };
const _query = { ...videoQuery[videoCourseDivision], current: page + 1 }
console.log('_query', _query)
this.setState(
{
videoQuery: _query,
videoQuery: {
...videoQuery,
[videoCourseDivision]: _query
}
},
() => {
this.handleFetchVideoList();
this.handleFetchVideoList()
}
);
)
}}
onShowSizeChange={this.onShowVideoSizeChange}
/>
......@@ -801,84 +846,76 @@ class AddCourse extends React.Component {
</div>
</div>
</TabPane>
<TabPane tab="直播课" key="LIVE">
<div className="live-list">
<TabPane tab='直播课' key='LIVE'>
<div className='live-list'>
<div>
<Row type="flex" justify="space-between" align="top">
<Row type='flex' justify='space-between' align='top'>
<div>
<span style={{ lineHeight: "32px" }}>课程名称:</span>
<span style={{ lineHeight: '32px' }}>课程名称:</span>
<Search
// value={courseName}
style={{ width: "calc(100% - 75px)" }}
placeholder="搜索课程名称"
// onChange={(e) => {
// this.handleChangLiveFilter(
// "courseName",
// e.target.value
// );
// }}
style={{ width: 'calc(100% - 75px)' }}
placeholder='搜索课程名称'
onSearch={(value) => {
this.handleChangLiveFilter("courseName", value);
this.handleChangLiveFilter('courseName', value)
}}
enterButton={
<span className="icon iconfont">&#xe832;</span>
}
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/>
</div>
<div style={{ width: "50%" }}>
<span className="shelf-status">课程分类:</span>
<div style={{ width: '50%' }}>
<span className='shelf-status'>课程分类:</span>
<TreeSelect
style={{ width: "calc(100% - 75px)" }}
dropdownStyle={{ maxHeight: 400, overflow: "auto" }}
style={{ width: 'calc(100% - 75px)' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={categoryList}
placeholder="请选择课程类型"
placeholder='请选择课程类型'
allowClear
onChange={(value) => {
this.handleChangLiveFilter("categoryId", value);
this.handleChangLiveFilter('categoryId', value)
}}
/>
</div>
</Row>
</div>
<TableSelectedData
selectedNum={
selectVideo.length + selectLive.length + selectPicture.length
}
selectedNum={selectVideo.internal.length + selectVideo.external.length + selectLive.length + selectPicture.length}
clearSelectedData={() => {
this.setState({
selectVideo: [],
selectVideo: {
internal: [],
external: []
},
selectLive: [],
selectPicture: [],
});
selectPicture: []
})
}}
/>
<Table
bordered
size="middle"
size='middle'
pagination={false}
columns={this.liveColumns()}
rowSelection={LiveSelection}
// loading={loading}
dataSource={liveDataSource}
style={{ maxHeight: 359, overflow: "scroll" }}
style={{ maxHeight: 359, overflow: 'scroll' }}
rowKey={(row) => row.liveCourseId}
/>
{liveTotalCount > 0 && (
<div className="box-footer">
<div className='box-footer'>
<PageControl
current={liveQuery.current - 1}
pageSize={liveSize}
total={parseInt(liveTotalCount)}
toPage={(page) => {
const _query = { ...liveQuery, current: page + 1 };
const _query = { ...liveQuery, current: page + 1 }
this.setState(
{
liveQuery: _query,
liveQuery: _query
},
() => {
this.handleFetchLiveList();
this.handleFetchLiveList()
}
);
)
}}
onShowSizeChange={this.onShowLiveSizeChange}
/>
......@@ -886,77 +923,76 @@ class AddCourse extends React.Component {
)}
</div>
</TabPane>
<TabPane tab="图文课" key="PICTURE">
<div className="live-list">
<TabPane tab='图文课' key='PICTURE'>
<div className='live-list'>
<div>
<Row type="flex" justify="space-between" align="top">
<Row type='flex' justify='space-between' align='top'>
<div>
<span style={{ lineHeight: "32px" }}>课程名称:</span>
<span style={{ lineHeight: '32px' }}>课程名称:</span>
<Search
style={{ width: "calc(100% - 75px)" }}
placeholder="搜索课程名称"
style={{ width: 'calc(100% - 75px)' }}
placeholder='搜索课程名称'
onSearch={(value) => {
this.handleChangPictureFilter("courseName", value);
this.handleChangPictureFilter('courseName', value)
}}
enterButton={
<span className="icon iconfont">&#xe832;</span>
}
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/>
</div>
<div style={{ width: "50%" }}>
<span className="shelf-status">课程分类:</span>
<div style={{ width: '50%' }}>
<span className='shelf-status'>课程分类:</span>
<TreeSelect
style={{ width: "calc(100% - 75px)" }}
dropdownStyle={{ maxHeight: 400, overflow: "auto" }}
style={{ width: 'calc(100% - 75px)' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={categoryList}
placeholder="请选择课程类型"
placeholder='请选择课程类型'
allowClear
onChange={(value) => {
this.handleChangPictureFilter("categoryId", value);
this.handleChangPictureFilter('categoryId', value)
}}
/>
</div>
</Row>
</div>
<TableSelectedData
selectedNum={
selectVideo.length + selectLive.length + selectPicture.length
}
selectedNum={selectVideo.internal.length + selectVideo.external.length + selectLive.length + selectPicture.length}
clearSelectedData={() => {
this.setState({
selectVideo: [],
selectVideo: {
internal: [],
external: []
},
selectLive: [],
selectPicture: [],
});
selectPicture: []
})
}}
/>
<Table
bordered
size="middle"
size='middle'
pagination={false}
columns={this.pictureColumns()}
rowSelection={PictureSelection}
// loading={loading}
dataSource={pictureDataSource}
style={{ maxHeight: 359, overflow: "scroll" }}
style={{ maxHeight: 359, overflow: 'scroll' }}
rowKey={(row) => row.id}
/>
{pictureTotalCount > 0 && (
<div className="box-footer">
<div className='box-footer'>
<PageControl
current={pictureQuery.current - 1}
pageSize={pictureSize}
total={parseInt(pictureTotalCount)}
toPage={(page) => {
const _query = { ...pictureQuery, current: page + 1 };
const _query = { ...pictureQuery, current: page + 1 }
this.setState(
{
pictureQuery: _query,
pictureQuery: _query
},
() => {
this.handleFetchPictureList();
this.handleFetchPictureList()
}
);
)
}}
onShowSizeChange={this.onShowPictureSizeChange}
/>
......@@ -966,7 +1002,7 @@ class AddCourse extends React.Component {
</TabPane>
</Tabs>
</Modal>
);
)
}
}
export default AddCourse;
export default AddCourse
......@@ -125,42 +125,10 @@
}
}
.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 {
border: 1px solid #ffb714;
color: #ffb714;
}
.ant-tabs-top .ant-tabs-ink-bar-animated:after {
height: 0;
}
.ant-modal-content tr > td{
padding:12px 8px !important;
.ant-modal-content tr > td {
padding: 12px 8px !important;
}
}
/*
* @Author: zhangleyuan
* @Date: 2021-02-20 16:13:39
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-05-10 10:15:58
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-24 00:00:31
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { useEffect, useState } from "react";
import { Button, message, Modal} from 'antd';
import ShowTips from "@/components/ShowTips";
import Breadcrumbs from "@/components/Breadcrumbs";
import BasicInfo from './components/BasicInfo';
import TrainingTask from './components/TrainingTask';
import ExpiredCourseList from './components/ExpiredCourseList';
import React, { useEffect, useState } from 'react'
import { Button, message, Modal } from 'antd'
import ShowTips from '@/components/ShowTips'
import Breadcrumbs from '@/components/Breadcrumbs'
import BasicInfo from './components/BasicInfo'
import TrainingTask from './components/TrainingTask'
import ExpiredCourseList from './components/ExpiredCourseList'
import PlanService from '@/domains/plan-domain/planService'
import User from '@/common/js/user';
import _ from "underscore";
import User from '@/common/js/user'
import _ from 'underscore'
import './AddPlan.less'
const defaultCover = 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png';
const defaultCover = 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'
const defaultBasicData = {
planName:"",
planName: '',
coverUrl: defaultCover,
coverId:null,
enableState:"YES",
selectOperatorList:[],
instro:'',
operateType:'All_Operate',
percentCompleteLive:80,
percentCompleteVideo:80,
percentCompletePicture:100
coverId: null,
enableState: 'YES',
selectOperatorList: [],
instro: '',
operateType: 'All_Operate',
percentCompleteLive: 80,
percentCompleteVideo: 80,
percentCompletePicture: 100
}
const defaultTaskList = [];
const defaultTaskList = []
function AddPlan() {
const id = getParameterByName("id");
const type = getParameterByName("type");
const [basicData,setBasicData] = useState(defaultBasicData);
const [taskList,setTaskList] = useState(defaultTaskList);
const [expiredCourseList,setExpiredCourseList] = useState([]);
const [hasGetDetail,setHasGetDetail]= useState(false);
const [submitDisabled,setSubmitDisabled] = useState(false);
useEffect(()=>{
if(type==='edit'){
getPlanDetail();
getPlanCustomerState();
}
},id)
function getPlanCustomerState(){
const id = getParameterByName('id')
const type = getParameterByName('type')
const [basicData, setBasicData] = useState(defaultBasicData)
const [taskList, setTaskList] = useState(defaultTaskList)
const [expiredCourseList, setExpiredCourseList] = useState([])
const [hasGetDetail, setHasGetDetail] = useState(false)
const [submitDisabled, setSubmitDisabled] = useState(false)
useEffect(() => {
if (type === 'edit') {
getPlanDetail()
getPlanCustomerState()
}
}, id)
function getPlanCustomerState() {
PlanService.getTrainingCourseAutoCancel({
planId: id
}).then((res) => {
const expiredCourseList = res.result;
const expiredCourseList = res.result
setExpiredCourseList(expiredCourseList)
})
}
function getPlanDetail (){
function getPlanDetail() {
PlanService.getTrainingPlanDetail({
planId: id
}).then((res) => {
......@@ -68,231 +68,231 @@ function AddPlan() {
percentCompletePicture,
courseMediaVOS,
trainingTaskList
} = res.result;
let coverId;
let coverUrl;
let instro;
} = res.result
let coverId
let coverUrl
let instro
courseMediaVOS.map((item) => {
switch (item.contentType){
case "COVER":
coverId = item.mediaContent;
coverUrl = item.mediaUrl;
break;
case "INTRO":
instro = item.mediaContent;
break;
switch (item.contentType) {
case 'COVER':
coverId = item.mediaContent
coverUrl = item.mediaUrl
break
case 'INTRO':
instro = item.mediaContent
break
default:
break;
break
}
return item;
return item
})
let _selectOperatorList = [];
if(operateIds){
_selectOperatorList = operateIds.map((item,index)=>{
let _item = {};
_item.id = item;
let _selectOperatorList = []
if (operateIds) {
_selectOperatorList = operateIds.map((item, index) => {
let _item = {}
_item.id = item
return _item
})
}
setTaskList(trainingTaskList);
setTaskList(trainingTaskList)
setBasicData({
planName,
coverUrl:coverUrl || defaultCover,
coverUrl: coverUrl || defaultCover,
coverId,
enableState,
selectOperatorList:_selectOperatorList,
selectOperatorList: _selectOperatorList,
instro,
operateType,
percentCompleteLive,
percentCompleteVideo,
percentCompletePicture
})
setHasGetDetail(true);
setHasGetDetail(true)
})
}
function handleChangeBasicInfo(field, value,option){
setBasicData( {
function handleChangeBasicInfo(field, value, option) {
setBasicData({
...basicData,
[field]: value,
[field]: value
})
}
function handleChangeTaskInfo(value){
function handleChangeTaskInfo(value) {
setTaskList(value)
}
function submitInfo(){
const {planName,enableState,selectOperatorList,instro,operateType,percentCompleteLive,percentCompleteVideo,percentCompletePicture,coverId,coverUrl} = basicData;
let input = /^[\s]*$/;
if(!planName || input.test(planName)){
message.warning('请输入的培训计划名称');
return;
}
if(operateType==='Assign_Operate' && selectOperatorList.length===0){
message.warning('请选择指定运营师');
return;
}
if(!percentCompleteLive && percentCompleteLive !==0 ){
message.warning('请输入完成标准');
return;
}
if(!percentCompleteVideo && percentCompleteVideo !==0 ){
message.warning('请输入完成标准');
return;
}
if(!percentCompletePicture && percentCompletePicture !==0 ){
message.warning('请输入完成标准');
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){
function submitInfo() {
const {
planName,
enableState,
selectOperatorList,
instro,
operateType,
percentCompleteLive,
percentCompleteVideo,
percentCompletePicture,
coverId,
coverUrl
} = basicData
let input = /^[\s]*$/
if (!planName || input.test(planName)) {
message.warning('请输入的培训计划名称')
return
}
if (operateType === 'Assign_Operate' && selectOperatorList.length === 0) {
message.warning('请选择指定运营师')
return
}
if (!percentCompleteLive && percentCompleteLive !== 0) {
message.warning('请输入完成标准')
return
}
if (!percentCompleteVideo && percentCompleteVideo !== 0) {
message.warning('请输入完成标准')
return
}
if (!percentCompletePicture && percentCompletePicture !== 0) {
message.warning('请输入完成标准')
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({
title: '保存失败',
content: '每个任务下至少关联一个课程',
okText: '确定',
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 coverObj ={
contentType:'COVER',
mediaContent:coverId,
mediaType:'PICTURE',
mediaUrl: coverUrl,
let scheduleMediaRequests = []
let coverObj = {
contentType: 'COVER',
mediaContent: coverId,
mediaType: 'PICTURE',
mediaUrl: coverUrl
}
if(coverId){
scheduleMediaRequests = [...scheduleMediaRequests,coverObj];
if (coverId) {
scheduleMediaRequests = [...scheduleMediaRequests, coverObj]
}
let instroObj = {
contentType:"INTRO",
contentType: 'INTRO',
mediaType: 'TEXT',
mediaContent:instro,
mediaContent: instro
}
if(instro){
scheduleMediaRequests = [...scheduleMediaRequests,instroObj];
if (instro) {
scheduleMediaRequests = [...scheduleMediaRequests, instroObj]
}
const params = {
createId:User.getStoreUserId(),
createId: User.getStoreUserId(),
enableState,
operateIds:_.pluck(selectOperatorList,'id'),
operateIds: _.pluck(selectOperatorList, 'id'),
operateType,
percentCompleteLive,
percentCompleteVideo,
percentCompletePicture,
planName,
scheduleMediaRequests,
storeId:User.getStoreId(),
trainingTaskList:handleSubmitTaskData(taskList)
storeId: User.getStoreId(),
trainingTaskList: handleSubmitTaskData(taskList)
}
if (type === 'add') {
PlanService.createTrainingPlan(params).then((res) => {
if (res.success){
message.success("新建成功");
setSubmitDisabled(true);
window.RCHistory.goBack();
if (res.success) {
message.success('新建成功')
setSubmitDisabled(true)
window.RCHistory.goBack()
}
});
}else{
})
} else {
const _params = {
...params,
id
}
PlanService.updateTrainingPlan(_params).then((res) => {
if (res.success){
message.success("更新成功");
window.RCHistory.goBack();
if (res.success) {
message.success('更新成功')
window.RCHistory.goBack()
}
});
})
}
}
function handleSubmitTaskData(taskData){
return taskData.map((item,index)=>{
let _item = {};
_item.taskId = item.taskId;
_item.taskName =item.taskName;
_item.courseList = item.courseList.map((childItem,index)=>{
function handleSubmitTaskData(taskData) {
return taskData.map((item, index) => {
let _item = {}
_item.taskId = item.taskId
_item.taskName = item.taskName
_item.courseList = item.courseList.map((childItem, index) => {
let _childItem = {}
_childItem.courseId = childItem.courseId;
_childItem.courseName = childItem.courseName;
_childItem.courseType = childItem.courseType;
return _childItem;
});
return _item;
_childItem.courseId = childItem.courseId
_childItem.courseName = childItem.courseName
_childItem.courseType = childItem.courseType
return _childItem
})
return _item
})
}
// 取消编辑并返回上一级路由
function handleGoBack (){
if (!_.isEqual(basicData, defaultBasicData) || !_.isEqual(taskList, defaultTaskList)
) {
function handleGoBack() {
if (!_.isEqual(basicData, defaultBasicData) || !_.isEqual(taskList, defaultTaskList)) {
Modal.confirm({
title: '确定要返回吗?',
content: '返回后,本次编辑的内容将不被保存',
okText: '确认返回',
cancelText: '留在本页',
icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>,
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
onOk: () => {
window.RCHistory.goBack();
window.RCHistory.goBack()
}
})
} else {
window.RCHistory.goBack();
window.RCHistory.goBack()
}
}
return (
<div className="page add-plan-page">
<Breadcrumbs
navList={type == "add" ? "新建培训计划" : "编辑培训计划"}
goBack={handleGoBack}
/>
<div className="box">
<div className="show-tips">
<ShowTips message="请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利" />
<div className='page add-plan-page'>
<Breadcrumbs navList={type == 'add' ? '新建培训计划' : '编辑培训计划'} goBack={handleGoBack} />
<div className='box'>
<div className='show-tips'>
<ShowTips message='请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利' />
</div>
<div className="add-plan-page__form">
<div className="basic-info__wrap">
<div className="title">基本信息</div>
<BasicInfo
data={basicData}
onChange={handleChangeBasicInfo}
/>
<div className='add-plan-page__form'>
<div className='basic-info__wrap'>
<div className='title'>基本信息</div>
<BasicInfo data={basicData} onChange={handleChangeBasicInfo} />
</div>
<div className="basic-info__wrap">
<div className="title">培训任务</div>
{ (type==='edit' && hasGetDetail) &&
<TrainingTask data={taskList} onChange={handleChangeTaskInfo} />
}
{ type==='add' &&
<TrainingTask data={taskList} onChange={handleChangeTaskInfo} />
}
<div className='basic-info__wrap'>
<div className='title'>培训任务</div>
{type === 'edit' && hasGetDetail && <TrainingTask data={taskList} onChange={handleChangeTaskInfo} />}
{type === 'add' && <TrainingTask data={taskList} onChange={handleChangeTaskInfo} />}
</div>
{ (type==='edit' && expiredCourseList.length > 0) &&
<div className="expired-info__wrap">
<div className="title">失效课程</div>
<ExpiredCourseList expiredCourseList={expiredCourseList}/>
{type === 'edit' && expiredCourseList.length > 0 && (
<div className='expired-info__wrap'>
<div className='title'>失效课程</div>
<ExpiredCourseList expiredCourseList={expiredCourseList} />
</div>
}
)}
</div>
</div>
<div className="footer">
<div className='footer'>
<Button onClick={handleGoBack}>取消</Button>
<Button type="primary" onClick={submitInfo} disabled={submitDisabled}>保存</Button>
<Button type='primary' onClick={submitInfo} disabled={submitDisabled}>
保存
</Button>
</div>
</div>
)
}
export default AddPlan;
\ No newline at end of file
export default AddPlan
/*
* @Author: zhangleyuan
* @Date: 2021-02-20 16:13:39
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-10 18:38:50
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-23 23:59:43
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { useEffect, useState } from "react";
import PlanFilter from './components/PlanFilter';
import PlanOpt from './components/PlanOpt';
import PlanList from './components/PlanList';
import PlanService from "@/domains/plan-domain/planService";
import User from '@/common/js/user';
import React, { useEffect, useState } from 'react'
import PlanFilter from './components/PlanFilter'
import PlanOpt from './components/PlanOpt'
import PlanList from './components/PlanList'
import PlanService from '@/domains/plan-domain/planService'
import User from '@/common/js/user'
function PlanPage() {
const [planListData, setPlanListData] = useState([]);
const [query,setQuery] = useState({
current:1,
size:10,
});
const [planListData, setPlanListData] = useState([])
const [query, setQuery] = useState({
current: 1,
size: 10
})
useEffect(() => {
handleFetchPlanList();
}, [query]);
const [totalCount,setTotalCount] = useState(0);
function queryChange(_query){
function handleFetchPlanList(_query) {
const params = {
...query,
..._query,
};
setQuery(params);
storeUserId: User.getStoreUserId()
}
function handleFetchPlanList(_query){
const params = {
...query,
..._query,
storeUserId:User.getStoreUserId()
};
//动态获取计划列表
PlanService.getTrainingPlanPage(params).then((res) => {
const { result: { records = [], total } } = res;
setPlanListData(records);
setTotalCount(total);
const {
result: { records = [], total }
} = res
setPlanListData(records)
setTotalCount(total)
})
}
handleFetchPlanList()
}, [query])
const [totalCount, setTotalCount] = useState(0)
function queryChange(_query) {
const params = {
...query,
..._query
}
setQuery(params)
}
return (
<div className="page">
<div className="content-header">培训计划</div>
<div className="box">
<PlanFilter onChange={queryChange}/>
<PlanOpt/>
<PlanList
planListData={planListData}
query={query}
totalCount={totalCount}
onChange={queryChange}
/>
<div className='page'>
<div className='content-header'>培训计划</div>
<div className='box'>
<PlanFilter onChange={queryChange} />
<PlanOpt />
<PlanList planListData={planListData} query={query} totalCount={totalCount} onChange={queryChange} />
</div>
</div>
)
}
export default PlanPage;
\ No newline at end of file
export default PlanPage
/*
* @Author: zhangleyuan
* @Date: 2021-02-20 16:45:51
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-27 14:43:17
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-24 15:15:06
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
import { Table ,Button,Input,Form,Collapse,Modal } from 'antd';
import { sortableContainer, sortableElement, sortableHandle} from 'react-sortable-hoc';
import { MenuOutlined } from '@ant-design/icons';
import arrayMove from 'array-move';
import React from 'react'
import { Input, Form, Modal } from 'antd'
import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc'
import arrayMove from 'array-move'
import RelatedCourseModal from '../modal/relatedCourseModal'
import { withRouter } from 'react-router-dom';
import './TrainingTask.less';
const { Panel } = Collapse;
const { confirm } = Modal;
import { withRouter } from 'react-router-dom'
import './TrainingTask.less'
const { confirm } = Modal
const CourseType = {
LIVE: {
text: "直播课"
text: '直播课'
},
VOICE : {
text:"视频课"
VOICE: {
text: '视频课'
},
RECORD : {
text:'录播课'
RECORD: {
text: '录播课'
},
PICTURE:{
text:'图文课'
PICTURE: {
text: '图文课'
}
};
}
const courseStateShow = {
UN_START: {
title: "待开播",
title: '待开播'
},
STARTING: {
title: "直播中",
title: '直播中'
},
FINISH: {
title: "回放",
title: '回放'
},
EXPIRED: {
title: "未成功开课",
},
};
title: '未成功开课'
}
}
const DragHandle = sortableHandle(() => (
<span className="operate__item" >
<span className="icon iconfont">&#xe7cd;</span>
<span className="text">移动</span>
<span className='operate__item'>
<span className='icon iconfont'>&#xe7cd;</span>
<span className='text'>移动</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 SortableTaskContainer = sortableContainer(props => <div {...props}></div>);
const SortableCourseItem = sortableElement(props => <div {...props}>{props.courseItem}</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 {
constructor(props) {
super(props);
super(props)
this.state = {
dataSource:this.props.data,
selectedTaskIndex:0,
relatedCourseModalVisible:false
};
dataSource: this.props.data,
selectedTaskIndex: 0,
relatedCourseModalVisible: false
}
componentWillMount(){
}
componentWillMount() {}
componentWillReceiveProps(nextProps) {
}
componentWillReceiveProps(nextProps) {}
onTaskSortEnd = ({ oldIndex, newIndex }) => {
const { dataSource } = this.state;
const { dataSource } = this.state
if (oldIndex !== newIndex) {
const newData = arrayMove([].concat(dataSource), oldIndex, newIndex).filter(el => !!el);
this.setState({
dataSource:newData,
},()=>{this.props.onChange(newData);})
const newData = arrayMove([].concat(dataSource), oldIndex, newIndex).filter((el) => !!el)
this.setState(
{
dataSource: 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) {
_dataSource[parentIndex].courseList = arrayMove([].concat(dataSource[parentIndex].courseList), oldIndex, newIndex).filter(el => !!el);
this.setState({
dataSource:_dataSource,
},()=>{this.props.onChange(_dataSource);})
_dataSource[parentIndex].courseList = arrayMove([].concat(dataSource[parentIndex].courseList), oldIndex, newIndex).filter((el) => !!el)
this.setState(
{
dataSource: _dataSource
},
() => {
this.props.onChange(_dataSource)
}
)
}
}
};
addTask = () => {
const { dataSource } = this.state;
const taskObj={
const { dataSource } = this.state
const taskObj = {
taskName: '',
index:dataSource.length,
type:'input',
open:true,
courseList:[
]
index: dataSource.length,
type: 'input',
open: true,
courseList: []
}
const newData = [...dataSource,taskObj];
this.setState({
dataSource:newData
},()=>{this.props.onChange(newData);})
const newData = [...dataSource, taskObj]
this.setState(
{
dataSource: newData
},
() => {
this.props.onChange(newData)
}
)
}
handleRenameTaskName = (e,record) => {
const { value } = e.target;
const { dataSource } = this.state;
record.taskName = value;
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);})
handleRenameTaskName = (e, record) => {
const { value } = e.target
const { dataSource } = this.state
record.taskName = value
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)=>{
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) => {
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({
title: "删除任务",
content: "删除该任务会同步删除任务下的课程,是否仍要删除?",
icon: (
<span className="icon iconfont default-confirm-icon">&#xe839; </span>
),
okText: "删除",
okType: "danger",
cancelText: "取消",
title: '删除任务',
content: '删除该任务会同步删除任务下的课程,是否仍要删除?',
icon: <span className='icon iconfont default-confirm-icon'>&#xe839; </span>,
okText: '删除',
okType: 'danger',
cancelText: '取消',
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({
title: "删除课程",
content: "确定删除该课程吗?",
icon: (
<span className="icon iconfont default-confirm-icon">&#xe839; </span>
),
okText: "删除",
okType: "danger",
cancelText: "取消",
title: '删除课程',
content: '确定删除该课程吗?',
icon: <span className='icon iconfont default-confirm-icon'>&#xe839; </span>,
okText: '删除',
okType: 'danger',
cancelText: '取消',
onOk: () => {
this.handleConfirmDeleteCourse(parentIndex,index);
},
});
this.handleConfirmDeleteCourse(parentIndex, index)
}
})
}
handleConfirmDeleteCourse = (parentIndex,index)=>{
const {dataSource}= this.state;
const newData=[...dataSource];
handleConfirmDeleteCourse = (parentIndex, index) => {
const { dataSource } = this.state
const newData = [...dataSource]
const selectData = [...newData[parentIndex].courseList]
selectData.splice(index,1)
newData[parentIndex].courseList= selectData;
this.setState({
dataSource:newData,
},()=>{this.props.onChange(newData);})
selectData.splice(index, 1)
newData[parentIndex].courseList = selectData
this.setState(
{
dataSource: newData
},
() => {
this.props.onChange(newData)
}
)
}
showRelatedCourseModal = (index)=>{
showRelatedCourseModal = (index) => {
this.setState({
selectedTaskIndex:index,
relatedCourseModalVisible:true
selectedTaskIndex: index,
relatedCourseModalVisible: true
})
}
closeRelatedCourseModal = (index)=>{
closeRelatedCourseModal = (index) => {
this.setState({
relatedCourseModalVisible:false
relatedCourseModalVisible: false
})
}
confirmSelectCourse = (selectList) =>{
console.log("selectList",selectList);
const {selectedTaskIndex}= this.state;
confirmSelectCourse = (selectList) => {
console.log('selectList', selectList)
const { selectedTaskIndex } = this.state
const { dataSource } = this.state
const newData=[...dataSource];
const newData = [...dataSource]
const selectData = [...newData[selectedTaskIndex].courseList]
const _selectData =[...selectData,...selectList];
newData[selectedTaskIndex].courseList= _selectData;
this.setState({
relatedCourseModalVisible:false,
dataSource:newData,
},()=>{
this.props.onChange(newData);
})
const _selectData = [...selectData, ...selectList]
newData[selectedTaskIndex].courseList = _selectData
this.setState(
{
relatedCourseModalVisible: false,
dataSource: 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)=>{
let input = /^[\s]*$/;
if (input.test(value) || !value){
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) => {
let input = /^[\s]*$/
if (input.test(value) || !value) {
return Promise.reject(new Error('请输入任务名称'))
}
return Promise.resolve()
}
handleValidatorCourseName = (rule,value)=>{
let input = /^[\s]*$/;
if (input.test(value) || !value){
handleValidatorCourseName = (rule, value) => {
let input = /^[\s]*$/
if (input.test(value) || !value) {
return Promise.reject(new Error('请输入课程名称'))
}
return Promise.resolve()
}
renderTaskItem = (record,index)=>{
return <div className="plan-sort-task-item">
<div className="task-con">
<div className="task-instro">
<span onClick={()=>this.openOrCloseTask(index)}>
{record.open?
<span className="icon iconfont open-icon">&#xe82d;</span>:
<span className="icon iconfont open-icon">&#xe835;</span>
}
renderTaskItem = (record, index) => {
return (
<div className='plan-sort-task-item'>
<div className='task-con'>
<div className='task-instro'>
<span onClick={() => this.openOrCloseTask(index)}>
{record.open ? <span className='icon iconfont open-icon'>&#xe82d;</span> : <span className='icon iconfont open-icon'>&#xe835;</span>}
</span>
{record.type==='input'?
<div className="task-name-con">
<span className="number">{index + 1}.</span>
<Choose>
<When condition={record.type === 'input'}>
<div className='task-name-con'>
<span className='number'>{index + 1}.</span>
<Form>
<Form.Item
initialValue={record.taskName}
validateTrigger={['onChange', 'onBlur']}
name={['taskName']}
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>
</div>
:
<div className="task-name-con">
<span className="number">{index + 1}.</span>
<span className="task-name">{record.taskName}</span>
</When>
<Otherwise>
<div className='task-name-con'>
<span className='number'>{index + 1}.</span>
<span className='task-name'>{record.taskName}</span>
</div>
}
</Otherwise>
</Choose>
</div>
<div className="operate">
<div className='operate'>
<DragHandle />
<span className="operate__item" onClick={()=>{const { dataSource }= this.state; record.type="input";this.setState({dataSource})}}>
<span className="icon iconfont">&#xe6f5;</span>
<span className="text">重命名</span>
<span
className='operate__item'
onClick={() => {
const { dataSource } = this.state
record.type = 'input'
this.setState({ dataSource })
}}>
<span className='icon iconfont'>&#xe6f5;</span>
<span className='text'>重命名</span>
</span>
<span className="operate__item" onClick={()=>{this.handleDeleteTask(index)}} >
<span className="icon iconfont">&#xe6f6;</span>
<span className="text">删除</span>
<span
className='operate__item'
onClick={() => {
this.handleDeleteTask(index)
}}>
<span className='icon iconfont'>&#xe6f6;</span>
<span className='text'>删除</span>
</span>
</div>
</div>
{record.open &&
<div className="course-box">
<SortableCourseContainer
useDragHandle
disableAutoscroll
helperClass="row-dragging"
onSortEnd={(record)=>this.onCourseSortEnd(record,index)} >
{record.courseList.map((courseItem, courseIndex) =>
<SortableCourseItem courseItem={this.renderCourseItem(courseItem,courseIndex,index)} index={courseIndex}>
</SortableCourseItem>
)}
{record.open && (
<div className='course-box'>
<SortableCourseContainer useDragHandle disableAutoscroll helperClass='row-dragging' onSortEnd={(record) => this.onCourseSortEnd(record, index)}>
{record.courseList.map((item, courseIndex) => (
<SortableCourseItem courseitem={this.renderCourseItem(item, courseIndex, index)} index={courseIndex} key={courseIndex}></SortableCourseItem>
))}
</SortableCourseContainer>
<div className="add-course-con">
{record.courseList.length>19?
<span className="add-course-btn-disabled" onClick={()=>{this.showRelatedCourseModal(index)}}><span>+</span><span>关联课程</span></span>
:
<span className="add-course-btn" onClick={()=>{this.showRelatedCourseModal(index)}}><span>+</span><span>关联课程</span></span>
}
<div className='add-course-con'>
<Choose>
<When condition={record.courseList.length > 19}>
<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>
)
}
renderCourseItem = (record,index,parentIndex)=>{
return <div className="plan-course-sort-item">
<div className="course-info">
<span className="course-type">{CourseType[record.courseType].text}</span>
{record.type==='input'?
renderCourseItem = (record, index, parentIndex) => {
return (
<div className='plan-course-sort-item'>
<div className='course-info'>
<span className='course-type'>{CourseType[record.courseType].text}</span>
<Choose>
<When condition={record.type === 'input'}>
<Form>
<Form.Item
initialValue={record.courseName}
validateTrigger={['onChange', 'onBlur']}
name={['courseName']}
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>
:
<span className="course-name">{parentIndex + 1}.{index + 1} {record.courseName}</span>
}
{record.courseState === "EXPIRED" &&
<span className="icon iconfont tip">&#xe834;</span>
}
{ record.courseType==="LIVE" &&
<span className="course-state">{courseStateShow[record.courseState].title}</span>
}
</When>
<Otherwise>
<span className='course-name'>
{parentIndex + 1}.{index + 1} {record.courseName}
</span>
</Otherwise>
</Choose>
{record.courseState === 'EXPIRED' && <span className='icon iconfont tip'>&#xe834;</span>}
{record.courseType === 'LIVE' && <span className='course-state'>{courseStateShow[record.courseState].title}</span>}
</div>
<div className="course-operate">
<div className='course-operate'>
<DragHandle />
{/* <span className="operate__item">
<span className="icon iconfont">&#xe6f5;</span>
<span className="text" onClick={(e)=>{const { dataSource } = this.state; record.type="input";this.setState({dataSource})}}>重命名</span>
</span> */}
<span className="operate__item" onClick={()=>{this.handleDeleteCourse(parentIndex,index)}}>
<span className="icon iconfont">&#xe6f6;</span>
<span className="text">删除</span>
<span
className='operate__item'
onClick={() => {
this.handleDeleteCourse(parentIndex, index)
}}>
<span className='icon iconfont'>&#xe6f6;</span>
<span className='text'>删除</span>
</span>
</div>
</div>
)
}
render() {
const { dataSource,selectedTaskIndex,relatedCourseModalVisible} = this.state;
const { data } = this.props;
const { dataSource, selectedTaskIndex, relatedCourseModalVisible } = this.state
console.log('dataSource', dataSource)
return (
<div className="training-task">
<SortableTaskContainer
useDragHandle
disableAutoscroll
helperClass="row-dragging"
onSortEnd={this.onTaskSortEnd}
className="plan-task-sort-container"
>
{dataSource.map((item, index) =>
<SortableTaskItem taskItem={this.renderTaskItem(item,index)} index={index}>
</SortableTaskItem>
)}
<div className='training-task'>
<SortableTaskContainer useDragHandle disableAutoscroll helperClass='row-dragging' onSortEnd={this.onTaskSortEnd} className='plan-task-sort-container'>
{dataSource.map((item, index) => (
<SortableTaskItem taskitem={this.renderTaskItem(item, index)} index={index} key={index}></SortableTaskItem>
))}
</SortableTaskContainer>
<div className="add-task-con">
{dataSource.length > 9 ?
<span className="add-task-btn-disabled"><span>+</span><span>添加任务</span></span>
:
<span className="add-task-btn" onClick={()=>this.addTask()}><span>+</span><span>添加任务</span></span>
}
<div className='add-task-con'>
<Choose>
<When condition={dataSource.length > 9}>
<span className='add-task-btn-disabled'>
<span>+</span>
<span>添加任务</span>
</span>
</When>
<Otherwise>
<span className='add-task-btn' onClick={() => this.addTask()}>
<span>+</span>
<span>添加任务</span>
</span>
</Otherwise>
</Choose>
</div>
{ relatedCourseModalVisible &&
{relatedCourseModalVisible && (
<RelatedCourseModal
selectedTaskIndex={selectedTaskIndex}
data={dataSource}
......@@ -403,9 +492,9 @@ class TrainingTask extends React.Component {
onClose={this.closeRelatedCourseModal}
onSelect={this.confirmSelectCourse}
/>
}
)}
</div>
);
)
}
}
......
import React from 'react';
import {Table, Modal,Input,message,Button,Tooltip} from 'antd';
import { PageControl } from "@/components";
import CourseService from "@/domains/course-domain/CourseService";
import User from '@/common/js/user';
import Service from '@/common/js/service';
import { Tabs } from 'antd';
import './relatedCourseModal.less';
import _ from "underscore";
import dealTimeDuration from "../../course-manage/utils/dealTimeDuration";
const { Search } = Input;
const { TabPane } = Tabs;
import React from 'react'
import _ from 'underscore'
import { Table, Radio, Tabs, Modal, Input, message, Button, Tooltip } from 'antd'
import { PageControl } from '@/components'
import CourseService from '@/domains/course-domain/CourseService'
import User from '@/common/js/user'
import Service from '@/common/js/service'
import dealTimeDuration from '../../course-manage/utils/dealTimeDuration'
import './relatedCourseModal.less'
const { Search } = Input
const { TabPane } = Tabs
const courseStateShow = {
UN_START: {
code: 1,
title: "待开课",
color: "#FFB714",
title: '待开课',
color: '#FFB714'
},
STARTING: {
code: 2,
title: "上课中",
color: "#238FFF",
title: '上课中',
color: '#238FFF'
},
FINISH: {
code: 3,
title: "已完成",
color: "#3BBDAA",
title: '已完成',
color: '#3BBDAA'
},
EXPIRED: {
code: 4,
title: "未成功开课",
color: "#999",
},
};
title: '未成功开课',
color: '#999'
}
}
class SelectOperatorModal extends React.Component {
constructor(props) {
super(props);
super(props)
this.state = {
liveDataSource:[],
liveSize:10,
liveDataSource: [],
liveSize: 10,
liveQuery: {
current: 1,
current: 1
},
liveTotalCount:0,
selectLive:[],//弹窗内已选择的直播课程
currentCourseListData:[],
currentLiveCourseListData:[], //页面中已关联的直播课程
videoDataSource:[],
videoSize:10,
liveTotalCount: 0,
selectLive: [], //弹窗内已选择的直播课程
currentCourseListData: [],
currentLiveCourseListData: [], //页面中已关联的直播课程
videoCourseDivision: 'internal',
videoDataSource: {
external: [],
internal: []
},
videoSize: {
external: 10,
internal: 10
},
videoSearchName: {
external: '',
internal: ''
},
videoSearchDefalt: '',
videoQuery: {
current: 1,
external: {
current: 1
},
videoTotalCount:0,
selectVideo:[], //弹窗内已选择的视频课程
currentVideoCourseListData:[], //页面中已关联的视频课程
pictureDataSource:[],
pictureSize:10,
internal: {
current: 1
}
},
videoTotalCount: {
external: 0,
internal: 0
},
selectVideo: {
external: [],
internal: []
}, //弹窗内已选择的视频课程
currentVideoCourseListData: {
external: [],
internal: []
}, //页面中已关联的视频课程
pictureDataSource: [],
pictureSize: 10,
pictureQuery: {
current: 1,
current: 1
},
pictureTotalCount:0,
selectPicture:[], //弹窗内已选择的视频课程
currentPictureCourseListData:[], //页面中已关联的视频课程
pictureTotalCount: 0,
selectPicture: [], //弹窗内已选择的视频课程
currentPictureCourseListData: [], //页面中已关联的视频课程
activeKey:'video',
currentTaskCourseData:this.props.data[this.props.selectedTaskIndex].courseList || []
activeKey: 'video',
currentTaskCourseData: this.props.data[this.props.selectedTaskIndex].courseList || []
}
}
componentDidMount() {
this.handleFetchLiveDataList();
this.handleFetchVideoDataList();
this.handleFetchPictureDataList();
this.handleFetchLiveDataList()
this.handleFetchVideoDataList()
this.handleFetchPictureDataList()
}
// 获取直播课列表
handleFetchLiveDataList = () => {
const {liveQuery,liveSize} = this.state;
const { selectedTaskIndex } =this.props;
const _data = [...this.props.data];
let currentLiveCourseListData = [];
_data.map((item,index) => {
item.courseList.map((childItem,childIndex)=>{
if(childItem.courseType ==="LIVE"){
const { liveQuery, liveSize } = this.state
const _data = [...this.props.data]
let currentLiveCourseListData = []
_data.map((item) => {
item.courseList.map((childItem, childIndex) => {
if (childItem.courseType === 'LIVE') {
currentLiveCourseListData.push(childItem.courseId)
}
return childItem
})
return item
});
const params ={
})
const params = {
...liveQuery,
size:liveSize,
excludeCourseIdList:currentLiveCourseListData
size: liveSize,
excludeCourseIdList: currentLiveCourseListData
}
CourseService.getLiveCloudCourseBasePage(params).then((res) => {
const { result = {} } = res ;
const { records = [], total = 0 } = result;
const { result = {} } = res
const { records = [], total = 0 } = result
this.setState({
liveDataSource: records,
liveTotalCount: Number(total),
currentLiveCourseListData
});
});
})
})
}
// 获取视频课列表
handleFetchVideoDataList = () => {
const {videoQuery,videoSize,videoTotalCount} = this.state;
const { selectedTaskIndex } =this.props;
const _data = [...this.props.data];
let currentVideoCourseListData = [];
_data.map((item,index) => {
item.courseList.map((childItem,childIndex)=>{
if(childItem.courseType ==="VOICE"){
const { videoQuery, videoSize, videoDataSource, videoTotalCount, videoCourseDivision } = this.state
const _data = [...this.props.data]
let currentVideoCourseListData = []
_data.map((item, index) => {
item.courseList.map((childItem, childIndex) => {
if (childItem.courseType === 'VOICE') {
currentVideoCourseListData.push(childItem.courseId)
}
return childItem
})
return item
});
const params ={
...videoQuery,
size:videoSize,
excludeCourseIdList:currentVideoCourseListData
})
const params = {
...videoQuery[videoCourseDivision],
size: videoSize[videoCourseDivision],
courseDivision: videoCourseDivision === 'internal' ? 'INTERNAL' : 'EXTERNAL',
excludeCourseIdList: currentVideoCourseListData
}
CourseService.videoScheduleBasePage(params).then((res) => {
const { result = {} } = res ;
const { records = [], total = 0 } = result;
const { result = {} } = res
console.log('result', result)
const { records = [], total = 0 } = result
this.setState({
videoDataSource: records,
videoTotalCount: Number(total),
videoDataSource: {
...videoDataSource,
[videoCourseDivision]: records
},
videoTotalCount: {
...videoTotalCount,
[videoCourseDivision]: Number(total)
},
currentVideoCourseListData
});
});
})
})
}
// 获取图文课列表
handleFetchPictureDataList = () => {
const {pictureQuery,pictureSize} = this.state;
const { selectedTaskIndex } =this.props;
const _data = [...this.props.data];
let currentPictureCourseListData = [];
_data.map((item,index) => {
item.courseList.map((childItem,childIndex)=>{
if(childItem.courseType ==="PICTURE"){
const { pictureQuery, pictureSize } = this.state
const _data = [...this.props.data]
let currentPictureCourseListData = []
_data.map((item, index) => {
item.courseList.map((childItem, childIndex) => {
if (childItem.courseType === 'PICTURE') {
currentPictureCourseListData.push(childItem.courseId)
}
return childItem
})
return item
});
const params ={
})
const params = {
...pictureQuery,
size:pictureSize,
courseType:"PICTURE",
storeId:User.getStoreId(),
excludeCourseIdList:currentPictureCourseListData
size: pictureSize,
courseType: 'PICTURE',
storeId: User.getStoreId(),
excludeCourseIdList: currentPictureCourseListData
}
Service.Hades('public/hades/mediaCoursePage', params).then((res) => {
const { result = {} } = res ;
const { records = [], total = 0 } = result;
const { result = {} } = res
const { records = [], total = 0 } = result
this.setState({
pictureDataSource: records,
pictureTotalCount: Number(total),
currentPictureCourseListData
});
});
})
})
}
handleChangVideoCourseName = (value)=>{
const { videoQuery } = this.state;
videoQuery.courseName = value;
videoQuery.current = 1;
handleChangVideoCourseName = (value) => {
const { videoQuery, videoCourseDivision, videoSearchName } = this.state
videoQuery[videoCourseDivision].courseName = value
videoQuery[videoCourseDivision].current = 1
this.setState({
videoQuery
...videoQuery,
videoSearchDefalt: value,
videoSearchName: {
...videoSearchName,
[videoCourseDivision]: value
}
})
}
handleChangLiveCourseName = (value)=>{
const { liveQuery } = this.state;
liveQuery.courseName = value;
liveQuery.current = 1;
handleChangLiveCourseName = (value) => {
const { liveQuery } = this.state
liveQuery.courseName = value
liveQuery.current = 1
this.setState({
liveQuery
})
}
handleChangPictureCourseName = (value)=>{
const { pictureQuery } = this.state;
pictureQuery.courseName = value;
pictureQuery.current = 1;
handleChangPictureCourseName = (value) => {
const { pictureQuery } = this.state
pictureQuery.courseName = value
pictureQuery.current = 1
this.setState({
pictureQuery
})
}
onShowLiveSizeChange = (current, size) => {
if (current == size) {
if (current === size) {
return
}
this.setState({
liveSize:size
},()=>{this.handleFetchLiveDataList()})
this.setState(
{
liveSize: size
},
() => {
this.handleFetchLiveDataList()
}
)
}
onShowVideoSizeChange = (current, size) => {
if (current == size) {
if (current === size) {
return
}
this.setState({
videoSize:size
},()=>{this.handleFetchLiveDataList()})
this.setState(
{
videoSize: size
},
() => {
this.handleFetchLiveDataList()
}
)
}
onShowPictureSizeChange = (current, size) => {
if (current == size) {
if (current === size) {
return
}
this.setState({
pictureSize:size
},()=>{this.handleFetchPictureDataList()})
this.setState(
{
pictureSize: size
},
() => {
this.handleFetchPictureDataList()
}
)
}
// 请求表头
parseLiveColumns = () => {
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: (
<span>
<span>课程信息</span>
<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%',
width: '40%',
render: (val, record) => {
let hasCover = false;
let hasCover = false
return (
<div className="course-info">
{
record.courseMediaVOS.map((item,index)=>{
if( item.contentType === "COVER"){
hasCover = true;
return <img className="course-cover" src={item.mediaUrl}/>
}
})
}
{ !hasCover &&
<img className="course-cover" src={'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'} />
}
<div className='course-info'>
{record.courseMediaVOS.map((item) => {
if (item.contentType === 'COVER') {
hasCover = true
return <img className='course-cover' src={item.mediaUrl} alt='' />
}
return null
})}
<If condition={!hasCover}>
<img className='course-cover' src={'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'} alt='' />
</If>
<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>
<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>
</div>
</div>
)
......@@ -263,12 +331,14 @@ class SelectOperatorModal extends React.Component {
title: '上课时间',
key: 'courseTime',
dataIndex: 'courseTime',
width:'40%',
width: '40%',
render: (val, record) => {
return (
<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>
)
}
......@@ -277,39 +347,50 @@ class SelectOperatorModal extends React.Component {
title: '学院展示',
key: 'shelfState',
dataIndex: 'shelfState',
width:'20%',
width: '20%',
render: (val, record) => {
return (
<span>
{record.shelfState==="YES"?
<Choose>
<When condition={record.shelfState === 'YES'}>
<span>开启</span>
:
</When>
<Otherwise>
<span>关闭</span>
}
</Otherwise>
</Choose>
</span>
)
}
},
];
return columns;
}
]
return columns
}
// 请求表头
parseVideoColumns = () => {
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: (
<span>
<span>课程信息</span>
<Tooltip title='仅显示未关联课程,已关联课程不支持重复选择'>
<i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px' }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
key: 'course',
dataIndex: 'course',
width:'60%',
width: '40%',
render: (val, record) => {
const { coverUrl, scheduleVideoUrl } = record;
const { coverUrl, scheduleVideoUrl } = record
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`}/>
<div className="course-name">{record.courseName}</div>
<img className='course-cover' src={coverUrl || `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`} alt='' />
<div className='course-name'>{record.courseName}</div>
</div>
)
}
......@@ -318,50 +399,57 @@ class SelectOperatorModal extends React.Component {
title: '课程时长',
key: 'courseTime',
dataIndex: 'courseTime',
width:'20%',
width: '20%',
render: (val, record) => {
return (
<span className="course-status">{dealTimeDuration(record.videoDuration)}</span>
)
return <span className='course-status'>{dealTimeDuration(record.videoDuration)}</span>
}
},
{
title: '学院展示',
key: 'shelfState',
dataIndex: 'shelfState',
width:'20%',
width: '20%',
render: (val, record) => {
return (
<span>
{record.shelfState==="YES"?
<Choose>
<When condition={record.shelfState === 'YES'}>
<span>开启</span>
:
</When>
<Otherwise>
<span>关闭</span>
}
</span>
</Otherwise>
</Choose>
)
}
}
];
return columns;
]
return columns
}
// 请求表头
parsePictureColumns = () => {
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: (
<span>
<span>课程信息</span>
<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%',
width: '55%',
render: (val, record) => {
const { coverUrl } = record;
const { coverUrl } = record
return (
<div className="course-info">
<div className='course-info'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
<img className="course-cover" src={coverUrl || 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'}/>
<div className="course-name">{record.courseName}</div>
<img className='course-cover' src={coverUrl || 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'} alt='' />
<div className='course-name'>{record.courseName}</div>
</div>
)
}
......@@ -370,136 +458,177 @@ class SelectOperatorModal extends React.Component {
title: '更新时间',
key: 'updated',
dataIndex: 'updated',
width:'25%',
width: '25%',
render: (val, record) => {
return (
<span className="course-status">{formatDate('YYYY-MM-DD',record.updated)}</span>
)
return <span className='course-status'>{formatDate('YYYY-MM-DD', record.updated)}</span>
}
},
{
title: '学院展示',
key: 'shelfState',
dataIndex: 'shelfState',
width:'20%',
width: '20%',
render: (val, record) => {
return (
<span>
{record.shelfState==="YES"?
<span>开启</span>
:
<span>关闭</span>
}
</span>
)
return <span>{record.shelfState === 'YES' ? '开启' : '关闭'}</span>
}
}
];
return columns;
]
return columns
}
selectLiveList = (record,selected) =>{
const {selectVideo,currentTaskCourseData,selectLive,selectPicture} = this.state;
let _list = [];
if (selected || !_.find(selectLive, (item) => item.liveCourseId == record.liveCourseId)) {
_list = _.uniq(selectLive.concat([record]), false, (item) => item.liveCourseId);
selectLiveList = (record, selected) => {
const { selectVideo, currentTaskCourseData, selectLive, selectPicture } = this.state
let _list = []
if (selected || !_.find(selectLive, (item) => item.liveCourseId === record.liveCourseId)) {
_list = _.uniq(selectLive.concat([record]), false, (item) => item.liveCourseId)
} 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){
message.warning('无法继续选择,一个任务最多关联20个课程');
return;
if (_list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程')
return
}
this.setState({selectLive:_list});
this.setState({ selectLive: _list })
}
selectVideoList = (record,selected) =>{
const {selectVideo,currentTaskCourseData,selectLive,selectPicture} = this.state;
let _list = [];
if (selected || !_.find(selectVideo, (item) => item.id == record.id)) {
_list = _.uniq(selectVideo.concat([record]), false, (item) => item.id);
selectVideoList = (record, selected) => {
const { selectVideo, currentTaskCourseData, selectLive, selectPicture, videoCourseDivision } = this.state
let { [videoCourseDivision]: selectList } = selectVideo
let _list = []
if (selected || !_.find(selectList, (item) => item.id === record.id)) {
_list = _.uniq(selectList.concat([record]), false, (item) => item.id)
} 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){
message.warning('无法继续选择,一个任务最多关联20个课程');
return;
if (_list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程')
return
}
this.setState({selectVideo:_list});
this.setState({
selectVideo: {
...selectVideo,
[videoCourseDivision]: _list
}
})
}
selectPictureList = (record,selected) =>{
const {selectVideo,currentTaskCourseData,selectLive,selectPicture} = this.state;
let _list = [];
if (selected || !_.find(selectPicture, (item) => item.id == record.id)) {
_list = _.uniq(selectPicture.concat([record]), false, (item) => item.id);
selectPictureList = (record, selected) => {
const { selectVideo, currentTaskCourseData, selectLive, selectPicture } = this.state
let _list = []
if (selected || !_.find(selectPicture, (item) => item.id === record.id)) {
_list = _.uniq(selectPicture.concat([record]), false, (item) => item.id)
} 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){
message.warning('无法继续选择,一个任务最多关联20个课程');
return;
if (_list.length + currentTaskCourseData.length + selectLive.length + selectVideo.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程')
return
}
this.setState({selectPicture:_list});
this.setState({ selectPicture: _list })
}
clearSelectCourse = ()=>{
clearSelectCourse = () => {
this.setState({
selectLive:[],
selectVideo:[],
selectPicture:[]
selectLive: [],
selectVideo: {
internal: [],
external: []
},
selectPicture: []
})
}
handleSelectVideo = (selectVideo)=>{
return selectVideo.map((item,index)=>{
let _item = {};
_item.courseId = item.id;
_item.courseType = "VOICE";
_item.courseName = item.courseName;
handleSelectVideo = (selectVideo) => {
return selectVideo.map((item) => {
let _item = {}
_item.courseId = item.id
_item.courseType = 'VOICE'
_item.courseName = item.courseName
return _item;
return _item
})
}
handleSelectLive = (selectLive)=>{
return selectLive.map((item,index)=>{
let _item = {};
_item.courseId = item.liveCourseId;
_item.courseType = "LIVE";
_item.courseName = item.courseName;
_item.courseState = item.courseState;
return _item;
handleSelectLive = (selectLive) => {
return selectLive.map((item, index) => {
let _item = {}
_item.courseId = item.liveCourseId
_item.courseType = 'LIVE'
_item.courseName = item.courseName
_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)=>{
return selectPicture.map((item,index)=>{
let _item = {};
_item.courseId = item.id;
_item.courseType = "PICTURE";
_item.courseName = item.courseName;
return _item;
handleSelectPicture = (selectPicture) => {
return selectPicture.map((item, index) => {
let _item = {}
_item.courseId = item.id
_item.courseType = 'PICTURE'
_item.courseName = item.courseName
return _item
})
}
renderFooter = ()=>{
const { activeKey } = this.state;
let href = '';
switch (activeKey){
renderFooter = () => {
const { activeKey } = this.state
let href = ''
switch (activeKey) {
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>
break;
href = (
<a
target='_blank'
rel='noopener noreferrer'
className='link-create-course'
href={window.location.origin + window.location.pathname + '#/create-live-course?type=add'}
onClick={this.props.onClose}>
没有找到需要的直播课?<span>去创建</span>
</a>
)
break
case 'video':
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>
break;
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' className="link-create-course" href={window.location.origin + window.location.pathname + '#/create-graphics-course?type=add'} onClick={this.props.onClose}>没有找到需要的图文课?<span>去创建</span></a>
break;
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() {
const { visible } = this.props;
const { visible } = this.props
const {
liveDataSource,
liveSize,
......@@ -509,130 +638,192 @@ class SelectOperatorModal extends React.Component {
videoDataSource,
videoSize,
videoQuery,
videoSearchDefalt,
videoTotalCount,
selectVideo,
currentTaskCourseData,
activeKey,
selectPicture,
pictureDataSource,
pictureSize,
pictureQuery,
pictureTotalCount,
} = this.state;
videoCourseDivision
} = this.state
return (
<Modal
title="关联课程"
title='关联课程'
onCancel={this.props.onClose}
maskClosable={false}
visible={visible}
className="related-course-modal"
className='related-course-modal'
closable={true}
width={800}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
footer={[
this.renderFooter()
,
// <If condition={videoCourseDivision === 'internal'}>{}</If>,
this.renderFooter(),
<Button
onClick={() => {
this.props.onClose()
}}
>
}}>
取消
</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>
]}
>
]}>
<div>
<Tabs defaultActiveKey="video" onChange={(activeKey)=>{this.setState({activeKey:activeKey})}}>
<TabPane tab="视频课" key="video">
<div className="search-container">
<Search enterButton={<span className="icon iconfont">&#xe832;</span>} placeholder="搜索课程名称" style={{ width: 200 }} onChange={(e) => { this.handleChangVideoCourseName(e.target.value)}} onSearch={ () => { this.handleFetchVideoDataList()}}/>
<Tabs
type='line'
defaultActiveKey='video'
onChange={(activeKey) => {
this.setState({ activeKey: activeKey })
}}>
<TabPane tab='视频课' key='video'>
<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='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 className='select-area'>
<div className='select-box'>
<div>
<span className="icon iconfont tip-icon">&#xe61d;</span>
<span className="select-num">已选择{selectVideo.length + selectLive.length + selectPicture.length }</span>
<span className='icon iconfont tip-icon'>&#xe61d;</span>
<span className='select-num'>
已选择{selectVideo.internal.length + selectVideo.external.length + selectLive.length + selectPicture.length}
</span>
</div>
<div>
<span className="clear-btn" onClick={this.clearSelectCourse}>清空</span>
<span className='clear-btn' onClick={this.clearSelectCourse}>
清空
</span>
</div>
</div>
<div className='related-box'>
该任务已关联{currentTaskCourseData.length}个课程,可继续选择{20 - currentTaskCourseData.length}
</div>
<div className="related-box">该任务已关联{currentTaskCourseData.length}个课程,可继续选择{20- currentTaskCourseData.length }</div>
</div>
<div>
<Table
rowKey={record => record.id}
dataSource={videoDataSource}
rowKey={(record) => record.id}
dataSource={videoDataSource[videoCourseDivision]}
columns={this.parseVideoColumns()}
pagination={false}
bordered
rowSelection={{
type: 'checkbox',
selectedRowKeys: _.pluck(selectVideo, 'id'),
selectedRowKeys: _.pluck(selectVideo[videoCourseDivision], 'id'),
onSelect: (record, selected) => {
this.selectVideoList(record, selected);
this.selectVideoList(record, selected)
},
onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = [];
let _list = []
if (selected) {
_list = _.uniq(selectVideo.concat(changeRows), false, (item) => item.id);
_list = _.uniq(selectVideo[videoCourseDivision].concat(changeRows), false, (item) => item.id)
} else {
_list = _.reject(selectVideo, (item) => _.find(changeRows, (data) => data.id === item.id));
_list = _.reject(selectVideo[videoCourseDivision], (item) => _.find(changeRows, (data) => data.id === item.id))
}
if(_list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length> 20){
message.warning('无法继续选择,一个任务最多关联20个课程');
const extraLength = (_list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length) -20;
_list.splice(_list.length - extraLength,extraLength);
if (_list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程')
const extraLength = _list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length - 20
_list.splice(_list.length - extraLength, extraLength)
}
this.setState({
selectVideo: {
...selectVideo,
[videoCourseDivision]: _list
}
})
}
this.setState({selectVideo:_list});
},
}}
/>
{videoDataSource.length >0 &&
<div className="box-footer">
{videoDataSource[videoCourseDivision].length > 0 && (
<div className='box-footer'>
<PageControl
current={videoQuery.current - 1}
pageSize={videoSize}
size="small"
total={videoTotalCount}
current={videoQuery[videoCourseDivision].current - 1}
pageSize={videoSize[videoCourseDivision]}
size='small'
total={videoTotalCount[videoCourseDivision]}
toPage={(page) => {
const _query = {...videoQuery, current: page + 1};
this.setState({
videoQuery:_query
},()=>{ this.handleFetchVideoDataList()})
const _query = { ...videoQuery[videoCourseDivision], current: page + 1 }
this.setState(
{
videoQuery: {
...videoQuery,
[videoCourseDivision]: _query
}
},
() => {
this.handleFetchVideoDataList()
}
)
}}
onShowSizeChange={this.onShowVideoSizeChange}
/>
</div>
}
)}
</div>
</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()}} />
<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">
<div className="select-box">
<div className='select-area'>
<div className='select-box'>
<div>
<span className="icon iconfont tip-icon">&#xe61d;</span>
<span className="select-num">已选择{selectVideo.length + selectLive.length + selectPicture.length}</span>
<span className='icon iconfont tip-icon'>&#xe61d;</span>
<span className='select-num'>
已选择{selectVideo.internal.length + selectVideo.external.length + selectLive.length + selectPicture.length}
</span>
</div>
<div>
<span className="clear-btn" onClick={this.clearSelectCourse}>清空</span>
<span className='clear-btn' onClick={this.clearSelectCourse}>
清空
</span>
</div>
</div>
<div className="related-box">该任务已关联{currentTaskCourseData.length}个课程,可继续选择{20- currentTaskCourseData.length }</div>
<div className='related-box'>
该任务已关联{currentTaskCourseData.length}个课程,可继续选择{20 - currentTaskCourseData.length}
</div>
</div>
<div>
<Table
rowKey={record => record.liveCourseId}
rowKey={(record) => record.liveCourseId}
dataSource={liveDataSource}
columns={this.parseLiveColumns()}
pagination={false}
......@@ -644,61 +835,81 @@ class SelectOperatorModal extends React.Component {
this.selectLiveList(record, selected)
},
onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = [];
let _list = []
if (selected) {
_list = _.uniq(selectLive.concat(changeRows), false, (item) => item.liveCourseId);
_list = _.uniq(selectLive.concat(changeRows), false, (item) => item.liveCourseId)
} else {
_list = _.reject(selectLive, (item) => _.find(changeRows, (data) => data.liveCourseId === item.liveCourseId));
_list = _.reject(selectLive, (item) => _.find(changeRows, (data) => data.liveCourseId === item.liveCourseId))
}
if(_list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length> 20){
message.warning('无法继续选择,一个任务最多关联20个课程');
const extraLength = (_list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length) -20;
_list.splice(_list.length - extraLength,extraLength);
if (_list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程')
const extraLength = _list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length - 20
_list.splice(_list.length - extraLength, extraLength)
}
this.setState({ selectLive: _list })
}
this.setState({selectLive:_list});
},
}}
/>
{liveDataSource.length >0 &&
<div className="box-footer">
{liveDataSource.length > 0 && (
<div className='box-footer'>
<PageControl
current={liveQuery.current - 1}
pageSize={liveSize}
size="small"
size='small'
total={liveTotalCount}
toPage={(page) => {
const _query = {...liveQuery, current: page + 1};
this.setState({
liveQuery:_query
},()=>{ this.handleFetchLiveDataList()})
const _query = { ...liveQuery, current: page + 1 }
this.setState(
{
liveQuery: _query
},
() => {
this.handleFetchLiveDataList()
}
)
}}
onShowSizeChange={this.onShowLiveSizeChange}
/>
</div>
}
)}
</div>
</TabPane>
<TabPane tab="图文课" key="picture">
<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()}} />
<TabPane tab='图文课' key='picture'>
<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()
}}
/>
</div>
<div className="select-area">
<div className="select-box">
<div className='select-area'>
<div className='select-box'>
<div>
<span className="icon iconfont tip-icon">&#xe61d;</span>
<span className="select-num">已选择{selectVideo.length + selectLive.length + selectPicture.length }</span>
<span className='icon iconfont tip-icon'>&#xe61d;</span>
<span className='select-num'>
已选择{selectVideo.internal.length + selectVideo.external.length + selectLive.length + selectPicture.length}
</span>
</div>
<div>
<span className="clear-btn" onClick={this.clearSelectCourse}>清空</span>
<span className='clear-btn' onClick={this.clearSelectCourse}>
清空
</span>
</div>
</div>
<div className='related-box'>
该任务已关联{currentTaskCourseData.length}个课程,可继续选择{20 - currentTaskCourseData.length}
</div>
<div className="related-box">该任务已关联{currentTaskCourseData.length}个课程,可继续选择{20- currentTaskCourseData.length }</div>
</div>
<div>
<Table
rowKey={record => record.id}
rowKey={(record) => record.id}
dataSource={pictureDataSource}
columns={this.parsePictureColumns()}
pagination={false}
......@@ -710,39 +921,43 @@ class SelectOperatorModal extends React.Component {
this.selectPictureList(record, selected)
},
onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = [];
let _list = []
if (selected) {
_list = _.uniq(selectPicture.concat(changeRows), false, (item) => item.id);
_list = _.uniq(selectPicture.concat(changeRows), false, (item) => item.id)
} 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){
message.warning('无法继续选择,一个任务最多关联20个课程');
const extraLength = (_list.length + currentTaskCourseData.length + selectVideo.length + selectLive.length) -20;
_list.splice(_list.length - extraLength,extraLength);
if (_list.length + currentTaskCourseData.length + selectVideo.length + selectLive.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程')
const extraLength = _list.length + currentTaskCourseData.length + selectVideo.length + selectLive.length - 20
_list.splice(_list.length - extraLength, extraLength)
}
this.setState({ selectPicture: _list })
}
this.setState({selectPicture:_list});
},
}}
/>
{pictureDataSource.length >0 &&
<div className="box-footer">
{pictureDataSource.length > 0 && (
<div className='box-footer'>
<PageControl
current={pictureQuery.current - 1}
pageSize={pictureSize}
size="small"
size='small'
total={pictureTotalCount}
toPage={(page) => {
const _query = {...pictureQuery, current: page + 1};
this.setState({
pictureQuery:_query
},()=>{ this.handleFetchPictureDataList()})
const _query = { ...pictureQuery, current: page + 1 }
this.setState(
{
pictureQuery: _query
},
() => {
this.handleFetchPictureDataList()
}
)
}}
onShowSizeChange={this.onShowPictureSizeChange}
/>
</div>
}
)}
</div>
</TabPane>
</Tabs>
......@@ -752,4 +967,4 @@ class SelectOperatorModal extends React.Component {
}
}
export default SelectOperatorModal;
\ No newline at end of file
export default SelectOperatorModal
.related-course-modal{
.ant-tabs-top > .ant-tabs-nav::before{
border-bottom: 0px;
}
.ant-tabs-nav-list{
margin:0 auto;
}
.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn{
font-weight:normal;
}
.ant-tabs-nav .ant-tabs-tab{
padding:6px 12px !important;
margin:0;
border: 1px solid #E8E8E8;
font-size:14px !important;
color:#999;
.related-course-modal {
.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn {
font-weight: normal;
}
// .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){
border-radius: 4px 0px 0px 4px;
}
&:nth-child(2){
border-radius: 0px 4px 4px 0px;
}
}
// &:nth-child(1) {
// border-radius: 4px 0px 0px 4px;
// }
// &:nth-child(2) {
// border-radius: 0px 4px 4px 0px;
// }
// }
.ant-tabs-nav .ant-tabs-tab-active{
border: 1px solid #FFB714;
color:#FFB714;
}
// .ant-tabs-nav .ant-tabs-tab-active {
// // border: 1px solid #ffb714;
// color: #ffb714;
// }
.ant-tabs-top .ant-tabs-ink-bar-animated:after{
height:0;
}
.link-create-course{
color:#666666;
font-size:14px;
width:638px;
text-align:left;
display:inline-block;
span{
color:#5289FA;
// .ant-tabs-top .ant-tabs-ink-bar-animated:after {
// height: 0;
// }
.link-create-course {
color: #666666;
font-size: 14px;
width: 638px;
text-align: left;
display: inline-block;
span {
color: #5289fa;
}
}
.search-container{
margin-bottom:16px;
.search-container {
margin-bottom: 16px;
}
.select-area{
margin-bottom:12px;
display:flex;
justify-content:space-between;
.select-box{
display:inline-box;
.select-area {
margin-bottom: 12px;
display: flex;
justify-content: space-between;
.select-box {
display: inline-box;
width: 186px;
background: #FFF4DD;
background: #fff4dd;
border-radius: 4px;
padding:6px 16px;
margin-right:8px;
padding: 6px 16px;
margin-right: 8px;
display: flex;
justify-content: space-between;
.tip-icon{
color:#FF9D14;
font-size:14px;
margin-right:4px;
.tip-icon {
color: #ff9d14;
font-size: 14px;
margin-right: 4px;
}
.select-num{
color:#666666;
font-size:14px;
.select-num {
color: #666666;
font-size: 14px;
}
.clear-btn{
text-align:right;
color:#5289FA;
font-size:14px;
.clear-btn {
text-align: right;
color: #5289fa;
font-size: 14px;
}
}
.related-box{
padding:6px 16px;
background: #FFF4DD;
.related-box {
padding: 6px 16px;
background: #fff4dd;
border-radius: 4px;
flex:1;
color:#666666;
font-size:14px;
flex: 1;
color: #666666;
font-size: 14px;
}
}
.course-info{
.course-info {
display: flex;
align-items: center;
.course-cover{
.course-cover {
width: 97px;
height: 55px;
display: inline-block;
border-radius:4px;
margin-right:8px;
border-radius: 4px;
margin-right: 8px;
}
.course-name{
font-size:14px;
color:#666;
.course-name {
font-size: 14px;
color: #666;
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
......@@ -101,15 +95,15 @@
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
width:238px;
width: 180px;
}
.course-status {
font-size:12px;
line-height:18px;
display:inline-block;
border-radius:2px;
padding:0 8px;
margin-top:8px;
font-size: 12px;
line-height: 18px;
display: inline-block;
border-radius: 2px;
padding: 0 8px;
margin-top: 8px;
}
}
}
import React, { useEffect, useState } from 'react';
import {
withRouter
} from 'react-router-dom';
import './Login.less';
import { Input, Popover, message, Tabs, Button } from 'antd';
import CheckBeforeSendCode from '../../components/CheckBeforeSendCode';
import User from '@/common/js/user';
import React, { useEffect, useState } from 'react'
import { withRouter } from 'react-router-dom'
import './Login.less'
import { Input, Popover, message, Tabs, Button } from 'antd'
import CheckBeforeSendCode from '../../components/CheckBeforeSendCode'
import User from '@/common/js/user'
import WechatLogin from './WechatLogin'
import BaseService from "@/domains/basic-domain/baseService";
import axios from 'axios';
import _ from 'underscore';
import user from '@/common/js/user';
const { TabPane } = Tabs;
import BaseService from '@/domains/basic-domain/baseService'
import axios from 'axios'
import _ from 'underscore'
import user from '@/common/js/user'
const { TabPane } = Tabs
function Login(props) {
const [phone, setPhone] = useState(''); // 登录手机号
const [phoneverify, setPhoneverify] = useState(''); // 密码登录验证码
const [openCheck1, setOpenCheck1] = useState(false);
const [checking1, setChecking1] = useState(false);
const [codeText, setCodeText] = useState('获取验证码'); // 验证码提示语
const [waitStatus, setWaitStatus] = useState(false); // 验证码是否在倒计时
const [errorMessage, setErrorMessage] = useState('');
const [phoneError, setPhoneError] = useState(false);
const [checkObject1, setCheckObject1] = useState({});
const [phone, setPhone] = useState('') // 登录手机号
const [phoneverify, setPhoneverify] = useState('') // 密码登录验证码
const [openCheck1, setOpenCheck1] = useState(false)
const [checking1, setChecking1] = useState(false)
const [codeText, setCodeText] = useState('获取验证码') // 验证码提示语
const [waitStatus, setWaitStatus] = useState(false) // 验证码是否在倒计时
const [errorMessage, setErrorMessage] = useState('')
const [phoneError, setPhoneError] = useState(false)
const [checkObject1, setCheckObject1] = useState({})
useEffect(() => {
const enterpriseId = getParameterByName("enterpriseId");
const userId = getParameterByName("userId");
const from = getParameterByName("from");
const storeId = getParameterByName("storeId");
const enterpriseId = getParameterByName('enterpriseId')
const userId = getParameterByName('userId')
const from = getParameterByName('from')
const storeId = getParameterByName('storeId')
if (storeId) {
User.setCustomerStoreId(storeId);
User.setCustomerStoreId(storeId)
}
if (from === 'customer' && enterpriseId && userId) {
if (!user.getToken() || enterpriseId !== user.getEnterpriseId() || userId !== User.getUserId()) {
getWXWorkLoginNoCheck(enterpriseId, userId);
getWXWorkLoginNoCheck(enterpriseId, userId)
} else {
window.RCHistory.push({
pathname: `/switch-route`,
pathname: `/switch-route`
})
}
} else {
User.removeUserId();
User.removeToken();
User.removeEnterpriseId();
User.removeUserId()
User.removeToken()
User.removeEnterpriseId()
}
}, [])
function getWXWorkLoginNoCheck(enterpriseId, userId) {
const params = {
appTermEnum: "XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN",
appTermEnum: 'XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN',
enterpriseId,
userId
}
BaseService.getWXWorkLoginNoCheck(params).then((res) => {
User.setUserId(res.result.loginInfo.userId);
User.setToken(res.result.loginInfo.xmToken);
User.setEnterpriseId(res.result.enterpriseId);
User.setUserId(res.result.loginInfo.userId)
User.setToken(res.result.loginInfo.xmToken)
User.setEnterpriseId(res.result.enterpriseId)
window.RCHistory.push({
pathname: `/switch-route`,
pathname: `/switch-route`
})
})
}
async function checkAccount(code, callback = () => { }) {
callback();
async function checkAccount(code, callback = () => {}) {
callback()
}
function checkSend(code) {
if (!phone) {
setPhoneError(true);
setErrorMessage("请输入手机号");
return;
setPhoneError(true)
setErrorMessage('请输入手机号')
return
}
if (phone.length != 11) {
setPhoneError(true);
setErrorMessage("请输入11位手机号")
return;
setPhoneError(true)
setErrorMessage('请输入11位手机号')
return
}
!_.isEmpty(checkObject1) && checkObject1.reset();
setOpenCheck1(true);
!_.isEmpty(checkObject1) && checkObject1.reset()
setOpenCheck1(true)
}
function handleSendSMSCode(checkData, userType) {
if (waitStatus) return;
let timer;
if (waitStatus) return
let timer
const params = {
phone: phone,
sig: checkData.sig,
sessionId: checkData.csessionid,
token: checkData.token,
scene: 'nc_login',
serverType: "CLOUD_CLASS_LOGIN",
serverType: 'CLOUD_CLASS_LOGIN',
appTermEnum: 'XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN'
}
BaseService.sendLoginAuthCode(params).then((res) => {
if (!res.success) {
setErrorMessage(res.message);
setErrorMessage(res.message)
} else {
timeSub(60);
timeSub(60)
setChecking1(true)
}
})
function timeSub(waitTime, unit) {
clearTimeout(timer);
clearTimeout(timer)
timer = setTimeout(function () {
if (waitTime === 0) {
setCodeText('发送验证码')
setChecking1(false)
setWaitStatus(false)
clearTimeout(timer);
clearTimeout(timer)
} else {
setCodeText(`${waitTime}秒后重发`)
setWaitStatus(true)
timeSub(--waitTime, 1000);
timeSub(--waitTime, 1000)
}
}, unit || 0);
}, unit || 0)
}
}
function handleSubmit() {
if (!phone) {
setPhoneError(true);
setErrorMessage("请输入手机号");
return;
setPhoneError(true)
setErrorMessage('请输入手机号')
return
}
if (phone.length != 11) {
setPhoneError(true);
setErrorMessage("请输入11位手机号")
return;
setPhoneError(true)
setErrorMessage('请输入11位手机号')
return
}
if (!phoneverify) {
setErrorMessage("请输入验证码");
return;
setErrorMessage('请输入验证码')
return
}
const params = {
phone,
authCode: phoneverify,
appTermEnum: "XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN"
appTermEnum: 'XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN'
}
BaseService.login(params).then((res) => {
if (!res.success) {
setErrorMessage(res.message);
setErrorMessage(res.message)
} else {
User.setUserId(res.result.userId);
User.setToken(res.result.xmToken);
User.setUserId(res.result.userId)
User.setToken(res.result.xmToken)
window.RCHistory.push({
pathname: `/switch-route`,
pathname: `/switch-route`
})
}
})
}
return (
<div className="login-page" >
<div className="login-main">
<div className="left-banner">
<div><img src="https://image.xiaomaiketang.com/xm/Newk4NrxKC.png" alt="" style={{ width: 60, height: 61 }} /></div>
<div className="name">小麦企学院</div>
<div className="desc">一键开启直播授课 让知识更有价值</div>
<div className='login-page'>
<div className='login-main'>
<div className='left-banner'>
<div>
<img src='https://image.xiaomaiketang.com/xm/Newk4NrxKC.png' alt='' style={{ width: 60, height: 61 }} />
</div>
<div className="login-box">
<div className="login">
<div className="r">
<Tabs defaultActiveKey="1">
<TabPane tab="企业微信登录" key="1">
<div className='name'>小麦企学院</div>
<div className='desc'>一键开启直播授课 让知识更有价值</div>
</div>
<div className='login-box'>
<div className='login'>
<div className='r'>
<Tabs defaultActiveKey='1'>
<TabPane tab='企业微信登录' key='1'>
<WechatLogin></WechatLogin>
</TabPane>
<TabPane tab="手机号登录" key="2">
<div className="login-form">
<div className="form">
<div className="username" style={{ marginBottom: 16 }}>
<TabPane tab='手机号登录' key='2'>
<div className='login-form'>
<div className='form'>
<div className='username' style={{ marginBottom: 16 }}>
<Input
type="phone"
autoComplete="off"
name="account"
type='phone'
autoComplete='off'
name='account'
maxLength={11}
placeholder="手机号"
placeholder='手机号'
value={phone}
onChange={(e) => { setPhone(e.target.value) }}
onChange={(e) => {
setPhone(e.target.value)
}}
/>
</div>
<div className="error-message">
</div>
<div className="phoneverify">
<div className='error-message'></div>
<div className='phoneverify'>
<Input
type="text"
id="phoneverify"
name="phoneverify"
placeholder="验证码"
autoComplete="off"
type='text'
id='phoneverify'
name='phoneverify'
placeholder='验证码'
autoComplete='off'
value={phoneverify}
maxLength={4}
onChange={(e) => { setPhoneverify(e.target.value) }}
onChange={(e) => {
setPhoneverify(e.target.value)
}}
/>
<Popover
visible={openCheck1}
trigger="click"
title=""
content={<div>
trigger='click'
title=''
content={
<div>
<span style={{ fontSize: '12px', color: '#999', marginBottom: 8, display: 'block' }}>请完成安全验证</span>
<CheckBeforeSendCode
callback={(data, nc) => {
setCheckObject1(nc);
setCheckObject1(nc)
checkAccount(1, (userType) => {
handleSendSMSCode(data, userType);
handleSendSMSCode(data, userType)
setTimeout(() => {
setOpenCheck1(false);
setOpenCheck1(false)
}, 500)
})
}}
/>
</div>}
</div>
}
onVisibleChange={(value) => {
if (!value) {
setOpenCheck1(false);
setOpenCheck1(false)
}
}}
placement="bottomRight"
>
placement='bottomRight'>
<div
className="btn"
id="sendVerifyCode"
className='btn'
id='sendVerifyCode'
onClick={() => {
if (checking1) return;
if (checking1) return
checkSend(1)
}}
>{codeText}</div>
</Popover>
}}>
{codeText}
</div>
<div className="error-message">
{errorMessage}
</Popover>
</div>
<div className="submit">
<div className="btn">
<span id='loginIn' onClick={() => { handleSubmit() }} >登录</span>
<div className='error-message'>{errorMessage}</div>
<div className='submit'>
<div className='btn'>
<span
id='loginIn'
onClick={() => {
handleSubmit()
}}>
登录
</span>
</div>
</div>
</div>
</div>
</TabPane>
</Tabs>
</div>
</div>
......@@ -249,4 +254,4 @@ function Login(props) {
)
}
export default withRouter(Login);
export default withRouter(Login)
......@@ -15,10 +15,11 @@
background-color: #f0f2f5;
overflow-x: scroll;
z-index: 1;
&.right-container-vertical{
left:@xm-left-min-width;
.page{
.page{
height: calc(~'100% - 50px');
&.right-container-vertical {
left: @xm-left-min-width;
.page {
.page {
left: @xm-left-min-width;
}
}
......@@ -41,7 +42,6 @@
}
}
.right-container {
// min-width: 1186px;
&:before {
......@@ -111,7 +111,7 @@
}
&.multiple {
h1{
h1 {
&.on {
border-left: 1px solid @border;
}
......
.wechatLoginBox{
.wechatLoginBox {
height: 320px;
text-align: center;
.text{
.text {
margin-top: 12px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
color: #999999;
line-height: 20px;
}
.rwm {
position: relative;
width: 160px;
height: 160px;
text-align: center;
display: inline-block;
margin-top: 24px;
.error {
position: absolute;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.95);
display: flex;
align-items: center;
justify-content: center;
left: 0px;
top: 0px;
div {
margin: 0 10px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
line-height: 20px;
}
.ope {
cursor: pointer;
color: rgba(82, 137, 250, 1);
}
}
<<<<<<< HEAD
.rwm{
position: relative;
width: 160px;
......@@ -42,33 +72,35 @@
}
}
}
=======
}
>>>>>>> 4ed93ca (feat:新增视频课外部视频模块相关功能,准备开始冒烟测试)
.ant-tabs-tab-active{
.ant-tabs-tab-btn{
.ant-tabs-tab-active {
.ant-tabs-tab-btn {
color: #333333;
}
}
.ant-tabs-tab-btn{
.ant-tabs-tab-btn {
color: #999999;
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
line-height: 25px;
}
.ant-tabs-nav::before{
.ant-tabs-nav::before {
display: none;
}
.ant-tabs-tab{
.ant-tabs-tab {
width: 105px;
text-align: center;
}
.ant-tabs > .ant-tabs-nav .ant-tabs-nav-list{
.ant-tabs > .ant-tabs-nav .ant-tabs-nav-list {
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;
height: 4px;
margin-left: 30px;
......
/*
* @Author: yuananting
* @Date: 2021-02-23 18:28:50
* @LastEditors: yuananting
* @LastEditTime: 2021-04-15 21:53:13
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-21 17:57:59
* @Description: 助学工具-课程分类
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { Component } from "react";
import Breadcrumbs from "@/components/Breadcrumbs";
import "./CourseCategoryManage.less";
import OpearteCourseCategoryModal from "../modal/OpearteCourseCategoryModal";
import AidToolService from "@/domains/aid-tool-domain/AidToolService";
import User from "@/common/js/user";
import {
Tree,
Input,
Space,
Button,
Menu,
Dropdown,
message,
Modal,
} from "antd";
import ShowTips from "@/components/ShowTips";
const { DirectoryTree } = Tree;
const { Search } = Input;
const { confirm } = Modal;
import React, { Component } from 'react'
import Breadcrumbs from '@/components/Breadcrumbs'
import './CourseCategoryManage.less'
import OpearteCourseCategoryModal from '../modal/OpearteCourseCategoryModal'
import AidToolService from '@/domains/aid-tool-domain/AidToolService'
import User from '@/common/js/user'
import { Tree, Input, Space, Button, Menu, Dropdown, message, Modal } from 'antd'
import ShowTips from '@/components/ShowTips'
const { DirectoryTree } = Tree
const { Search } = Input
const { confirm } = Modal
class CourseCategoryManage extends Component {
constructor(props) {
super(props);
super(props)
this.state = {
operateCourseCategoryModal: null, //新增或编辑分类模态框
treeData: [],
originTreeData: [],
treeMap: {},
selectedKeys: ["null"],
autoExpandParent: true,
};
selectedKeys: ['null'],
autoExpandParent: true
}
}
componentDidMount() {
this.queryCategoryTree("init");
this.queryCategoryTree('init')
}
getWholeTree = () => {
let query = {
bizType: "QUESTION",
bizType: 'QUESTION',
count: false,
source: 0,
userId: User.getStoreUserId(),
tenantId: User.getStoreId(),
};
tenantId: User.getStoreId()
}
AidToolService.queryCategoryTree(query).then((res) => {
const { categoryList = [] } = res.result;
const { categoryList = [] } = res.result
this.setState({ originTreeData: categoryList }, () => {
let map = {};
let topItem = [];
let map = {}
let topItem = []
categoryList.forEach((item) => {
topItem.push(item);
});
topItem.push(item)
})
this.setState({
treeMap: Object.assign(this.getTreeMap(categoryList, map), {
0: {
sonCategoryList: topItem,
},
}),
});
});
});
};
sonCategoryList: topItem
}
})
})
})
})
}
// 查询分类树
queryCategoryTree = (operateType, categoryName) => {
this.getWholeTree();
this.setState({ categoryName });
this.getWholeTree()
this.setState({ categoryName })
let query = {
bizType: "QUESTION",
bizType: 'QUESTION',
count: false,
source: 0,
categoryName,
userId: User.getStoreUserId(),
tenantId: User.getStoreId(),
};
tenantId: User.getStoreId()
}
AidToolService.queryCategoryTree(query).then((res) => {
const { categoryList = [] } = res.result;
let str = "未分类";
const { categoryList = [] } = res.result
let str = '未分类'
if (categoryName) {
this.setState({ autoExpandParent: true });
this.setState({ autoExpandParent: true })
if (str.indexOf(categoryName) < 0) {
this.setState({
treeData: this.renderTreeNodes(categoryList, categoryName),
});
let nodeId = [];
treeData: this.renderTreeNodes(categoryList, categoryName)
})
let nodeId = []
Object.keys(this.state.treeMap).forEach((item) => {
nodeId.push(item);
});
this.setState({ expandedKeys: nodeId });
nodeId.push(item)
})
this.setState({ expandedKeys: nodeId })
} else {
const defaultNode = {
id: "null",
categoryName: "未分类",
id: 'null',
categoryName: '未分类',
categoryCount: 0,
parentId: "0",
categoryLevel: 0,
};
categoryList.unshift(defaultNode);
parentId: '0',
categoryLevel: 0
}
categoryList.unshift(defaultNode)
this.setState({
treeData: this.renderTreeNodes(categoryList, categoryName),
});
let nodeId = [];
treeData: this.renderTreeNodes(categoryList, categoryName)
})
let nodeId = []
Object.keys(this.state.treeMap).forEach((item) => {
nodeId.push(item);
});
if (operateType === "init") {
this.setState({ expandedKeys: nodeId });
nodeId.push(item)
})
if (operateType === 'init') {
this.setState({ expandedKeys: nodeId })
}
}
} else {
this.setState({ autoExpandParent: false });
this.setState({ autoExpandParent: false })
const defaultNode = {
id: "null",
categoryName: "未分类",
id: 'null',
categoryName: '未分类',
categoryCount: 0,
parentId: "0",
categoryLevel: 0,
};
categoryList.unshift(defaultNode);
this.setState({ treeData: this.renderTreeNodes(categoryList, categoryName) });
if (operateType === "init") {
this.setState({ expandedKeys: [] });
parentId: '0',
categoryLevel: 0
}
categoryList.unshift(defaultNode)
this.setState({ treeData: this.renderTreeNodes(categoryList, categoryName) })
if (operateType === 'init') {
this.setState({ expandedKeys: [] })
}
}
});
};
})
}
// 树节点渲染-内容处理
renderTreeNodes = (data, value) => {
let newTreeData = data.map((item) => {
item.title = item.categoryName;
item.key = item.id;
item.title = item.categoryName
item.key = item.id
item.title = (
<div
style={{
opacity:
!value || (value && item.categoryName.indexOf(value) > -1)
? 1
: 0.5,
opacity: !value || (value && item.categoryName.indexOf(value) > -1) ? 1 : 0.5
}}
className="node-title-div"
className='node-title-div'
onMouseOver={(e) => {
let mouseNodeOpts = e.currentTarget.getElementsByTagName("div")[0];
let mouseNodeOpts = e.currentTarget.getElementsByTagName('div')[0]
if (mouseNodeOpts) {
mouseNodeOpts.style.visibility = "visible";
mouseNodeOpts.style.visibility = 'visible'
}
}}
onMouseOut={(e) => {
let mouseNodeOpts = e.currentTarget.getElementsByTagName("div")[0];
let mouseNodeOpts = e.currentTarget.getElementsByTagName('div')[0]
if (mouseNodeOpts) {
mouseNodeOpts.style.visibility = "hidden";
mouseNodeOpts.style.visibility = 'hidden'
}
}}
>
}}>
<span>{item.categoryName}</span>
{item.categoryName !== "未分类" && (
<Space className="title-opts" size={16}>
{item.categoryName !== '未分类' && (
<Space className='title-opts' size={16}>
<span
onClick={() => {
let nodesCount = 0;
const { originTreeData } = this.state;
console.log("orororo", originTreeData);
let nodesCount = 0
const { originTreeData } = this.state
console.log('orororo', originTreeData)
if (
(item.categoryLevel === 0 && originTreeData.length >= 29) ||
(item.categoryLevel > 0 &&
this.getRelatedNodes(item.parentId).length >= 30)
(item.categoryLevel > 0 && this.getRelatedNodes(item.parentId).length >= 30)
) {
return message.info("最多只能添加30个分类");
}
this.newEditCourseCategory(
"newEqualLevelCategory",
"equal",
"new",
item
);
}}
>
<span className="icon iconfont" style={{ color: "#BFBFBF" }}>
&#xe7f5;{" "}
return message.info('最多只能添加30个分类')
}
this.newEditCourseCategory('newEqualLevelCategory', 'equal', 'new', item)
}}>
<span className='icon iconfont' style={{ color: '#BFBFBF' }}>
&#xe7f5;{' '}
</span>
<span>同级</span>
</span>
{item.categoryLevel < 4 && (
<span
onClick={() => {
if (
this.getRelatedNodes(item.id) &&
this.getRelatedNodes(item.id).length >= 30
) {
message.info("最多只能添加30个子分类");
return;
}
this.newEditCourseCategory(
"newChildLevelCategory",
"child",
"new",
item
);
}}
>
<span className="icon iconfont" style={{ color: "#BFBFBF" }}>
&#xe7f8;{" "}
if (this.getRelatedNodes(item.id) && this.getRelatedNodes(item.id).length >= 30) {
message.info('最多只能添加30个子分类')
return
}
this.newEditCourseCategory('newChildLevelCategory', 'child', 'new', item)
}}>
<span className='icon iconfont' style={{ color: '#BFBFBF' }}>
&#xe7f8;{' '}
</span>
<span>子级</span>
</span>
)}
<Dropdown overlay={this.initDropMenu(item)}>
<span>
<span className="icon iconfont" style={{ color: "#BFBFBF" }}>
&#xe7f7;{" "}
<span className='icon iconfont' style={{ color: '#BFBFBF' }}>
&#xe7f7;{' '}
</span>
<span>更多</span>
</span>
......@@ -224,77 +195,71 @@ class CourseCategoryManage extends Component {
</Space>
)}
</div>
);
)
item.icon =
item.categoryName === "未分类" ? (
item.categoryName === '未分类' ? (
<img
style={{
width: "24px",
height: "24px",
opacity:
!value || (value && item.categoryName.indexOf(value) > -1)
? 1
: 0.5,
width: '24px',
height: '24px',
opacity: !value || (value && item.categoryName.indexOf(value) > -1) ? 1 : 0.5
}}
src="https://image.xiaomaiketang.com/xm/defaultCategory.png"
alt=""
src='https://image.xiaomaiketang.com/xm/defaultCategory.png'
alt=''
/>
) : (
<img
style={{
width: "24px",
height: "24px",
opacity:
!value || (value && item.categoryName.indexOf(value) > -1)
? 1
: 0.5,
width: '24px',
height: '24px',
opacity: !value || (value && item.categoryName.indexOf(value) > -1) ? 1 : 0.5
}}
src="https://image.xiaomaiketang.com/xm/hasCategory.png"
alt=""
src='https://image.xiaomaiketang.com/xm/hasCategory.png'
alt=''
/>
);
)
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) => {
data.forEach((item) => {
map[item.id] = item;
map[item.id] = item
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) => {
let title = "";
let label = "";
let title = ''
let label = ''
switch (categoryType) {
case "newEqualLevelCategory":
title = "新增分类";
label = "分类名称";
break;
case "newChildLevelCategory":
title = "新增子分类";
label = "子分类名称";
break;
case "editEqualLevelCategory":
title = "编辑分类";
label = "分类名称";
break;
case "editChildLevelCategory":
title = "编辑子分类";
label = "子分类名称";
break;
case 'newEqualLevelCategory':
title = '新增分类'
label = '分类名称'
break
case 'newChildLevelCategory':
title = '新增子分类'
label = '子分类名称'
break
case 'editEqualLevelCategory':
title = '编辑分类'
label = '分类名称'
break
case 'editChildLevelCategory':
title = '编辑子分类'
label = '子分类名称'
break
}
const m = (
<OpearteCourseCategoryModal
......@@ -304,336 +269,273 @@ class CourseCategoryManage extends Component {
title={title}
label={label}
close={() => {
this.queryCategoryTree("remain", this.state.categoryName);
this.queryCategoryTree('remain', this.state.categoryName)
this.setState({
operateCourseCategoryModal: null,
});
operateCourseCategoryModal: null
})
}}
/>
);
this.setState({ operateCourseCategoryModal: m });
};
)
this.setState({ operateCourseCategoryModal: m })
}
// 删除分类
delCategory = (item) => {
return confirm({
title: "确认删除该分类吗?",
content: "删除后,分类下的所有内容将自动转入“未分类”中。",
icon: (
<span className="icon iconfont default-confirm-icon">&#xe839; </span>
),
okText: "删除",
okType: "danger",
cancelText: "取消",
title: '确认删除该分类吗?',
content: '删除后,分类下的所有内容将自动转入“未分类”中。',
icon: <span className='icon iconfont default-confirm-icon'>&#xe839; </span>,
okText: '删除',
okType: 'danger',
cancelText: '取消',
onOk: () => {
let params = {
categoryId: item.id,
source: 0,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
};
userId: User.getStoreUserId()
}
AidToolService.delCategory(params).then((res) => {
if (res.success) {
message.success("删除分类成功");
this.queryCategoryTree("remain", this.state.categoryName);
message.success('删除分类成功')
this.queryCategoryTree('remain', this.state.categoryName)
}
})
}
})
}
});
},
});
};
// 更多操作-【重命名 删除】
initDropMenu = (item) => {
return (
<Menu>
<Menu.Item key="0">
<Menu.Item key='0'>
<span
onClick={() => {
let categoryType =
item.categoryLevel === 0
? "editEqualLevelCategory"
: "editChildLevelCategory";
this.newEditCourseCategory(categoryType, "equal", "edit", item);
}}
>
let categoryType = item.categoryLevel === 0 ? 'editEqualLevelCategory' : 'editChildLevelCategory'
this.newEditCourseCategory(categoryType, 'equal', 'edit', item)
}}>
重命名
</span>
</Menu.Item>
<Menu.Item key="1">
<Menu.Item key='1'>
<span
onClick={() => {
this.delCategory(item);
}}
>
this.delCategory(item)
}}>
删除
</span>
</Menu.Item>
</Menu>
);
};
)
}
// 获取相关节点
getRelatedNodes = (parentId) => {
return this.state.treeMap[parentId]
? this.state.treeMap[parentId].sonCategoryList
: [];
};
return this.state.treeMap[parentId] ? this.state.treeMap[parentId].sonCategoryList : []
}
// 获取拖拽目标父节点层级
getParentDragNodesLevel = (dragNode) => {
if (!dragNode) {
return [];
return []
}
let dragNodes = [];
dragNodes.push(dragNode.id);
let dragNodes = []
dragNodes.push(dragNode.id)
if (dragNode.parentId != 0) {
dragNodes = dragNodes.concat(
this.getParentDragNodesLevel(this.state.treeMap[dragNode.parentId])
);
dragNodes = dragNodes.concat(this.getParentDragNodesLevel(this.state.treeMap[dragNode.parentId]))
}
return dragNodes
}
return dragNodes;
};
// 获取拖拽节点层级
getDragNodesLevel = (dragNode) => {
let dragNodes = [];
let dragNodes = []
if (dragNode.sonCategoryList && dragNode.sonCategoryList.length > 0) {
dragNode.sonCategoryList.forEach((item) => {
dragNodes.push(item.categoryLevel);
dragNodes.push(item.categoryLevel)
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) => {
if (this.state.categoryName) {
return;
return
}
// 未分类不可以拖拽
if (
info.dragNode.categoryName === "未分类" &&
info.dragNode.categoryLevel === 0
)
return message.info("未分类”为默认分类暂不支持移动");
if (info.dragNode.categoryName === '未分类' && info.dragNode.categoryLevel === 0) return message.info('未分类”为默认分类暂不支持移动')
// 不允许其他节点拖拽到未分类之前
if (
info.node.categoryName === "未分类" &&
info.dropToGap &&
info.dropPosition === -1
)
return;
if (info.node.categoryName === '未分类' && info.dropToGap && info.dropPosition === -1) return
let targetParentId = info.dropToGap ? info.node.parentId : info.node.id;
let relatedNodes = this.getRelatedNodes(targetParentId);
if (
!(
(info.dropToGap && info.node.parentId === info.dragNode.parentId) ||
(!info.dropToGap && info.node.id === info.dragNode.parentId)
)
) {
let targetParentId = info.dropToGap ? info.node.parentId : info.node.id
let relatedNodes = this.getRelatedNodes(targetParentId)
if (!((info.dropToGap && info.node.parentId === info.dragNode.parentId) || (!info.dropToGap && info.node.id === info.dragNode.parentId))) {
if (this.state.treeMap[targetParentId].categoryLevel === 4) {
return message.info("最多支持5级分类");
return message.info('最多支持5级分类')
} else {
let nodesArr = this.getDragNodesLevel(
this.state.treeMap[info.dragNode.id]
);
let parentArr = this.getParentDragNodesLevel(
this.state.treeMap[targetParentId]
);
let nodesArr = this.getDragNodesLevel(this.state.treeMap[info.dragNode.id])
let parentArr = this.getParentDragNodesLevel(this.state.treeMap[targetParentId])
if (nodesArr.length + parentArr.length > 4) {
console.log(nodesArr.length, parentArr.length);
return message.info("最多支持5级分类");
console.log(nodesArr.length, parentArr.length)
return message.info('最多支持5级分类')
}
}
if (relatedNodes && relatedNodes.length >= 30) {
return message.info("最多只能添加30个分类");
return message.info('最多只能添加30个分类')
}
}
const dropKey = info.node.key;
const dragKey = info.dragNode.key;
const dropPos = info.node.pos.split("-");
const dropPosition =
info.dropPosition - Number(dropPos[dropPos.length - 1]);
const dropKey = info.node.key
const dragKey = info.dragNode.key
const dropPos = info.node.pos.split('-')
const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1])
const loop = (data, key, callback) => {
for (let i = 0; i < data.length; i++) {
if (data[i].key === key) {
return callback(data[i], i, data);
return callback(data[i], i, data)
}
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) {
if (relatedNodes && relatedNodes.length > 0) {
let sameNameNodes = [];
let sameNameNodes = []
relatedNodes.forEach((item) => {
if (item.id === info.dragNode.id) return true;
if (item.id === info.dragNode.id) return true
if (item.categoryName === name) {
sameNameNodes.push(item);
sameNameNodes.push(item)
}
});
})
if (sameNameNodes.length > 0) {
sufIndex++;
return getSuf(
originCategoryName + `(${sufIndex})`,
originCategoryName,
sufIndex
);
sufIndex++
return getSuf(originCategoryName + `(${sufIndex})`, originCategoryName, sufIndex)
}
}
return sufIndex
}
return sufIndex;
};
let dragObj;
let dragObj
loop(data, dragKey, (item, index, arr) => {
arr.splice(index, 1);
item.parentId = targetParentId;
arr.splice(index, 1)
item.parentId = targetParentId
if (item.originCategoryName) {
item.categoryName = item.originCategoryName;
item.categoryName = item.originCategoryName
} else {
item.originCategoryName = item.categoryName;
}
info.dragNode.categoryName = item.originCategoryName;
let sufIndex = getSuf(
info.dragNode.categoryName,
item.originCategoryName,
0
);
item.categoryName = item.categoryName + (sufIndex ? `(${sufIndex})` : "");
item.categoryName =
item.originCategoryName + (sufIndex ? `(${sufIndex})` : "");
dragObj = item;
});
item.originCategoryName = item.categoryName
}
info.dragNode.categoryName = item.originCategoryName
let sufIndex = getSuf(info.dragNode.categoryName, item.originCategoryName, 0)
item.categoryName = item.categoryName + (sufIndex ? `(${sufIndex})` : '')
item.categoryName = item.originCategoryName + (sufIndex ? `(${sufIndex})` : '')
dragObj = item
})
if (!info.dropToGap) {
loop(data, dropKey, (item) => {
item.sonCategoryList = item.sonCategoryList || [];
item.sonCategoryList.unshift(dragObj);
});
} else if (
(info.node.props.sonCategoryList || []).length > 0 &&
info.node.props.expanded &&
dropPosition === 1
) {
item.sonCategoryList = item.sonCategoryList || []
item.sonCategoryList.unshift(dragObj)
})
} else if ((info.node.props.sonCategoryList || []).length > 0 && info.node.props.expanded && dropPosition === 1) {
loop(data, dropKey, (item) => {
item.sonCategoryList = item.children || [];
item.sonCategoryList.unshift(dragObj);
});
item.sonCategoryList = item.children || []
item.sonCategoryList.unshift(dragObj)
})
} else {
let ar;
let i;
let ar
let i
loop(data, dropKey, (item, index, arr) => {
ar = arr;
i = index;
});
ar = arr
i = index
})
if (dropPosition === -1) {
ar.splice(i, 0, dragObj);
ar.splice(i, 0, dragObj)
} else {
ar.splice(i + 1, 0, dragObj);
ar.splice(i + 1, 0, dragObj)
}
}
data.shift();
let newTreeData = this.renderTreeNodes(this.handleLoop(data, 0));
this.setState({ treeData: newTreeData });
data.shift()
let newTreeData = this.renderTreeNodes(this.handleLoop(data, 0))
this.setState({ treeData: newTreeData })
let params = {
categoryList: newTreeData,
source: 0,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
};
userId: User.getStoreUserId()
}
AidToolService.editCategoryTree(params).then((res) => {
this.queryCategoryTree("remain");
});
};
this.queryCategoryTree('remain')
})
}
handleLoop = (data, level) => {
data.map((item, index) => {
item.sort = index;
item.categoryLevel = level;
item.sort = index
item.categoryLevel = level
if (item.sonCategoryList) {
item.children = this.handleLoop(item.sonCategoryList, level + 1);
item.sonCategoryList = this.handleLoop(item.sonCategoryList, level + 1);
item.children = this.handleLoop(item.sonCategoryList, level + 1)
item.sonCategoryList = this.handleLoop(item.sonCategoryList, level + 1)
}
return item
})
return data
}
return item;
});
return data;
};
// 树状展开事件
onExpand = (expandedKeys) => {
this.setState({ expandedKeys });
};
this.setState({ expandedKeys })
}
// 树状选中事件
onSelect = (selectedKeys) => {
this.setState({ selectedKeys });
};
this.setState({ selectedKeys })
}
render() {
const {
treeData,
originTreeData,
expandedKeys,
selectedKeys,
autoExpandParent,
operateCourseCategoryModal,
} = this.state;
const { treeData, originTreeData, expandedKeys, selectedKeys, autoExpandParent, operateCourseCategoryModal } = this.state
return (
<div className="page course-category-manage">
{["aid", "knowledge"].includes(getParameterByName("from")) ? (
<Breadcrumbs
navList="课程分类"
goBack={() =>
window.RCHistory.goBack()
}
/>
<div className='page course-category-manage'>
{['aid', 'knowledge'].includes(getParameterByName('from')) ? (
<Breadcrumbs navList='课程分类' goBack={() => window.RCHistory.goBack()} />
) : (
<div className="content-header">课程分类</div>
<div className='content-header'>课程分类</div>
)}
<div className="box">
<div className="search-condition">
<span className="search-label">搜索名称:</span>
<div className='box'>
<div className='search-condition'>
<span className='search-label'>搜索名称:</span>
<Search
placeholder="请输入名称"
style={{ width: "300px" }}
onSearch={(value) => this.queryCategoryTree("init", value)}
className="search-input"
enterButton={<span className="icon iconfont">&#xe832;</span>}
placeholder='请输入名称'
style={{ width: '300px' }}
onSearch={(value) => this.queryCategoryTree('init', value)}
className='search-input'
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/>
</div>
<Button
type="primary"
type='primary'
onClick={() => {
if (originTreeData.length >= 29) {
message.info("最多只能添加30个分类");
return;
}
this.newEditCourseCategory(
"newEqualLevelCategory",
"equal",
"new"
);
}}
>
message.info('最多只能添加30个分类')
return
}
this.newEditCourseCategory('newEqualLevelCategory', 'equal', 'new')
}}>
新增一级分类
</Button>
<div
className="show-tips"
style={{ marginTop: "12px", width: "900px" }}
>
<ShowTips message="为方便管理,该分类用于课程、培训计划、题库、知识库等模块,改动将同步各模块更新" />
<div className='show-tips' style={{ marginTop: '12px', width: '900px' }}>
<ShowTips message='为方便管理,该分类用于课程、培训计划、题库、知识库等模块,改动将同步各模块更新' />
</div>
<div className="course-category-tree">
<div className='course-category-tree'>
<DirectoryTree
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
......@@ -643,14 +545,13 @@ class CourseCategoryManage extends Component {
draggable
blockNode
onDrop={this.onDrop}
treeData={treeData}
></DirectoryTree>
treeData={treeData}></DirectoryTree>
</div>
</div>
{operateCourseCategoryModal}
</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