Commit 00b7ddf6 by zhangleyuan

feat:解决合并代码的冲突

parents c55aca42 82d45b29
'use strict'
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const resolve = require('resolve');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const safePostCssParser = require('postcss-safe-parser');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const paths = require('./paths');
const modules = require('./modules');
const getClientEnvironment = require('./env');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin');
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
const fs = require('fs')
const 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));
var PREFIX = 'b-'; //static 文件夹前缀
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
......@@ -73,11 +70,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
......@@ -92,32 +89,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'),
......@@ -127,14 +124,14 @@ module.exports = function (webpackEnv) {
'primary-color': '#2966FF',
'border-radius-base': '2px',
},
javascriptEnabled: true
}
}
javascriptEnabled: true,
},
},
}
)
);
}
return loaders
}
return loaders;
};
return {
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
......@@ -156,7 +153,7 @@ module.exports = function (webpackEnv) {
isEnvDevelopment && require.resolve('webpack/hot/dev-server'),
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.
......@@ -168,11 +165,11 @@ 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 ? PREFIX + 'static/js/[name].[contenthash:8].js' : isEnvDevelopment && PREFIX + '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 ? PREFIX + 'static/js/[name].[contenthash:8].chunk.js' : isEnvDevelopment && PREFIX + 'static/js/[name].chunk.js',
// webpack uses `publicPath` to determine where the app is being served from.
// 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.
......@@ -189,7 +186,7 @@ module.exports = function (webpackEnv) {
globalObject: 'this',
library: appPackageJson.name,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${appPackageJson.name}`
jsonpFunction: `webpackJsonp_${appPackageJson.name}`,
},
optimization: {
minimize: isEnvProduction,
......@@ -203,7 +200,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,
......@@ -217,10 +214,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,
......@@ -230,10 +227,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({
......@@ -246,28 +243,28 @@ 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.
......@@ -290,9 +287,9 @@ module.exports = function (webpackEnv) {
// 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
......@@ -303,15 +300,15 @@ module.exports = function (webpackEnv) {
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// 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,
......@@ -330,12 +327,12 @@ module.exports = function (webpackEnv) {
cache: true,
formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
resolvePluginsRelativeTo: __dirname
resolvePluginsRelativeTo: __dirname,
},
loader: require.resolve('eslint-loader')
}
loader: require.resolve('eslint-loader'),
},
],
include: paths.appSrc
include: paths.appSrc,
},
{
// "oneOf" will traverse all following loaders until one will
......@@ -350,8 +347,8 @@ module.exports = function (webpackEnv) {
loader: require.resolve('url-loader'),
options: {
limit: imageInlineSizeLimit,
name: 'static/media/[name].[hash:8].[ext]'
}
name: PREFIX + 'static/media/[name].[hash:8].[ext]',
},
},
// Process application JS with Babel.
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
......@@ -368,11 +365,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/
......@@ -380,8 +377,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.
......@@ -402,8 +399,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.
......@@ -417,13 +414,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
......@@ -433,9 +430,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
......@@ -446,11 +443,11 @@ module.exports = function (webpackEnv) {
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'less-loader'
),
sideEffects: true
sideEffects: true,
},
{
test: lessModuleRegex,
......@@ -459,10 +456,10 @@ module.exports = function (webpackEnv) {
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent
getLocalIdent: getCSSModuleLocalIdent,
},
'less-loader'
)
),
},
// "file" loader makes sure those assets get served by WebpackDevServer.
// When you `import` an asset, you get its (virtual) filename.
......@@ -477,14 +474,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: PREFIX + '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.
......@@ -493,7 +490,7 @@ module.exports = function (webpackEnv) {
{},
{
inject: true,
template: paths.appHtml
template: paths.appHtml,
},
isEnvProduction
? {
......@@ -507,8 +504,8 @@ module.exports = function (webpackEnv) {
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
}
minifyURLs: true,
},
}
: undefined
)
......@@ -547,8 +544,8 @@ module.exports = function (webpackEnv) {
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'
filename: PREFIX + 'static/css/[name].[contenthash:8].css',
chunkFilename: PREFIX + '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
......@@ -561,16 +558,16 @@ 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
......@@ -594,14 +591,14 @@ 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,
......@@ -612,11 +609,8 @@ module.exports = function (webpackEnv) {
reportFiles: ['**', '!**/__tests__/**', '!**/?(*.)(spec|test).*', '!**/src/setupProxy.*', '!**/src/setupTests.*'],
silent: true,
// The formatter is invoked directly in WebpackDevServerUtils during development
formatter: isEnvProduction ? typescriptFormatter : undefined
})
// new vConsolePlugin({
// enable: (process.env.DEPLOY_ENV === 'prod' || process.env.DEPLOY_ENV === 'beta') ? false : true
// })
formatter: isEnvProduction ? typescriptFormatter : undefined,
}),
].filter(Boolean),
// Some libraries import Node modules but don't use them in the browser.
// Tell webpack to provide empty mocks for them so importing them works.
......@@ -628,10 +622,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,
};
};
......@@ -110,6 +110,7 @@
"start:dev1": "cross-env DEPLOY_ENV=dev node scripts/start.js",
"start:rc": "cross-env DEPLOY_ENV=rc node scripts/start.js",
"start:gray": "cross-env DEPLOY_ENV=gray node scripts/start.js",
"start:prod": "cross-env DEPLOY_ENV=prod node scripts/start.js",
"build:dev": "cross-env DEPLOY_ENV=dev node scripts/build.js",
"build:dev1": "cross-env DEPLOY_ENV=dev node scripts/build.js",
"build:rc": "cross-env DEPLOY_ENV=rc node scripts/build.js",
......@@ -143,7 +144,6 @@
"@types/ali-oss": "^6.0.5",
"@types/jquery": "^3.5.4",
"ali-oss": "^6.12.0",
"react-sortable-hoc": "^1.11.0",
"vconsole-webpack-plugin": "^1.5.2"
"react-sortable-hoc": "^1.11.0"
}
}
/*
* @Author: 吴文洁
* @Date: 2020-08-31 09:34:31
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-06-24 19:34:21
* @LastEditors: yuananting
* @LastEditTime: 2021-07-12 17:40:26
* @Description:
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
......@@ -115,6 +115,10 @@ class Axios {
window.RCHistory.replace('/login');
return Promise.reject();
break;
case 504:
message.error('网络状况不稳定,如果出现数据异常,请刷新页面');
Promise.reject();
break;
default:
message.error(error.message);
break;
......
......@@ -6,7 +6,7 @@
*/
@import './variables.less';
@active-color: #2966FF;
@active-color: #2966ff;
// 消息提示框
.ant-message {
......@@ -41,6 +41,9 @@
.ant-switch-small:after {
top: 0.3px;
}
tbody .ant-switch-small {
min-width: 32px;
}
.ant-pagination {
display: inline !important;
......@@ -234,7 +237,7 @@
}
.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn {
font-weight: 500!important;
font-weight: 500 !important;
}
.ant-tabs-nav .ant-tabs-tab-active {
......@@ -264,7 +267,7 @@
position: absolute;
width: 30px;
height: 4px;
background: #2966FF !important;
background: #2966ff !important;
left: 50%;
transform: translateX(-50%);
}
......@@ -339,14 +342,14 @@ mr0 {
background: #f3f6fa;
.ant-select-selected-icon {
color:#2966FF !important;
color: #2966ff !important;
}
}
.ant-select-dropdown-menu-item-selected {
background: none;
font-weight: 400 !important;
color:#2966FF;
color: #2966ff;
}
.ant-select-open .ant-select-selection {
......@@ -444,7 +447,7 @@ mr0 {
.ant-modal {
.ant-input {
&:focus {
border: 1px solid #2966FF !important;
border: 1px solid #2966ff !important;
}
}
}
......@@ -457,8 +460,8 @@ mr0 {
.ant-calendar-footer-extra {
.ant-tag-blue {
background: #fff;
color: #2966FF;
border-color: #2966FF;
color: #2966ff;
border-color: #2966ff;
}
}
}
......@@ -474,7 +477,7 @@ mr0 {
}
.ant-modal-confirm-info .ant-modal-confirm-body > .anticon {
color: #2966FF!important;
color: #2966ff !important;
}
.ant-breadcrumb > span:last-child .ant-breadcrumb-separator {
......@@ -502,7 +505,7 @@ mr0 {
font-size: 22px !important;
line-height: 22px !important;
float: left !important;
color:#2966FF !important;
color: #2966ff !important;
margin-right: 16px !important;
&.blue {
color: #5cbaff !important;
......@@ -527,8 +530,8 @@ mr0 {
float: left;
margin-right: 16px;
}
>.confirm-icon{
color: #2966FF;
> .confirm-icon {
color: #2966ff;
}
.ant-modal-confirm-content {
margin-left: 38px;
......@@ -594,9 +597,9 @@ mr0 {
// background: #FAFAFA !important;
// }
.ant-table-thead > tr > th{
font-weight:bold !important;
color:#333 !important;
.ant-table-thead > tr > th {
font-weight: bold !important;
color: #333 !important;
}
td.ant-table-column-sort {
background: none;
......@@ -657,11 +660,11 @@ td.ant-table-column-sort {
border-color: @xm-color-text-select-primary !important;
border: none !important;
&:hover {
background-color: #2966FF !important;
background-color: #2966ff !important;
opacity: 0.8 !important;
}
&:active{
background-color:#5C8AFF !important;
&:active {
background-color: #5c8aff !important;
}
}
......@@ -720,9 +723,9 @@ td.ant-table-column-sort {
border-right: 0px !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 {
......@@ -774,4 +777,3 @@ td.ant-table-column-sort {
.ant-table-column-sorter {
margin-left: 8px !important;
}
......@@ -111,9 +111,8 @@ class CollegeInfoPage extends React.Component {
} = this.state;
return (
<div className="page college-info-page">
<div className="content-header">学院基本信息</div>
<div className="content-header">学院信息</div>
<div className="box">
<div className="college-info-header">学院基本信息</div>
<div className="college-info-page-form">
<Form ref={this.formRef}>
......
......@@ -125,7 +125,7 @@ class LiveCourseList extends React.Component {
columns = [
{
title: '直播课',
width: '23%',
key: 'course',
fixed: 'left',
dataIndex: 'courseName',
......@@ -284,6 +284,7 @@ class LiveCourseList extends React.Component {
render: (val, item, index) => {
return (
<Switch
size='small'
checked={item.shelfState === 'YES'}
defaultChecked={item.shelfState === 'YES' ? true : false}
onChange={(checked) => this.changeShelfState(index, item, checked)}
......@@ -311,7 +312,7 @@ class LiveCourseList extends React.Component {
return (
<div className='related-task'>
<Choose>
<When condition={record.relatedPlanLis}>
<When condition={record.relatedPlanList}>
<Tooltip title={this.handlePlanName(record.relatedPlanList)} placement='top' arrowPointAtCenter>
{record.relatedPlanList.map((item, index) => {
return (
......
......@@ -9,7 +9,7 @@
import User from '@/common/js/user';
import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components';
import { appId, LIVE_SHARE } from '@/domains/course-domain/constants';
import { LIVE_SHARE } from '@/domains/course-domain/constants';
import CourseService from '@/domains/course-domain/CourseService';
import ShareLiveModal from '@/modules/course-manage/modal/ShareLiveModal';
import { Dropdown, message, Modal, Switch, Tooltip } from 'antd';
......@@ -57,7 +57,7 @@ class GraphicsCourseList extends React.Component {
handlePlanName = (planArray) => {
let planStr = '';
planArray.map((item, index) => {
planArray.forEach((item, index) => {
if (index < planArray.length - 1) {
planStr = planStr + item.planName + '、';
} else {
......@@ -76,18 +76,21 @@ class GraphicsCourseList extends React.Component {
width: 321,
fixed: 'left',
render: (val, record) => {
const { coverUrl, scheduleVideoUrl } = record;
const { coverUrl } = record;
return (
<div className='record__item'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
<img className='course-cover' src={coverUrl || defaultCoverUrl} />
{record.courseName.length > 25 ? (
<Tooltip title={record.courseName}>
<img className='course-cover' src={coverUrl || defaultCoverUrl} alt='' />
<Choose>
<When condition={record.courseName.length > 25}>
<Tooltip title={record.courseName}>
<div className='course-name'>{record.courseName}</div>
</Tooltip>
</When>
<Otherwise>
<div className='course-name'>{record.courseName}</div>
</Tooltip>
) : (
<div className='course-name'>{record.courseName}</div>
)}
</Otherwise>
</Choose>
</div>
);
},
......@@ -144,12 +147,14 @@ class GraphicsCourseList extends React.Component {
width: 120,
dataIndex: 'courseware',
render: (val, item, index) => {
return (
<Switch
checked={item.shelfState === "YES"}
defaultChecked={item.shelfState==="YES"?true:false}
onChange={(checked)=>this.changeShelfState(index,item,checked)}/>
)
return (
<Switch
size='small'
checked={item.shelfState === 'YES'}
defaultChecked={item.shelfState === 'YES' ? true : false}
onChange={(checked) => this.changeShelfState(index, item, checked)}
/>
);
},
},
{
......@@ -168,7 +173,7 @@ class GraphicsCourseList extends React.Component {
dataIndex: 'created',
sorter: true,
render: (val) => {
return formatDate('YYYY-MM-DD H:i', val);
return window.formatDate('YYYY-MM-DD H:i', val);
},
},
{
......@@ -178,7 +183,7 @@ class GraphicsCourseList extends React.Component {
dataIndex: 'updated',
sorter: true,
render: (val) => {
return formatDate('YYYY-MM-DD H:i', val);
return window.formatDate('YYYY-MM-DD H:i', val);
},
},
{
......@@ -189,19 +194,22 @@ class GraphicsCourseList extends React.Component {
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>
);
})}
</Tooltip>
) : (
<span></span>
)}
<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>
);
},
......@@ -297,7 +305,7 @@ class GraphicsCourseList extends React.Component {
className='operate__item'
key='edit'
onClick={() => {
RCHistory.push(`/create-graphics-course?type=edit&id=${item.id}`);
window.RCHistory.push(`/create-graphics-course?type=edit&id=${item.id}`);
}}>
编辑
</div>
......@@ -305,17 +313,17 @@ class GraphicsCourseList extends React.Component {
删除
</div>
</div>
)
}
//改变上架状态
changeShelfState = (index,item,checked) =>{
let _shelfState = checked ? "YES" : "NO"
);
};
//改变上架状态
changeShelfState = (index, item, checked) => {
let _shelfState = checked ? 'YES' : 'NO';
// if(_shelfState==='NO'){
// _shelfState = "YES";
// }else{
// _shelfState = "NO"
// }
const params={
const params = {
courseId: item.id,
shelfState: _shelfState,
};
......@@ -326,7 +334,7 @@ class GraphicsCourseList extends React.Component {
} else {
message.success('已取消展示');
}
this.props.changeShelfState(index,_shelfState)
this.props.changeShelfState(index, _shelfState);
}
});
};
......@@ -355,8 +363,6 @@ class GraphicsCourseList extends React.Component {
// 显示分享弹窗
handleShowShareModal = (record, needStr = false) => {
const { id, scheduleVideoUrl } = record;
const _appId = appId;
const htmlUrl = `${LIVE_SHARE}graphics_detail/${id}?id=${User.getStoreId()}`;
const longUrl = htmlUrl;
const { coverUrl, courseName } = record;
......
/*
* @Author: 吴文洁
* @Date: 2020-08-05 10:07:47
* @LastEditors: yuananting
* @LastEditTime: 2021-07-15 14:02:45
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-07-18 10:13:42
* @Description: 线下课新增/编辑页
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
......@@ -14,7 +14,6 @@ import $ from 'jquery';
import RangePicker from '@/modules/common/DateRangePicker';
import ShowTips from '@/components/ShowTips';
import Breadcrumbs from '@/components/Breadcrumbs';
import SelectStudent from '../modal/select-student';
import SelectPrepareFileModal from '../../prepare-lesson/modal/SelectPrepareFileModal';
import PreviewOfflineModal from './modal/PreviewOfflineModal';
import StoreService from '@/domains/store-domain/storeService';
......@@ -26,13 +25,12 @@ import moment from 'moment';
import Upload from '@/core/upload';
import GraphicsEditor from '../components/GraphicsEditor';
import MultipleDatePicker from '@/components/MultipleDatePicker';
import ImgClipModal from '@/components/ImgClipModal'
import ImgClipModal from '@/components/ImgClipModal';
import './AddOfflineCourse.less';
import Bus from '@/core/bus';
const { Option } = Select;
const defaultCoverUrl = 'https://image.xiaomaiketang.com/xm/pxbWKsYA87.png';
let cutFlag = false;
const unitList = [
{ key: 'HOUR', value: '小时' },
{ key: 'MINUTE', value: '分钟' },
......@@ -42,8 +40,8 @@ class AddOfflineCourse extends React.Component {
constructor(props) {
super(props);
const courseId = getParameterByName('id');
const pageType = getParameterByName('type');
const courseId = window.getParameterByName('id');
const pageType = window.getParameterByName('type');
this.state = {
courseId, // 线下课ID,编辑的时候从URL上带过来
......@@ -159,7 +157,6 @@ class AddOfflineCourse extends React.Component {
const { result = {} } = res || {};
const {
courseName,
courseState,
categoryId,
offlinePlace,
whetherVisitorsJoin,
......@@ -346,8 +343,7 @@ class AddOfflineCourse extends React.Component {
visible: true,
imageFile: file
});
}
};
//获取resourceId
getSignature = (blob, fileName) => {
......@@ -620,7 +616,7 @@ class AddOfflineCourse extends React.Component {
};
StoreService.getStoreUserBasicPage(_query).then((res) => {
const { result = {} } = res;
const { records = [], total = 0, hasNext } = result;
const { records = [], hasNext } = result;
const list = current > 1 ? teacherList.concat(records) : records;
this.setState({
hasNext,
......@@ -630,11 +626,7 @@ class AddOfflineCourse extends React.Component {
});
}
changeIntro = (value, textLength) => {
// const isMore = textLength > 1000;
// if (isMore) {
// message.warning('内容过长,不能超过1000字');
// }
changeIntro = (value) => {
this.setState({ introduce: value });
};
......@@ -682,14 +674,11 @@ class AddOfflineCourse extends React.Component {
coverUrl,
introduce,
categoryId,
categoryList,
courseCatalogList,
whetherVisitorsJoin,
loadintroduce,
showSelectCoverModal,
visible,
hasImgReady,
cutImageBlob,
teacherId,
teacherList,
calendarTime,
......@@ -851,7 +840,7 @@ class AddOfflineCourse extends React.Component {
);
}}
getPopupContainer={() => document.getElementById('teacher')}>
{_.map(teacherList, (item, index) => {
{_.map(teacherList, (item) => {
return (
<Option value={item.id} key={item.id}>
{item.nickName}
......@@ -860,7 +849,6 @@ class AddOfflineCourse extends React.Component {
})}
</Select>
</div>
<div className='introduce'>
<span className='label'>课程简介:</span>
<div className='content'>
......@@ -893,7 +881,7 @@ class AddOfflineCourse extends React.Component {
</span>
<div>
<div className='select-day'>
已选 <span className='mark-day'>{isLongArr(calendarTime) ? calendarTime.length : 0}</span>
已选 <span className='mark-day'>{window.isLongArr(calendarTime) ? calendarTime.length : 0}</span>
</div>
<MultipleDatePicker disabled={isEditDisablie} selectDateList={calendarTime} onSelect={this.selectMultiDate} canSelectTodayBefore={false} />
</div>
......@@ -982,8 +970,8 @@ class AddOfflineCourse extends React.Component {
onChange={(dates) => {
this.handleChangeDates(dates);
}}
renderExtraFooter={() =>
calendarTime[0] ? (
renderExtraFooter={() => (
<If condition={calendarTime[0]}>
<div style={{ position: 'absolute', bottom: 8, cursor: 'pointer' }}>
<span
onClick={() =>
......@@ -1043,8 +1031,8 @@ class AddOfflineCourse extends React.Component {
上课前3天
</span>
</div>
) : null
}
</If>
)}
/>
</div>
)}
......@@ -1282,11 +1270,17 @@ class AddOfflineCourse extends React.Component {
}}
onSelect={this.handleSelectCover}
/>
)
}
{visible &&
<ImgClipModal visible={visible} imgUrl={imageFile.ossUrl} onConfirm={this.getSignature} onClose={() => { this.setState({ visible: false }); }} />
}
)}
{visible && (
<ImgClipModal
visible={visible}
imgUrl={imageFile.ossUrl}
onConfirm={this.getSignature}
onClose={() => {
this.setState({ visible: false });
}}
/>
)}
{this.state.previewOfflineModal}
</div>
);
......
/*
* @Author: 吴文洁
* @Date: 2020-07-16 11:05:17
* @Author: 吴文洁
* @Date: 2020-07-16 11:05:17
* @Last Modified by: chenshu
* @Last Modified time: 2021-04-06 16:17:57
* @Description: 添加直播-简介
*/
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
import { Col, message, Row, Switch } from 'antd';
import React from 'react';
import { Input, message, Upload, Radio, Row, Col, Button, Popover, Switch } from 'antd';
import Service from '@/common/js/service';
import GraphicsEditor from '../../components/GraphicsEditor';
import User from '@/common/js/user';
import UploadOss from '@/core/upload';
import './AddGraphicsIntro.less';
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
import { DISK_MAP } from '@/common/constants/academic/lessonEnum';
import { ImgCutModalNew } from '@/components';
const { TextArea } = Input;
class AddGraphicsIntro extends React.Component {
constructor(props) {
super(props);
this.state = {
showSelectFileModal: false,
diskList: [],
selectType: null,
}
};
}
// 上传封面图
handleShowImgCutModal = (event) => {
const imageFile = event.target.files[0];
if (!imageFile) return;
if (!imageFile) return;
this.setState({
imageFile,
showCutModal: true,
});
}
};
// 选择暖场资源
handleSelectVideo = (file) => {
const { selectType } = this.state;
// 选择暖场资源
handleSelectVideo = (file) => {
const { selectType } = this.state;
this.setState({
showSelectFileModal: false
})
showSelectFileModal: false,
});
const { ossUrl, resourceId, folderName, folderFormat, folderSize } = file;
if(selectType === 'WARMUP'){
if (selectType === 'WARMUP') {
const liveCourseWarmMedia = {
contentType: 'WARMUP',
mediaType: folderFormat === 'MP4' ? 'VIDEO' : 'PICTURE',
mediaContent: resourceId,
mediaUrl: ossUrl,
mediaName: folderName,
size: folderSize
}
size: folderSize,
};
this.props.onChange('liveCourseWarmMedia', liveCourseWarmMedia);
}else{
} else {
// 最多添加九图片
const { liveCourseMediaRequests } = this.props.data;
const list = _.filter(liveCourseMediaRequests, (item) => {
return item.mediaType == "PICTURE";
return item.mediaType == 'PICTURE';
});
if (list.length > 8) {
message.warning("最多添加9张图片");
message.warning('最多添加9张图片');
return;
}
liveCourseMediaRequests.push({
......@@ -78,121 +70,128 @@ class AddGraphicsIntro extends React.Component {
});
this.props.onChange('liveCourseMediaRequests', liveCourseMediaRequests);
}
}
};
changeDetail = (value) => {
this.props.onChange('courseMedia', value);
}
};
changeIntro = (value) => {
this.props.onChange('introduce', value);
}
};
whetherVisitorsJoinChange = ()=>{
if(this.props.data.whetherVisitorsJoin==="NO"){
this.props.onChange('whetherVisitorsJoin','YES')
}else{
this.props.onChange('whetherVisitorsJoin','NO')
whetherVisitorsJoinChange = () => {
if (this.props.data.whetherVisitorsJoin === 'NO') {
this.props.onChange('whetherVisitorsJoin', 'YES');
} else {
this.props.onChange('whetherVisitorsJoin', 'NO');
}
}
};
shelfStateChange = ()=>{
if(this.props.data.shelfState==="NO"){
this.props.onChange('shelfState','YES')
}else{
this.props.onChange('shelfState','NO')
shelfStateChange = () => {
if (this.props.data.shelfState === 'NO') {
this.props.onChange('shelfState', 'YES');
} else {
this.props.onChange('shelfState', 'NO');
}
}
};
render() {
const {data: { id, whetherVisitorsJoin, courseMedia, introduce, shelfState, loadcourseMedia, loadintroduce } } = this.props;
const {
data: { id, whetherVisitorsJoin, courseMedia, introduce, shelfState, loadcourseMedia, loadintroduce },
} = this.props;
const { showSelectFileModal, selectType } = this.state;
return (
<div className="add-offline__intro-info">
<div className="allow-tourist-join">
<span className="label">观看设置:</span>
<div className="content">
<div className='add-video__intro-info'>
<div className='allow-tourist-join'>
<span className='label'>观看设置:</span>
<div className='content'>
<div>
<Switch checked={whetherVisitorsJoin==="YES"? true:false} onChange={this.whetherVisitorsJoinChange}/>
</div>
<Switch checked={whetherVisitorsJoin === 'YES' ? true : false} onChange={this.whetherVisitorsJoinChange} />
</div>
<div>
<div className="desc">
<div className='desc'>
<div>开启:允许未绑定手机号的学员观看</div>
<div>关闭:仅限绑定了手机号的学员可以进入观看图文课</div>
</div>
</div>
</div>
</div>
</div>
<div className="store-show">
<span className="label">学院展示:</span>
<div className="content">
<Row>
<Col span={3}>
<Switch checked={shelfState==="YES"? true:false} onChange={this.shelfStateChange}/>
</Col>
<Col span={21}>
<div className="desc">
<div>开启:图文课将在学员学院图文课列表中展示</div>
<div>关闭:图文课将在学员学院图文课列表中隐藏</div>
</div>
</Col>
</Row>
<div className='store-show'>
<span className='label'>学院展示:</span>
<div className='content'>
<Row>
<Col span={3}>
<Switch checked={shelfState === 'YES' ? true : false} onChange={this.shelfStateChange} />
</Col>
<Col span={21}>
<div className='desc'>
<div>开启:图文课将在学员学院图文课列表中展示</div>
<div>关闭:图文课将在学员学院图文课列表中隐藏</div>
</div>
</Col>
</Row>
</div>
</div>
<div className="introduce required">
<span className="label" style={{ marginTop: 5 }}>课程内容:</span>
<div className="content">
<div className="intro-list">
<div className="intro-list__item content-editor">
{(!id || loadcourseMedia) &&
<div className='introduce required'>
<span className='label' style={{ marginTop: 5 }}>
课程内容:
</span>
<div className='content'>
<div className='intro-list'>
<div className='intro-list__item content-editor'>
{(!id || loadcourseMedia) && (
<GraphicsEditor
id="content"
id='content'
detail={{
content: courseMedia
content: courseMedia,
}}
onChange={(val) => {
this.changeDetail(val);
}}
onChange={(val) => { this.changeDetail(val) }}
/>
}
)}
</div>
</div>
</div>
</div>
<div className="introduce">
<span className="label">课程简介:</span>
<div className="content">
<div className="intro-list">
<div className="intro-list__item introduce-editor">
{(!id || loadintroduce) &&
<div className='introduce'>
<span className='label'>课程简介:</span>
<div className='content'>
<div className='intro-list'>
<div className='intro-list__item introduce-editor'>
{(!id || loadintroduce) && (
<GraphicsEditor
id="intro"
id='intro'
isIntro={true}
detail={{
content: introduce
content: introduce,
}}
onChange={(val) => {
this.changeIntro(val);
}}
onChange={(val) => { this.changeIntro(val) }}
/>
}
)}
</div>
</div>
</div>
</div>
{/* 选择暖场图文件弹窗 */}
{ showSelectFileModal &&
{showSelectFileModal && (
<SelectPrepareFileModal
operateType="select"
accept={selectType==="INTRO"?"image/jpeg,image/png,image/jpg":"video/mp4,image/jpeg,image/png,image/jpg"}
selectTypeList={ selectType==="INTRO" ? ['JPG', 'JPEG', 'PNG']: ['MP4', 'JPG', 'JPEG', 'PNG'] }
tooltip={ selectType==="INTRO"?'支持文件类型:jpg、jpeg、png':'支持文件类型:jpg、jpeg、png、mp4'}
operateType='select'
accept={selectType === 'INTRO' ? 'image/jpeg,image/png,image/jpg' : 'video/mp4,image/jpeg,image/png,image/jpg'}
selectTypeList={selectType === 'INTRO' ? ['JPG', 'JPEG', 'PNG'] : ['MP4', 'JPG', 'JPEG', 'PNG']}
tooltip={selectType === 'INTRO' ? '支持文件类型:jpg、jpeg、png' : '支持文件类型:jpg、jpeg、png、mp4'}
isOpen={showSelectFileModal}
onClose={() => {
this.setState({ showSelectFileModal: false })
this.setState({ showSelectFileModal: false });
}}
onSelect={this.handleSelectVideo}
/>
}
)}
</div>
)
);
}
}
......
......@@ -17,6 +17,7 @@ import OfflineCourseData from '@/modules/course-manage/offline-course/OfflineCou
import { Dropdown, message, Modal, Switch, Tooltip } from 'antd';
import moment from 'moment';
import React from 'react';
import { find, reduce, last } from 'underscore';
import { Route, withRouter } from 'react-router-dom';
import ENUM from '../../../knowledge-base/ENUM.js';
import PreviewOfflineModal from '../modal/PreviewOfflineModal';
......@@ -44,7 +45,7 @@ class OfflineCourseList extends React.Component {
handlePlanName = (planArray) => {
let planStr = '';
planArray.map((item, index) => {
planArray.forEach((item, index) => {
if (index < planArray.length - 1) {
planStr = planStr + item.planName + '、';
} else {
......@@ -64,13 +65,13 @@ class OfflineCourseList extends React.Component {
fixed: 'left',
render: (val, record) => {
const { courseMediaVOS, courseName, offlinePlace, calendarTime, startTime, endTime } = record;
const coverUrl = (_.find(courseMediaVOS, (data) => data.contentType === 'COVER') || {}).mediaUrl;
const coverUrl = (find(courseMediaVOS, (data) => data.contentType === 'COVER') || {}).mediaUrl;
let isContinue = calendarTime.length > 1;
_.reduce(calendarTime, (a, b) => {
reduce(calendarTime, (a, b) => {
isContinue = isContinue && b - a === 86400000;
return b;
});
const lastTime = _.last(calendarTime);
const lastTime = last(calendarTime);
const time = `${
!isContinue
? calendarTime.map((item) => moment(item).format('MM-DD')).join('、')
......@@ -138,7 +139,9 @@ class OfflineCourseList extends React.Component {
width: 120,
dataIndex: 'courseware',
render: (val, item, index) => {
return <Switch disabled={item.courseState === 'EXPIRED'} checked={item.shelfState === 'YES'} onChange={() => this.changeShelfState(item)} />;
return (
<Switch size='small' disabled={item.courseState === 'EXPIRED'} checked={item.shelfState === 'YES'} onChange={() => this.changeShelfState(item)} />
);
},
},
{
......
/*
* @Author: 吴文洁
* @Date: 2020-07-16 11:05:17
* @Author: 吴文洁
* @Date: 2020-07-16 11:05:17
* @Last Modified by: chenshu
* @Last Modified time: 2021-04-06 16:44:09
* @Description: 添加直播-简介
*/
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
import { Col, message, Row, Switch } from 'antd';
import React from 'react';
import { Input, message, Upload, Radio, Row, Col, Button, Popover, Switch } from 'antd';
import Service from '@/common/js/service';
import GraphicsEditor from '../../components/GraphicsEditor';
import User from '@/common/js/user';
import UploadOss from '@/core/upload';
import { filter } from 'underscore';
import './AddVideoIntro.less';
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
import { DISK_MAP } from '@/common/constants/academic/lessonEnum';
import { ImgCutModalNew } from '@/components';
const { TextArea } = Input;
const defaultCover = 'https://xiaomai-image.oss-cn-hangzhou.aliyuncs.com/1599635741526.png';
class AddVideoIntro extends React.Component {
constructor(props) {
super(props);
this.state = {
warmUrl: defaultCover,
showSelectFileModal: false,
diskList: [],
selectType:null
}
selectType: null,
};
}
// 上传封面图
handleShowImgCutModal = (event) => {
const imageFile = event.target.files[0];
if (!imageFile) return;
if (!imageFile) return;
this.setState({
imageFile,
showCutModal: true,
});
}
};
// 选择暖场资源
handleSelectVideo = (file) => {
const { selectType } = this.state;
// 选择暖场资源
handleSelectVideo = (file) => {
const { selectType } = this.state;
this.setState({
showSelectFileModal: false
})
showSelectFileModal: false,
});
const { ossUrl, resourceId, folderName, folderFormat, folderSize } = file;
if(selectType === 'WARMUP'){
if (selectType === 'WARMUP') {
const liveCourseWarmMedia = {
contentType: 'WARMUP',
mediaType: folderFormat === 'MP4' ? 'VIDEO' : 'PICTURE',
mediaContent: resourceId,
mediaUrl: ossUrl,
mediaName: folderName,
size: folderSize
}
size: folderSize,
};
this.props.onChange('liveCourseWarmMedia', liveCourseWarmMedia);
}else{
// 最多添加九图片
const { liveCourseMediaRequests } = this.props.data;
} else {
// 最多添加九图片
const { liveCourseMediaRequests } = this.props.data;
const list = _.filter(liveCourseMediaRequests, (item) => {
return item.mediaType == "PICTURE";
});
if (list.length > 8) {
message.warning("最多添加9张图片");
return;
}
liveCourseMediaRequests.push({
contentType: 'INTRO',
size: folderSize,
mediaName: folderName,
mediaContent: resourceId,
mediaType: 'PICTURE',
mediaUrl: ossUrl,
});
this.props.onChange('liveCourseMediaRequests', liveCourseMediaRequests);
const list = filter(liveCourseMediaRequests, (item) => {
return item.mediaType === 'PICTURE';
});
if (list.length > 8) {
message.warning('最多添加9张图片');
return;
}
liveCourseMediaRequests.push({
contentType: 'INTRO',
size: folderSize,
mediaName: folderName,
mediaContent: resourceId,
mediaType: 'PICTURE',
mediaUrl: ossUrl,
});
this.props.onChange('liveCourseMediaRequests', liveCourseMediaRequests);
}
}
};
changeIntro = (value) => {
this.props.onChange('introduce', value);
}
};
handleUpload = (Blob) => {
this.setState({
showSelectFileModal: true,
selectType:'INTRO'
})
}
whetherVisitorsJoinChange = ()=>{
if(this.props.data.whetherVisitorsJoin==="NO"){
this.props.onChange('whetherVisitorsJoin','YES')
}else{
this.props.onChange('whetherVisitorsJoin','NO')
selectType: 'INTRO',
});
};
whetherVisitorsJoinChange = () => {
if (this.props.data.whetherVisitorsJoin === 'NO') {
this.props.onChange('whetherVisitorsJoin', 'YES');
} else {
this.props.onChange('whetherVisitorsJoin', 'NO');
}
}
shelfStateChange = ()=>{
if(this.props.data.shelfState==="NO"){
this.props.onChange('shelfState','YES')
}else{
this.props.onChange('shelfState','NO')
};
shelfStateChange = () => {
if (this.props.data.shelfState === 'NO') {
this.props.onChange('shelfState', 'YES');
} else {
this.props.onChange('shelfState', 'NO');
}
}
};
render() {
const {data: { whetherVisitorsJoin,liveCourseMediaRequests = [], shelfState, id, introduce, loadintroduce } } = this.props;
const {showSelectFileModal,selectType} = this.state
const {
data: { whetherVisitorsJoin, shelfState, id, introduce, loadintroduce },
} = this.props;
const { showSelectFileModal, selectType } = this.state;
return (
<div className="add-video__intro-info">
<div className="allow-tourist-join">
<span className="label">观看设置:</span>
<div className="content">
<div className='add-video__intro-info'>
<div className='allow-tourist-join'>
<span className='label'>观看设置:</span>
<div className='content'>
<div>
<Switch checked={whetherVisitorsJoin==="NO"? true:false} onChange={this.whetherVisitorsJoinChange}/>
</div>
......@@ -130,7 +124,7 @@ class AddVideoIntro extends React.Component {
</Otherwise>
</Choose>
</div>
</div>
</div>
</div>
</div>
<div className="store-show">
......@@ -166,31 +160,33 @@ class AddVideoIntro extends React.Component {
id="intro"
isIntro={true}
detail={{
content: introduce
content: introduce,
}}
onChange={(val) => {
this.changeIntro(val);
}}
onChange={(val) => { this.changeIntro(val) }}
/>
}
)}
</div>
</div>
</div>
</div>
{/* 选择暖场图文件弹窗 */}
{ showSelectFileModal &&
{showSelectFileModal && (
<SelectPrepareFileModal
operateType="select"
accept={selectType==="INTRO"?"image/jpeg,image/png,image/jpg":"video/mp4,image/jpeg,image/png,image/jpg"}
selectTypeList={ selectType==="INTRO" ? ['JPG', 'JPEG', 'PNG']: ['MP4', 'JPG', 'JPEG', 'PNG'] }
tooltip={ selectType==="INTRO"?'支持文件类型:jpg、jpeg、png':'支持文件类型:jpg、jpeg、png、mp4'}
operateType='select'
accept={selectType === 'INTRO' ? 'image/jpeg,image/png,image/jpg' : 'video/mp4,image/jpeg,image/png,image/jpg'}
selectTypeList={selectType === 'INTRO' ? ['JPG', 'JPEG', 'PNG'] : ['MP4', 'JPG', 'JPEG', 'PNG']}
tooltip={selectType === 'INTRO' ? '支持文件类型:jpg、jpeg、png' : '支持文件类型:jpg、jpeg、png、mp4'}
isOpen={showSelectFileModal}
onClose={() => {
this.setState({ showSelectFileModal: false })
this.setState({ showSelectFileModal: false });
}}
onSelect={this.handleSelectVideo}
/>
}
)}
</div>
)
);
}
}
......
......@@ -153,6 +153,7 @@ class VideoCourseList extends React.Component {
render: (val, item, index) => {
return (
<Switch
size='small'
loading={ShelfLoading}
checked={item.shelfState === 'YES'}
defaultChecked={item.shelfState}
......
......@@ -188,7 +188,7 @@ class Classification extends Component {
<div className='sider-title'>知识分类</div>
<Search
className='sider-search'
placeholder='搜索名称分类'
placeholder='搜索分类名称'
onSearch={(value) => {
this.queryCategoryTree(value);
}}
......
/*
* @Author: yuananting
* @Date: 2021-07-05 10:48:08
* @LastEditors: yuananting
* @LastEditTime: 2021-07-15 13:58:17
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-07-18 10:14:26
* @Description: 描述一下咯
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
......@@ -16,7 +16,7 @@
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
import { Button, Input, Switch, Radio, Row, Col, Modal, message, Tooltip } from 'antd';
import { Button, Input, Switch, Radio, Row, Col, message, Tooltip } from 'antd';
import { withRouter } from 'react-router-dom';
import SelectOperatorModal from '../modal/SelectOperatorModal';
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
......@@ -27,7 +27,6 @@ import './BasicInfo.less';
const { TextArea } = Input;
const defaultCover = 'https://image.xiaomaiketang.com/xm/rEAetaTEh3.png';
let cutFlag = false;
class BasicInfo extends React.Component {
constructor(props) {
super(props);
......@@ -84,7 +83,7 @@ class BasicInfo extends React.Component {
handleSelectCover = (file) => {
this.setState({
visible: true,
imageFile:file
imageFile: file,
});
};
//获取resourceId
......@@ -139,7 +138,7 @@ class BasicInfo extends React.Component {
};
render() {
const { operatorModalVisible, showSelectFileModal, visible, hasImgReady, cutImageBlob,imageFile} = this.state;
const { operatorModalVisible, showSelectFileModal, visible, imageFile } = this.state;
const { data } = this.props;
const { planName, coverUrl, introduce, enableState, operateType, selectOperatorList, percentCompleteLive, percentCompleteVideo, percentCompletePicture } =
data;
......@@ -343,9 +342,16 @@ class BasicInfo extends React.Component {
onSelect={this.handleSelectCover}
/>
)}
{ visible &&
<ImgClipModal visible={visible} imgUrl={imageFile.ossUrl} onConfirm={this.getSignature} onClose={()=>{this.setState({ visible: false });}}/>
}
{visible && (
<ImgClipModal
visible={visible}
imgUrl={imageFile.ossUrl}
onConfirm={this.getSignature}
onClose={() => {
this.setState({ visible: false });
}}
/>
)}
</div>
);
}
......
/*
* @Author: 吴文洁
* @Date: 2019-09-10 18:26:03
* @LastEditors: yuananting
* @LastEditTime: 2021-07-06 14:37:49
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-07-14 14:14:16
* @Description:
*/
import React, { useRef, useContext, useEffect, useState } from 'react';
......@@ -36,7 +36,7 @@ function Header(props) {
const [openDropdown, setOpenDropdown] = useState(false);
const [instScroll, setInstScroll] = useState(false);
const ctx = useContext(XMContext);
const htmlUrl = `${LIVE_SHARE}store/index?id=${User.getStoreId()}&userId=${User.getUserId()}&from=work_weixin`;
const htmlUrl = `${LIVE_SHARE}store/index?id=${User.getStoreId()}&userId=${User.getUserId()}`;
const helpCenterUrl = 'https://www.yuque.com/wangzhong-zkqw0/qixue'; // 帮助中心
const storeUserId = User.getStoreUserId();
const enterpriseId = User.getEnterpriseId();
......@@ -207,8 +207,9 @@ function Header(props) {
}
function handleConvertShortUrl() {
//提供给企业微信下的人员使用的码,用微信方式扫时页时获取企业微信的身份
CourseService.getQrcode({
urls: [htmlUrl],
urls: [`${htmlUrl}&source=workWechat`],
}).then((res) => {
const { result = [] } = res;
const qrcodeWrapDom = document.querySelector('#h5-qrcode');
......@@ -343,7 +344,7 @@ function Header(props) {
<div className='h5-url'>
<div className='name'>手机端学院</div>
<div id='h5-qrcode'></div>
<div className='tip'>微信扫码,打开学院</div>
<div className='tip'>企业员工扫码登录学院</div>
</div>
</div>
</div>
......
......@@ -378,7 +378,7 @@
.name,
.tip {
width: 70px;
font-size: 14px;
font-size: 13px;
color: #333333;
line-height: 52px;
margin: 0 auto;
......
......@@ -97,7 +97,7 @@
margin: 6px 8px;
width: calc(100% - 15px);
&:hover {
background: #f3f6fa;
background: rgba(41, 102, 255, .05);
border-radius: 2px;
color: #333;
}
......@@ -106,8 +106,10 @@
background-color: @active-color;
color: #fff;
border-radius: 2px;
&:hover {
color: #fff;
&.single-menu {
background: rgba(41, 102, 255, .1) !important;
color: @active-color;
font-weight:500;
}
}
.ant-menu-submenu {
......@@ -117,6 +119,9 @@
}
.ant-menu-item {
padding-left: 46px !important;
&:hover {
color: #333!important;
}
}
}
......@@ -124,14 +129,22 @@
color: @active-color;
.ant-menu-item-selected {
color: #fff;
color: @active-color;
font-weight: 500;
&:hover {
color: @active-color!important;
}
.listType {
background: @active-color;
}
}
.ant-menu-submenu-arrow {
right: 10px!important;
color: @active-color;
}
}
.ant-menu-submenu-arrow {
right: 22px;
right: 10px!important;
color: #5e606a;
}
}
......@@ -266,16 +279,30 @@
.shink-footer {
left: 74px;
}
// &:hover{
// .menu-type-icon{
// display:inline-block;
// }
// }
}
.ant-menu-inline .ant-menu-item, .ant-menu-inline .ant-menu-submenu-title {
width: calc(100% - 15px)!important;
}
.ant-menu-submenu:hover {
// background: rgba(41, 102, 255, .05) !important;
&.ant-menu-submenu-title:hover {
color: #333333!important;
}
.ant-menu-submenu-title > .ant-menu-submenu-arrow {
color: #5e606a!important;
}
&.icon-img-box {
background-color: rgba(41, 102, 255, .05)!important;
}
}
.ant-menu-light .ant-menu-submenu-title:hover {
color: #333!important;
background: rgba(41, 102, 255, .05) !important;
}
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
background: @active-color !important;
background: rgba(41, 102, 255, .1) !important;
}
.ant-menu.ant-menu-dark,
.ant-menu-dark .ant-menu-sub,
......
......@@ -239,10 +239,10 @@ function Aside(props: any) {
icon={
!menuType && item.groupCode === selectKeyParent ? (
<div className='icon-img-box' style={{ backgroundColor: '#2966FF', width: '40px', height: '40px' }}>
<img src={item.selectImg} className='icon-img' />
<img src={item.img} className='icon-img' />
</div>
) : (
<div className='icon-img-box' style={{ backgroundColor: '#fff', width: '40px', height: '40px', display: 'inline-block' }}>
<div className='icon-img-box' style={{ width: '40px', height: '40px', display: 'inline-block' }}>
<img src={item.img} className='icon-img' />
</div>
)
......@@ -269,6 +269,7 @@ function Aside(props: any) {
} else {
return (
<Menu.Item
className="single-menu"
onClick={() => {
toggleMenu(item, item.groupCode);
}}
......@@ -276,11 +277,11 @@ function Aside(props: any) {
icon={
!menuType && item.groupCode === selectKeyParent ? (
<div className='icon-img-box' style={{ backgroundColor: '#2966FF' }}>
<img src={item.selectImg} className='icon-img' />
<img src={item.img} className='icon-img' />
</div>
) : (
<div className='icon-img-box'>
<img src={selectKey === item.groupCode ? item.selectImg : item.img} className='icon-img' />
<img src={item.img} className='icon-img' />
</div>
)
}>
......
......@@ -539,15 +539,15 @@ class CourseCategoryManage extends Component {
return (
<div className='page course-category-manage'>
{['aid', 'knowledge'].includes(getParameterByName('from')) ? (
<Breadcrumbs navList='课程分类' goBack={() => window.RCHistory.goBack()} />
<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>
<Search
placeholder='请输入名称'
placeholder='搜索分类名称'
style={{ width: '300px' }}
onSearch={(value) => this.queryCategoryTree('init', value)}
className='search-input'
......
/*
* @Author: yuananting
* @Date: 2021-02-22 10:59:43
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-06-15 11:20:48
* @LastEditors: yuananting
* @LastEditTime: 2021-07-13 11:54:21
* @Description: 助学工具-侧边课程分类树
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -20,7 +20,7 @@ class CourseCategorySiderTree extends Component {
constructor(props) {
super(props);
this.state = {
selectedKeys: ['QUESTION_INDEX', 'PAPER_INDEX'].includes(props.fromModule) ? [getParameterByName('categoryId') || 'null'] : ['null'],
selectedKeys: ['null'],
treeData: props.treeData || [],
autoExpandParent: false,
};
......@@ -189,7 +189,7 @@ class CourseCategorySiderTree extends Component {
)}
<Search
className='sider-search'
placeholder='搜索名称分类'
placeholder='搜索分类名称'
onSearch={(value) => {
this.queryCategoryTree('init', value);
}}
......
import React, { useState, useRef, useEffect, useContext } from 'react';
import Breadcrumbs from '@/components/Breadcrumbs';
import React, { useState, useRef, useEffect, useContext } from 'react'
import Breadcrumbs from "@/components/Breadcrumbs";
import { Form, Alert, Input, Button, InputNumber, DatePicker, Switch, Radio, message, Modal, Tooltip } from 'antd';
import { Route, withRouter } from 'react-router-dom';
import User from '@/common/js/user';
import moment from 'moment';
import Service from '@/common/js/service';
import _ from 'underscore';
import User from "@/common/js/user";
import moment from 'moment'
import Service from "@/common/js/service";
import _ from 'underscore'
import GraphicsEditor from '../../course-manage/components/GraphicsEditor';
import SelectPaperModal from './SelectPaperModal';
import PreviewModal from './PreviewModal';
import ShowTips from '@/components/ShowTips';
import SelectPaperModal from './SelectPaperModal'
import PreviewModal from './PreviewModal'
import ShowTips from "@/components/ShowTips";
import './AddExam.less';
const { RangePicker } = DatePicker;
function AddExam(props: any) {
const paperInfoInit: any = { passScore: 60 };
const [showModal, setShowModal] = useState(false);
const [paperInfo, setPaperInfo] = useState(paperInfoInit);
const [paperId, setPaperId] = useState('');
const [passRate, setPassRate] = useState(60); //及格线
const [examStartTime, setStartTime] = useState('');
const [examEndTime, setExamEndTime] = useState('');
const [examName, setExamName] = useState('');
const [needPhone, setNeedPhone] = useState('DO_NOT_NEED_PHONE_VERIFY');
const [needOptionDisorder, setNeedOptionDisorder] = useState('OPTION_SORT');
const [resultContent, setResultContent] = useState('PASS_AND_SCORE');
const [answerAnalysis, setAnswerAnalysis] = useState('RIGHT_OR_WRONG');
const [resultShow, setResultShow] = useState('IMMEDIATELY');
const [examDesc, setExamDesc] = useState('');
const [passScore, setPassScore] = useState(100);
const [desclen, setDescLen] = useState(0);
const [check, setCheck] = useState(false);
const [getData, setGetData] = useState(false);
const [preview, setPreview] = useState(false);
const [examTotal, setExamTotal] = useState(0);
const request = useRef(false);
const { match } = props;
const [examDuration, setExamDuration] = useState(undefined);
useEffect(() => {
switch (props.type) {
case 'copy': // 考试列表-复制考试进入
case 'edit': // 考试列表-编辑考试进入
queryExamDetail();
break;
case 'organizeExam': // 试卷列表-组织考试进入
case 'newPaperToAddExam': // 组卷页面-新建保存试卷并组织考试
case 'editPaperToAddExam': // 组卷页面-编辑保存试卷并组织考试
setGetData(true);
setPaperInfo(props.paperInfo);
break;
}
}, []);
useEffect(() => {
setPaperId(paperInfo.paperId);
setPassRate(paperInfo.passRate);
}, [paperInfo.paperId]);
useEffect(() => {
setPassScore(Math.round((((paperInfo.totalScore || 0) * (passRate || 0)) as any) / 100));
setExamTotal(paperInfo.singleChoiceCnt + paperInfo.multiChoiceCnt + paperInfo.judgeCnt + paperInfo.gapFillingCnt + paperInfo.indefiniteChoiceCnt || 0);
}, [paperInfo.paperId, passRate]);
function disabledDate(current: any) {
// Can not select days before today and today
return current && current < moment().startOf('day');
}
function queryExamDetail() {
Service.Hades('public/hades/queryExamDetail', {
examId: match.params.id,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0,
}).then((res) => {
const { result } = res;
setPaperInfo(result.examPaper);
setPaperId(result.examPaper.paperId);
setStartTime(props.type === 'edit' ? result.examStartTime : '');
setExamEndTime(props.type === 'edit' ? result.examEndTime : '');
setExamName(props.type === 'edit' ? result.examName : `${result.examName}(复制)`);
setPassRate(result.passRate * 100);
setNeedPhone(result.needPhone);
setExamDesc(result.examDesc);
setExamDuration((result.examDuration / 60 / 1000) as any);
setAnswerAnalysis(result.answerAnalysis);
setNeedOptionDisorder(result.needOptionDisorder);
setPassScore(result.passScore);
setResultContent(result.resultContent);
setResultShow(result.resultShow);
setGetData(true);
});
}
function handleSave() {
if (request.current) {
return;
}
setCheck(true);
const param = {
paperId,
startTime: examStartTime,
endTime: examEndTime,
examName,
passRate: passRate / 100,
examStartTime,
examEndTime,
examDesc,
needPhone,
needOptionDisorder,
resultContent,
answerAnalysis,
resultShow,
examDuration: (examDuration || 0) * 60 * 1000,
passScore,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0,
examId: '',
};
if (!param.examName) {
message.warning('请输入考试名称');
return;
}
if (param.examName && param.examName.length > 40) {
message.warning('考试名称最多40字');
return;
const paperInfoInit: any = { passScore: 60 };
const [showModal, setShowModal] = useState(false);
const [paperInfo, setPaperInfo] = useState(paperInfoInit);
const [paperId, setPaperId] = useState('');
const [passRate, setPassRate] = useState(60);//及格线
const [examStartTime, setStartTime] = useState('');
const [examEndTime, setExamEndTime] = useState('');
const [examName, setExamName] = useState('');
const [needPhone, setNeedPhone] = useState('DO_NOT_NEED_PHONE_VERIFY');
const [needOptionDisorder, setNeedOptionDisorder] = useState('OPTION_SORT');
const [resultContent, setResultContent] = useState('PASS_AND_SCORE');
const [answerAnalysis, setAnswerAnalysis] = useState('RIGHT_OR_WRONG');
const [resultShow, setResultShow] = useState('IMMEDIATELY');
const [examDesc, setExamDesc] = useState('');
const [passScore, setPassScore] = useState(100);
const [desclen, setDescLen] = useState(0);
const [check, setCheck] = useState(false);
const [getData, setGetData] = useState(false);
const [preview, setPreview] = useState(false);
const [examTotal, setExamTotal] = useState(0);
const [examList, setExamList] = useState([]);
const request = useRef(false);
const { match } = props;
const [examDuration, setExamDuration] = useState(undefined);
useEffect(() => {
queryExamList();
switch (props.type) {
case "copy": // 考试列表-复制考试进入
case "edit": // 考试列表-编辑考试进入
queryExamDetail();
break;
case "organizeExam": // 试卷列表-组织考试进入
case "newPaperToAddExam": // 组卷页面-新建保存试卷并组织考试
case "editPaperToAddExam": // 组卷页面-编辑保存试卷并组织考试
setGetData(true);
setPaperInfo(props.paperInfo);
setExamName(props.paperInfo?.paperName)
break;
}
}, [])
useEffect(() => {
setPaperId(paperInfo.paperId)
setPassRate(paperInfo.passRate)
}, [paperInfo.paperId])
useEffect(() => {
setPassScore(Math.round((paperInfo.totalScore || 0) * (passRate || 0) as any / 100))
setExamTotal(paperInfo.singleChoiceCnt + paperInfo.multiChoiceCnt + paperInfo.judgeCnt + paperInfo.gapFillingCnt + paperInfo.indefiniteChoiceCnt || 0)
}, [paperInfo.paperId, passRate])
function disabledDate(current: any) {
// Can not select days before today and today
return current && current < moment().startOf('day');
}
if (!paperId) {
message.warning('请选择试卷');
return;
function queryExamList() {
let param = {
current: 1,
size: 9999,
order: 'EXAM_START_TIME_DESC',
userId: User.getStoreUserId(),
tenantId: User.getStoreId(),
source: 0,
}
Service.Hades('public/hades/queryExamPageList', param).then(res=> {
console.log(res)
const { result = {} } = res;
setExamList(result.records)
})
}
if (!passRate) {
message.warning('请输入及格线');
return;
function queryExamDetail() {
Service.Hades("public/hades/queryExamDetail", {
examId: match.params.id,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0
}).then((res) => {
const { result } = res
setPaperInfo(result.examPaper)
setPaperId(result.examPaper.paperId)
setStartTime(props.type === 'edit' ? result.examStartTime : '')
setExamEndTime(props.type === 'edit' ? result.examEndTime : '')
setExamName(props.type === 'edit' ? result.examName : `${result.examName}(复制)`)
setPassRate(result.passRate * 100)
setNeedPhone(result.needPhone)
setExamDesc(result.examDesc)
setExamDuration(result.examDuration / 60 / 1000 as any)
setAnswerAnalysis(result.answerAnalysis)
setNeedOptionDisorder(result.needOptionDisorder)
setPassScore(result.passScore)
setResultContent(result.resultContent)
setResultShow(result.resultShow)
setGetData(true)
})
}
if (!examStartTime || !examEndTime) {
message.warning('请选择考试起止时间');
return;
}
if (Number(examStartTime) < moment().valueOf()) {
message.warning('开始时间不能早于现在');
return;
}
if (!examDuration) {
message.warning('请输入考试时长');
return;
}
if (examStartTime + (examDuration as any) * 60 * 1000 > examEndTime) {
message.warning('考试时长不得超过考试有效期时长');
return;
}
function handleSave() {
if (request.current) {
return
}
if (desclen > 1000) {
message.warning('内容过长,不能超过1000字');
return;
}
request.current = true;
setTimeout(() => {
request.current = false;
}, 2000);
if (props.type === 'edit') {
param.examId = match.params.id;
}
Service.Hades(props.type === 'edit' ? 'public/hades/editExam' : 'public/hades/createExam', param).then((res) => {
message.success(props.type === 'edit' ? '编辑成功' : '创建成功');
switch (props.type) {
case 'organizeExam': // 试卷列表-组织考试进入
case 'newPaperToAddExam': // 组卷保存组织考试
case 'editPaperToAddExam':
window.RCHistory.push('/examination-manage-index');
break;
case 'add':
case 'edit': // 考试列表-新建或编辑
case 'copy': // 考试列表-新建或编辑
props.freshList();
props.history.goBack();
break;
}
});
}
function disabledRangeTime(date: any, type: any) {
if (moment(date).isSame(moment(), 'day')) {
return {
disabledHours: () => {
const hours = [];
for (let i = 0; i < moment().hour(); i++) {
hours.push(i);
}
return hours;
},
disabledMinutes: () => {
const currentMinute = moment().minute();
const currentHour = moment(date).hour();
const minutes = [];
if (currentHour === moment().hour()) {
for (let i = 0; i < currentMinute; i++) {
minutes.push(i);
}
}
return minutes;
},
};
}
return {
disabledHours: () => [],
disabledMinutes: () => [],
disabledSeconds: () => [],
};
}
function handleGoBack() {
Modal.confirm({
title: '确定要返回吗?',
content: '返回后,本次编辑的内容将不被保存',
okText: '确认返回',
cancelText: '留在本页',
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
onOk: () => {
window.RCHistory.push('/examination-manage-index');
},
});
}
let title = '';
switch (props.type) {
case 'add':
case 'organizeExam':
case 'newPaperToAddExam':
case 'editPaperToAddExam':
title = '新建考试';
break;
case 'edit':
title = '编辑考试';
break;
case 'copy':
title = '复制考试';
break;
default:
break;
}
return (
<div className='page examPage'>
<Breadcrumbs navList={title} goBack={handleGoBack} />
<div className='box'>
<div className='show-tips'>
<ShowTips message='请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利' />
</div>{' '}
<div className='form'>
<div className='title'>基本信息</div>
<Form labelCol={{ span: 3 }} wrapperCol={{ span: 14 }} layout='horizontal'>
<Form.Item
label='考试名称'
validateStatus={check && (!examName ? '请输入考试名称' : examName.length > 40 && '考试名称最多40字') ? 'error' : ''}
help={check && (!examName ? '请输入考试名称' : examName.length > 40 && '考试名称最多40字')}
required>
<Input
placeholder='请输入考试名称(40字以内)'
maxLength={40}
value={examName}
onChange={(e) => {
setExamName(e.target.value);
}}
style={{ width: 320 }}
/>
</Form.Item>
<Form.Item label='选择试卷' validateStatus={check && !paperId ? 'error' : ''} help={check && !paperId && '请选择试卷'} required>
<Button
onClick={() => {
setShowModal(true);
}}>
{paperInfo.paperId ? '重新选择' : '选择试卷'}
</Button>
{paperInfo.paperId && (
<div className='paperTitle'>
<img src='https://image.xiaomaiketang.com/xm/pY5imEhjzw.png' alt='' /> {paperInfo.paperName}
</div>
)}
{paperInfo.paperId && (
<div className='table'>
<div className='header'>
<div className='item'>单选题</div>
<div className='item'>多选题</div>
<div className='item'>判断题</div>
<div className='item'>填空题</div>
<div className='item long'>不定项选择题</div>
<div className='item'>合计</div>
</div>
<div className='body-list'>
<div className='item'>{paperInfo.singleChoiceCnt || 0}</div>
<div className='item'>{paperInfo.multiChoiceCnt || 0}</div>
<div className='item'>{paperInfo.judgeCnt || 0}</div>
<div className='item'>{paperInfo.gapFillingCnt || 0}</div>
<div className='item long'>{paperInfo.indefiniteChoiceCnt || 0}</div>
<div className='item'>{examTotal}</div>
</div>
<div className='body-list'>
<div className='item'>{paperInfo.singleChoiceScore || 0}</div>
<div className='item'>{paperInfo.multiChoiceScore || 0}</div>
<div className='item'>{paperInfo.judgeScore || 0}</div>
<div className='item'>{paperInfo.gapFillingScore || 0}</div>
<div className='item long'>{paperInfo.indefiniteChoiceScore || 0}</div>
<div className='item'>{paperInfo.totalScore || 0}</div>
</div>
</div>
)}
</Form.Item>
<Form.Item
label={
<div>
<span>及格线</span>
<Tooltip title='默认为选中试卷所设置的及格线,可修改'>
<span className='icon iconfont' style={{ color: '#BFBFBF', marginLeft: 4 }}>
&#xe61d;
</span>
</Tooltip>
</div>
}
style={{ marginTop: 24 }}
validateStatus={check && !passRate ? 'error' : ''}
help={check && !passRate && '请输入及格线'}
required>
<InputNumber
value={passRate}
min={0}
max={100}
onChange={(value: any) => {
setPassRate(parseInt(value));
}}
style={{ width: 100 }}
/>
<span style={{ marginLeft: 4 }}>%</span>
<span style={{ marginLeft: 16, color: '#999' }}>
{` 总分(${paperInfo.totalScore || 0})*及格线(${passRate || 0}%)=及格分数(${passScore})`}
</span>
</Form.Item>
<Form.Item
label='考试有效期'
validateStatus={check && !examStartTime ? 'error' : ''}
help={check && !examStartTime && '请选择考试起止时间'}
required>
<RangePicker
style={{ width: 320 }}
showTime={{ defaultValue: [moment().add(5, 'minutes'), moment().add(5, 'minutes')] }}
ranges={{
近七天: [moment().add(5, 'minute'), moment().add(6, 'day').endOf('day')],
1个月: [moment().add(5, 'minute'), moment().add(1, 'month').endOf('day')],
3个月: [moment().add(5, 'minute'), moment().add(3, 'month').endOf('day')],
}}
disabledDate={disabledDate}
value={[examStartTime ? moment(Number(examStartTime)) : null, examEndTime ? moment(Number(examEndTime)) : null]}
disabledTime={disabledRangeTime}
format='YYYY/MM/DD HH:mm'
onChange={(date: any) => {
setStartTime(date && date[0]?.valueOf());
setExamEndTime(date && date[1]?.valueOf());
}}
/>
</Form.Item>
<Form.Item label='考试时长' validateStatus={check && !examDuration ? 'error' : ''} help={check && !examDuration && '请输入考试时长'} required>
<InputNumber
value={examDuration}
max={1440}
min={1}
onChange={(value: any) => {
setExamDuration(parseInt(value) as any);
}}
style={{ width: 100 }}
/>
<span style={{ marginLeft: 4 }}>分钟</span>
<span style={{ marginLeft: 16, color: '#999' }}>{` 时长不能超过1440分钟(24小时)`}</span>
</Form.Item>
<Form.Item label='考试说明' validateStatus={check && desclen > 1000 ? 'error' : ''} help={check && desclen > 1000 && '最多只能输入1000个字'}>
{(getData || props.type === 'add') && (
<GraphicsEditor
maxLimit={1000}
isIntro={true}
detail={{
content: examDesc,
}}
onChange={(val: any, len: any) => {
setExamDesc(val);
setDescLen(len);
}}
/>
)}
</Form.Item>
<div className='title' style={{ marginTop: 40 }}>
考试设置
</div>
<Form.Item label='身份验证' required>
<div style={{ display: 'flex', marginLeft: 4 }}>
<Switch
checked={needPhone == 'NEED_PHONE_VERIFY'}
onChange={(val) => {
setNeedPhone(val ? 'NEED_PHONE_VERIFY' : 'DO_NOT_NEED_PHONE_VERIFY');
}}></Switch>
<div style={{ position: 'relative', left: 8, color: '#999' }}>
{needPhone == 'NEED_PHONE_VERIFY' ? '已开启,学员需绑定手机号才可参与考试' : '已关闭,学员无需绑定手机号即可参与考试'}
</div>
</div>
</Form.Item>
<Form.Item label='选项乱序' required>
<div style={{ display: 'flex', marginLeft: 4 }}>
<Switch
checked={needOptionDisorder == 'OPTION_RANDOM'}
onChange={(val) => {
setNeedOptionDisorder(val ? 'OPTION_RANDOM' : 'OPTION_SORT');
}}></Switch>
<div style={{ position: 'relative', left: 8, color: '#999' }}>
{needOptionDisorder == 'OPTION_RANDOM' ? '已开启,选项随机排序' : '已关闭,选项按设置顺序排序'}
</div>
</div>
</Form.Item>
<Form.Item label='考试结果查看' required>
<Radio.Group
onChange={(e: any) => {
setResultShow(e.target.value);
}}
value={resultShow}>
<Radio value={'IMMEDIATELY'}>交卷后立即显示考试结果</Radio>
<Radio value={'AFTER_EXAM_END'}>到达考试截止日期才显示结果</Radio>
</Radio.Group>
</Form.Item>
<Form.Item label=' 考试结果内容' required>
<Radio.Group
onChange={(e: any) => {
setResultContent(e.target.value);
}}
value={resultContent}>
<Radio value={'PASS_AND_SCORE'}>显示考试分数和是否及格</Radio>
<Radio value={'ONLY_SCORE'}>仅显示考试分数</Radio>
<Radio value={'ONLY_PASS'}>仅显示是否及格</Radio>
</Radio.Group>
</Form.Item>
<Form.Item label='答案与解析' required>
<Radio.Group
onChange={(e: any) => {
setAnswerAnalysis(e.target.value);
}}
value={answerAnalysis}>
<Radio value={'ANALYSE_AND_RIGHT_OR_WRONG'}>显示对错与解析</Radio>
<Radio value={'RIGHT_OR_WRONG'}>仅显示对错</Radio>
<Radio value={'CAN_NOT_CHECK'}>都不显示</Radio>
</Radio.Group>
</Form.Item>
</Form>
</div>
</div>
{showModal && (
<SelectPaperModal
onSelect={(info: any) => {
setPaperInfo(info);
}}
paperInfo={paperInfo}
close={() => {
setShowModal(false);
}}></SelectPaperModal>
)}
<div className='footer shrink-footer'>
<Button onClick={handleGoBack}>取消</Button>
<Button
onClick={() => {
setPreview(true);
}}>
预览
</Button>
<Button type='primary' onClick={handleSave}>
保存
</Button>
</div>
{preview && (
<PreviewModal
info={{
setCheck(true);
const param = {
paperId,
startTime: examStartTime,
endTime: examEndTime,
......@@ -514,17 +138,409 @@ function AddExam(props: any) {
resultContent,
answerAnalysis,
resultShow,
examDuration,
examDuration: (examDuration || 0) * 60 * 1000,
passScore,
examTotal,
totalScore: paperInfo.totalScore,
}}
onClose={() => {
setPreview(false);
}}></PreviewModal>
)}
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0,
examId: ''
}
if (!param.examName) {
message.warning('请输入考试名称');
return
}
if (param.examName && param.examName.length > 40) {
message.warning('考试名称最多40字');
return
}
if(checkExist(param.examName)) {
message.warning('此考试名称已存在');
return
}
if (!paperId) {
message.warning('请选择试卷');
return
}
if (!passRate) {
message.warning('请输入及格线');
return
}
if (!examStartTime || !examEndTime) {
message.warning('请选择考试起止时间');
return
}
if (Number(examStartTime) < moment().valueOf()) {
message.warning('开始时间不能早于现在');
return
}
if (!examDuration) {
message.warning('请输入考试时长');
return
}
if (examStartTime + (examDuration as any) * 60 * 1000 > examEndTime) {
message.warning('考试时长不得超过考试有效期时长');
return
}
if (desclen > 1000) {
message.warning('内容过长,不能超过1000字');
return
}
request.current = true;
setTimeout(() => {
request.current = false
}, 2000)
if (props.type === 'edit') {
param.examId = match.params.id;
}
Service.Hades(props.type === 'edit' ? 'public/hades/editExam' : "public/hades/createExam", param).then((res) => {
message.success(props.type === 'edit' ? '编辑成功' : '创建成功');
switch (props.type) {
case "organizeExam": // 试卷列表-组织考试进入
case "newPaperToAddExam": // 组卷保存组织考试
case "editPaperToAddExam":
window.RCHistory.push("/examination-manage-index")
break;
case "add":
case "edit": // 考试列表-新建或编辑
case "copy": // 考试列表-新建或编辑
props.freshList()
props.history.goBack();
break;
}
})
}
function disabledRangeTime(date: any, type: any) {
if (moment(date).isSame(moment(), 'day')) {
return {
disabledHours: () => {
const hours = [];
for (let i = 0; i < moment().hour(); i++) {
hours.push(i);
}
return hours;
},
disabledMinutes: () => {
const currentMinute = moment().minute();
const currentHour = moment(date).hour();
const minutes = [];
if (currentHour === moment().hour()) {
for (let i = 0; i < currentMinute; i++) {
minutes.push(i);
}
}
return minutes;
},
};
}
return {
disabledHours: () => [],
disabledMinutes: () => [],
disabledSeconds: () => [],
};
}
function handleGoBack() {
Modal.confirm({
title: '确定要返回吗?',
content: '返回后,本次编辑的内容将不被保存',
okText: '确认返回',
cancelText: '留在本页',
icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>,
onOk: () => {
window.RCHistory.push("/examination-manage-index")
}
})
}
// 校验考试名称是否存在
function checkExist(examName: any) {
var result:any = null;
examList.forEach((item:any) => {
if (result != null) {
return result;
}
if (props.type === 'edit') {
if (item.examName === examName && item.examId !== match.params.id) {
result = item;
}
} else {
if (item.examName === examName) {
result = item;
}
}
});
return result;
};
let title = '';
switch (props.type) {
case 'add':
case "organizeExam":
case "newPaperToAddExam":
case "editPaperToAddExam":
title = '新建考试';
break;
case 'edit':
title = '编辑考试';
break;
case 'copy':
title = '复制考试';
break;
default:
break;
}
return <div className="page examPage">
<Breadcrumbs navList={title} goBack={handleGoBack} />
<div className="box">
<div className="show-tips">
<ShowTips message="请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利" />
</div> <div className="form">
<div className="title">基本信息</div>
<Form
labelCol={{ span: 3 }}
wrapperCol={{ span: 14 }}
layout="horizontal"
>
<Form.Item label="考试名称"
validateStatus={(check && (!examName || (examName.length > 40 || checkExist(examName)) )) ? 'error' : ''}
help={check && (!examName ? '请输入考试名称' : (examName.length > 40 ? '考试名称最多40字' : (checkExist(examName) && '此考试名称已存在')))}
required>
<Input placeholder='请输入考试名称(40字以内)' maxLength={40} value={examName} onChange={(e) => {
setExamName(e.target.value)
}} style={{ width: 320 }} />
</Form.Item>
<Form.Item label="选择试卷"
validateStatus={(check && !paperId) ? 'error' : ''}
help={check && !paperId && '请选择试卷'}
required>
<Button onClick={() => { setShowModal(true) }} >{paperInfo.paperId ? '重新选择' : '选择试卷'}</Button>
{
paperInfo.paperId && <div className="paperTitle"><img src="https://image.xiaomaiketang.com/xm/pY5imEhjzw.png" alt="" /> {paperInfo.paperName}</div>
}
{
paperInfo.paperId && <div className="table">
<div className="header">
<div className="item">单选题</div>
<div className="item">多选题</div>
<div className="item">判断题</div>
<div className="item">填空题</div>
<div className="item long">不定项选择题</div>
<div className="item">合计</div>
</div>
<div className="body-list">
<div className="item">{paperInfo.singleChoiceCnt || 0}</div>
<div className="item">{paperInfo.multiChoiceCnt || 0}</div>
<div className="item">{paperInfo.judgeCnt || 0}</div>
<div className="item">{paperInfo.gapFillingCnt || 0}</div>
<div className="item long">{paperInfo.indefiniteChoiceCnt || 0}</div>
<div className="item">{examTotal}</div>
</div>
<div className="body-list">
<div className="item">{paperInfo.singleChoiceScore || 0}</div>
<div className="item">{paperInfo.multiChoiceScore || 0}</div>
<div className="item">{paperInfo.judgeScore || 0}</div>
<div className="item">{paperInfo.gapFillingScore || 0}</div>
<div className="item long">{paperInfo.indefiniteChoiceScore || 0}</div>
<div className="item">{paperInfo.totalScore || 0}</div>
</div>
</div>
}
</Form.Item>
<Form.Item
label={<div>
<span>及格线</span>
<Tooltip title="默认为选中试卷所设置的及格线,可修改">
<span className="icon iconfont" style={{ color: '#BFBFBF', marginLeft: 4 }}>&#xe61d;</span>
</Tooltip>
</div>}
style={{ marginTop: 24 }}
validateStatus={(check && !passRate) ? 'error' : ''}
help={check && !passRate && '请输入及格线'}
required
>
<InputNumber value={passRate} min={0} max={100} onChange={(value: any) => { setPassRate(parseInt(value)) }} style={{ width: 100 }} />
<span style={{ marginLeft: 4 }}>%
</span>
<span style={{ marginLeft: 16, color: "#999" }}>
{` 总分(${paperInfo.totalScore || 0})*及格线(${passRate || 0}%)=及格分数(${passScore})`}</span>
</Form.Item>
<Form.Item label="考试有效期"
validateStatus={(check && !examStartTime) ? 'error' : ''}
help={check && !examStartTime && '请选择考试起止时间'}
required>
<RangePicker
style={{ width: 320 }}
showTime={{ defaultValue: [moment().add(5, 'minutes'), moment().add(5, 'minutes')] }}
ranges={{
'近七天': [moment().add(5, 'minute'), moment().add(6, 'day').endOf('day')],
'近1个月': [moment().add(5, 'minute'), moment().add(1, 'month').endOf('day')],
'近3个月': [moment().add(5, 'minute'), moment().add(3, 'month').endOf('day')],
}}
disabledDate={disabledDate}
value={[
examStartTime ? moment(Number(examStartTime)) : null,
examEndTime ? moment(Number(examEndTime)) : null
]}
disabledTime={disabledRangeTime}
format="YYYY/MM/DD HH:mm"
onChange={(date: any) => {
setStartTime(date && date[0]?.valueOf());
setExamEndTime(date && date[1]?.valueOf());
}}
/>
</Form.Item>
<Form.Item label="考试时长"
validateStatus={(check && !examDuration) ? 'error' : ''}
help={check && !examDuration && '请输入考试时长'}
required>
<InputNumber value={examDuration} max={1440} min={1} onChange={(value: any) => { setExamDuration(parseInt(value) as any) }} style={{ width: 100 }} />
<span style={{ marginLeft: 4 }}>分钟
</span>
<span style={{ marginLeft: 16, color: "#999" }}>
{` 时长不能超过1440分钟(24小时)`}</span>
</Form.Item>
<Form.Item label="考试说明"
validateStatus={(check && (desclen > 1000)) ? 'error' : ''}
help={check && (desclen > 1000) && '最多只能输入1000个字'}
>
{
(getData || (props.type === 'add')) && <GraphicsEditor
maxLimit={1000}
isIntro={true}
detail={{
content: examDesc
}}
onChange={(val: any, len: any) => { setExamDesc(val); setDescLen(len) }}
/>
}
</Form.Item>
<div className="title" style={{ marginTop: 40 }}>考试设置</div>
<Form.Item label="身份验证" required>
<div style={{ display: 'flex', marginLeft: 4, }}>
<Switch style={{ position: 'relative', top: 6 }}
checked={needPhone == 'NEED_PHONE_VERIFY'}
onChange={(val) => { setNeedPhone(val ? 'NEED_PHONE_VERIFY' : 'DO_NOT_NEED_PHONE_VERIFY') }}
></Switch>
<div style={{ position: 'relative', top: 3, left: 8, color: "#999" }}><p>开启:需要绑定手机号的学员才能参加考试</p>
<p>关闭:微信/企业微信登陆直接参加考试</p></div>
</div>
</Form.Item>
<Form.Item label="选项乱序" required>
<div style={{ display: 'flex', marginLeft: 4, }}>
<Switch style={{ position: 'relative', top: 6 }}
checked={needOptionDisorder == 'OPTION_RANDOM'}
onChange={(val) => { setNeedOptionDisorder(val ? 'OPTION_RANDOM' : 'OPTION_SORT') }}
></Switch>
<div style={{ position: 'relative', top: 3, left: 8, color: "#999" }}><p>开启:选择题的选项随机排序</p>
<p>关闭:选择题按题目原有顺序展示</p></div>
</div>
</Form.Item>
<Form.Item label="考试结果查看" required>
<Radio.Group onChange={(e: any) => { setResultShow(e.target.value) }} value={resultShow}>
<Radio value={'IMMEDIATELY'}>交卷后立即显示考试结果</Radio>
<Radio value={'AFTER_EXAM_END'}>到达考试截止日期才显示结果</Radio>
</Radio.Group>
</Form.Item>
<Form.Item label=" 考试结果内容" required>
<Radio.Group onChange={(e: any) => { setResultContent(e.target.value) }} value={resultContent}>
<Radio value={'PASS_AND_SCORE'}>显示考试分数和是否及格</Radio>
<Radio value={'ONLY_SCORE'}>仅显示考试分数</Radio>
<Radio value={'ONLY_PASS'}>仅显示是否及格</Radio>
</Radio.Group>
</Form.Item>
<Form.Item label="答案与解析" required>
<Radio.Group onChange={(e: any) => { setAnswerAnalysis(e.target.value) }} value={answerAnalysis}>
<Radio value={'ANALYSE_AND_RIGHT_OR_WRONG'}>显示对错与解析</Radio>
<Radio value={'RIGHT_OR_WRONG'}>仅显示对错</Radio>
<Radio value={'CAN_NOT_CHECK'}>都不显示</Radio>
</Radio.Group>
</Form.Item>
</Form>
</div>
</div>
{
showModal && <SelectPaperModal onSelect={(info: any) => {
setPaperInfo(info)
}} paperInfo={paperInfo} close={() => { setShowModal(false) }}></SelectPaperModal>
}
<div className="footer shrink-footer">
<Button onClick={handleGoBack}>取消</Button>
<Button onClick={() => { setPreview(true) }}>预览</Button>
<Button type="primary" onClick={handleSave}>保存</Button>
</div>
{
preview && <PreviewModal
info={{
paperId,
startTime: examStartTime,
endTime: examEndTime,
examName,
passRate: passRate / 100,
examStartTime,
examEndTime,
examDesc,
needPhone,
needOptionDisorder,
resultContent,
answerAnalysis,
resultShow,
examDuration,
passScore,
examTotal,
totalScore: paperInfo.totalScore
}}
onClose={() => { setPreview(false) }}></PreviewModal>
}
</div>
);
}
export default withRouter(AddExam);
export default withRouter(AddExam);
\ No newline at end of file
/*
* @Author: yuananting
* @Date: 2021-03-27 16:15:13
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-07-01 16:30:38
* @LastEditors: yuananting
* @LastEditTime: 2021-07-13 12:01:37
* @Description: 助学工具-新建/复制/编辑试卷
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -118,7 +118,7 @@ class OperatePaper extends Component {
portionScore: 0,
totalQuestion: 0,
},
]
],
};
}
......@@ -352,20 +352,6 @@ class OperatePaper extends Component {
return result;
};
validatePaperName = (paperName) => {
if (this.state.check && !paperName) {
return '请输入试卷名称';
}
if (this.checkExist(paperName)) {
return '该试卷名称已存在';
}
if (paperName && paperName.length > 40) {
return '试卷名称最多40字';
}
};
// 保存试卷
savePaper = (saveType) => {
this.setState({ check: true });
......@@ -373,8 +359,17 @@ class OperatePaper extends Component {
const categoryId = getParameterByName('categoryId');
const { match } = this.props;
let questionList = [];
if (!formData.passRate || !formData.paperName || this.checkExist(formData.paperName) || (formData.paperName && formData.paperName.length > 40)) {
return;
if(!formData.paperName) {
return message.warning('请输入试卷名称')
}
if(formData.paperName.length > 40) {
return message.warning('试卷名称最多40字')
}
if (this.checkExist(formData.paperName)) {
return message.warning('该试卷名称已存在')
}
if(!formData.passRate) {
return message.warning('请输入及格线')
}
if (selectQuestionList.length === 0) {
return message.warning('请选择题目');
......@@ -410,18 +405,14 @@ class OperatePaper extends Component {
});
} else {
message.success(currentOperate === 'new' ? '新建成功' : '复制成功');
window.RCHistory.push({
pathname: `/paper-manage-index?categoryId=${categoryId}`,
});
window.RCHistory.goBack();
Bus.trigger('queryPaperPageList', categoryId, selectQuestionList.length);
Bus.trigger('queryCategoryTree', 'remain');
}
}
})
.catch((e) => {
window.RCHistory.push({
pathname: `/paper-manage-index?categoryId=${categoryId}`,
});
window.RCHistory.goBack();
Bus.trigger('queryPaperPageList', categoryId, selectQuestionList.length);
Bus.trigger('queryCategoryTree', 'remain');
});
......@@ -438,17 +429,13 @@ class OperatePaper extends Component {
});
} else {
message.success('编辑成功');
window.RCHistory.push({
pathname: `/paper-manage-index?categoryId=${categoryId}`,
});
window.RCHistory.goBack();
Bus.trigger('queryPaperPageList', categoryId, selectQuestionList.length);
}
}
})
.catch((e) => {
window.RCHistory.push({
pathname: `/paper-manage-index?categoryId=${categoryId}`,
});
window.RCHistory.goBack();
Bus.trigger('queryPaperPageList', categoryId, selectQuestionList.length);
});
}
......@@ -482,9 +469,7 @@ class OperatePaper extends Component {
cancelText: '留在本页',
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
onOk: () => {
window.RCHistory.push({
pathname: `/paper-manage-index?categoryId=${getParameterByName('categoryId')}`,
});
window.RCHistory.goBack()
Bus.trigger('queryCategoryTree', 'remain');
Bus.trigger('queryPaperPageList', getParameterByName('categoryId'), 0);
},
......@@ -822,8 +807,8 @@ class OperatePaper extends Component {
name='paperName'
label='试卷名称:'
required
validateStatus={this.validatePaperName(paperName) ? 'error' : ''}
help={this.validatePaperName(paperName)}>
validateStatus={(check && (!paperName || paperName.length > 40 || this.checkExist(paperName))) ? 'error' : ''}
help={check && (!paperName ? '请输入试卷名称' : (paperName.length > 40 ? '试卷名称最多40字' : (this.checkExist(paperName) && '该试卷名称已存在')))}>
<Input
value={paperName}
autoComplete='off'
......
......@@ -2,7 +2,7 @@
* @Author: yuananting
* @Date: 2021-02-25 11:23:47
* @LastEditors: yuananting
* @LastEditTime: 2021-06-03 17:13:30
* @LastEditTime: 2021-07-13 15:16:52
* @Description: 助学工具-题库-试卷列表数据
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -362,9 +362,9 @@ class PaperList extends Component {
{isPermiss && (
<div
className="record-operate__item"
onClick={() => this.copyPaper(record)}
onClick={() => this.organizeExam(record)}
>
复制
组织考试
</div>
)}
{isPermiss && (
......@@ -391,7 +391,7 @@ class PaperList extends Component {
<span onClick={() => this.editPaper(item)}>编辑</span>
</Menu.Item>
<Menu.Item key="organizeExam">
<span onClick={() => this.organizeExam(item)}>组织考试</span>
<span onClick={() => this.copyPaper(item)}>复制</span>
</Menu.Item>
<Menu.Item key="del">
<span onClick={() => this.confirmDeletePaper(item)}>删除</span>
......
/*
* @Author: yuananting
* @Date: 2021-02-25 13:46:35
* @LastEditors: wufan
* @LastEditTime: 2021-05-24 11:32:47
* @LastEditors: yuananting
* @LastEditTime: 2021-07-13 11:51:56
* @Description: 助学工具-题库-新建/编辑题目
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -161,9 +161,7 @@ class OperateQuestion extends Component {
<span className="icon iconfont default-confirm-icon">&#xe6f4;</span>
),
onOk: () => {
window.RCHistory.push({
pathname: `/question-manage-index?categoryId=${getParameterByName("categoryId")}`,
});
window.RCHistory.goBack();
Bus.trigger("queryCategoryTree", "remain");
Bus.trigger("queryQuestionPageList", getParameterByName("categoryId"));
},
......@@ -248,9 +246,7 @@ class OperateQuestion extends Component {
this.handleRest(type);
}
if (next === "close") {
window.RCHistory.push({
pathname: `/question-manage-index?categoryId=${categoryId}`,
});
window.RCHistory.goBack();
Bus.trigger("queryCategoryTree", "remain")
Bus.trigger("queryQuestionPageList", categoryId)
}
......@@ -271,9 +267,7 @@ class OperateQuestion extends Component {
this.handleRest(type);
}
if (next === "close") {
window.RCHistory.push({
pathname: `/question-manage-index?categoryId=${categoryId}`,
});
window.RCHistory.goBack();
Bus.trigger("queryCategoryTree", "remain")
Bus.trigger("queryQuestionPageList", categoryId)
}
......
......@@ -3,108 +3,86 @@
* @Date: 2020-04-28 18:05:30
* @LastEditors: wufan
* @LastEditTime: 2020-12-26 14:37:23
* @Description:
* @Description:
*/
import mainRoutes from './config/mainRoutes';
import redirectRoutes from './config/redirectRoutes';
import React from 'react'
import { Redirect,HashRouter as Router,Route ,Switch} from 'react-router-dom';
import React from 'react';
import { HashRouter as Router, Route, Switch } from 'react-router-dom';
import { createHashHistory } from 'history';
import App from '../modules/root/App';
import AppContext from '@/modules/root/AppContent';
import Login from '../modules/root/Login';
import CollegeManagePage from '../modules/root/CollegeManagePage';
import CreateCollege from '../modules/root/CreateCollege';
import _ from 'underscore';
import { asyncComponent } from 'react-async-component'
import SwitchRoute from '@/modules/root/SwitchRoute';
import ErrorCollege from '@/modules/root/ErrorCollege';
const history = createHashHistory();
window.RCHistory = _.extend({}, history, {
push: (obj: any) => {
history.push(obj)
},
pushState: (obj: any) => {
history.push(obj)
},
pushStateWithStatus: (obj: any) => {
history.push(obj)
},
goBack: history.goBack,
location: history.location,
replace: (obj: any) => {
history.replace(obj)
}
push: (obj: any) => {
history.push(obj);
},
pushState: (obj: any) => {
history.push(obj);
},
pushStateWithStatus: (obj: any) => {
history.push(obj);
},
goBack: history.goBack,
location: history.location,
replace: (obj: any) => {
history.replace(obj);
},
});
const cache:any = {
path: '',
component: null
}
function dynamic (component:any) {
const resolveComponent = component
return asyncComponent({
resolve: () => {
const ts = resolveComponent()
return ts
},
})
}
export const RootRouter = () => {
return (
<Router {...history}>
<Switch>
<Route key="1" exact path="/login" render={() => <Login />} />
<Route key="2" exact path="/switch-route" render={() => <SwitchRoute />} />
<Route key="3" exact path="/college-manage" render={() => <CollegeManagePage />} />
<Route key="4" exact path="/college-manage/create" render={() => <CreateCollege />} />
<Route key="6" exact path="/error-college" render={() => <ErrorCollege />} />
<Route key="5" path="/" render={() => <AppContext />} />
</Switch>
</Router>
)
}
return (
<Router {...history}>
<Switch>
<Route key='1' exact path='/login' render={() => <Login />} />
<Route key='2' exact path='/switch-route' render={() => <SwitchRoute />} />
<Route key='3' exact path='/college-manage' render={() => <CollegeManagePage />} />
<Route key='4' exact path='/college-manage/create' render={() => <CreateCollege />} />
<Route key='6' exact path='/error-college' render={() => <ErrorCollege />} />
<Route key='5' path='/' render={() => <AppContext />} />
</Switch>
</Router>
);
};
export const MainRoutes = () => {
return (
<Switch>
{
_.map(mainRoutes, ({
path,
component,
}, key) => {
return <Route
key={key}
path={path}
render={() => {
const Component = component;
return <Component />
}}
/>
})
}
</Switch>
)
}
return (
<Switch>
{_.map(mainRoutes, ({ path, component }, key) => {
return (
<Route
key={key}
path={path}
render={() => {
const Component = component;
return <Component />;
}}
/>
);
})}
</Switch>
);
};
export const RedirectRoutes = () => {
return (
<Switch>
{
_.map(redirectRoutes, ({
path,
component,
}, key) => {
return <Route
key={key}
path={path}
render={() => {
const Component = component;
return <Component />
}}
/>
})
}
</Switch>
)
}
return (
<Switch>
{_.map(redirectRoutes, ({ path, component }, key) => {
return (
<Route
key={key}
path={path}
render={() => {
const Component = component;
return <Component />;
}}
/>
);
})}
</Switch>
);
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment