20241113-a
This commit is contained in:
5
.dockerignore
Normal file
5
.dockerignore
Normal file
@@ -0,0 +1,5 @@
|
||||
.history
|
||||
node_modules
|
||||
build
|
||||
dist
|
||||
README.md
|
||||
36
.github/workflows/publish.yml
vendored
Normal file
36
.github/workflows/publish.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
|
||||
jobs:
|
||||
call_workflow_build_node:
|
||||
uses: infrastructure/reusing_workflows/.github/workflows/build_node.yml@main
|
||||
with:
|
||||
node-version: '16.x'
|
||||
dist-dir: 'dist'
|
||||
artifact-name: 'artifact'
|
||||
secrets: inherit
|
||||
|
||||
call_workflow_publish_docker:
|
||||
uses: infrastructure/reusing_workflows/.github/workflows/publish_docker.yml@main
|
||||
with:
|
||||
artifact-name: 'artifact'
|
||||
docker_context: '.'
|
||||
dockerfile_path: './docker/Dockerfile'
|
||||
platforms: ${{ github.event_name == 'pull_request' && 'linux/amd64' || 'linux/amd64,linux/arm64' }}
|
||||
secrets: inherit
|
||||
needs: call_workflow_build_node
|
||||
|
||||
call_workflow_deploy_compose:
|
||||
uses: infrastructure/reusing_workflows/.github/workflows/deploy_compose.yml@main
|
||||
with:
|
||||
stack_file_name: compose.admin.yml
|
||||
env_file_name: ${{ github.event_name == 'pull_request' && '.env.dev' || '.env.base' }},.env.version
|
||||
args: up -d
|
||||
pull_images_first: true
|
||||
secrets: inherit
|
||||
needs: call_workflow_publish_docker
|
||||
37
.gitignore
vendored
Normal file
37
.gitignore
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
/dist
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
yarn.lock
|
||||
.eslintcache
|
||||
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@@ -0,0 +1 @@
|
||||
**/dist
|
||||
6
.prettierrc.yaml
Normal file
6
.prettierrc.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
trailingComma: 'es5'
|
||||
tabWidth: 2
|
||||
semi: true
|
||||
singleQuote: true
|
||||
bracketSpacing: true
|
||||
endOfLine: 'auto'
|
||||
7
.vscode/settings.json
vendored
Normal file
7
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules\\typescript\\lib",
|
||||
"CodeGPT.Autocomplete.suggestionDelay": 0,
|
||||
"CodeGPT.Autocomplete.maxTokens": 1000,
|
||||
"CodeGPT.Autocomplete.provider": "Ollama - llama3:instruct",
|
||||
"CodeGPT.apiKey": "Ollama"
|
||||
}
|
||||
8
README.md
Normal file
8
README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
REACT_APP_BASE=/tianmuhu PUBLIC_URL=/tianmuhu/admin npm run build
|
||||
|
||||
docker build -t ccr.ccs.tencentyun.com/olms/admin:1.1-subpath-changzhou -f Dockerfile.subpath --build-arg BASE_PATH=/changzhou .
|
||||
|
||||
系统配置
|
||||
系统LOGO
|
||||
微信服务
|
||||
考勤客户端下载路径
|
||||
38
babel.config.json
Normal file
38
babel.config.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"edge": "17",
|
||||
"firefox": "60",
|
||||
"chrome": "67",
|
||||
"safari": "11.1"
|
||||
},
|
||||
"useBuiltIns": "usage",
|
||||
"corejs": "3.6.5"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@babel/preset-react",
|
||||
{
|
||||
"runtime": "classic"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@babel/preset-typescript",
|
||||
{
|
||||
"isTSX": true,
|
||||
"allExtensions": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": [
|
||||
[
|
||||
"@babel/plugin-proposal-decorators",
|
||||
{
|
||||
"legacy": true
|
||||
}
|
||||
],
|
||||
]
|
||||
}
|
||||
106
config/env.js
Normal file
106
config/env.js
Normal file
@@ -0,0 +1,106 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const paths = require('./paths');
|
||||
|
||||
// Make sure that including paths.js after env.js will read .env variables.
|
||||
delete require.cache[require.resolve('./paths')];
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV;
|
||||
if (!NODE_ENV) {
|
||||
throw new Error(
|
||||
'The NODE_ENV environment variable is required but was not specified.'
|
||||
);
|
||||
}
|
||||
|
||||
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
|
||||
const dotenvFiles = [
|
||||
`${paths.dotenv}.${NODE_ENV}.local`,
|
||||
// Don't include `.env.local` for `test` environment
|
||||
// since normally you expect tests to produce the same
|
||||
// results for everyone
|
||||
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
|
||||
`${paths.dotenv}.${NODE_ENV}`,
|
||||
paths.dotenv,
|
||||
].filter(Boolean);
|
||||
|
||||
// Load environment variables from .env* files. Suppress warnings using silent
|
||||
// if this file is missing. dotenv will never modify any environment variables
|
||||
// that have already been set. Variable expansion is supported in .env files.
|
||||
// https://github.com/motdotla/dotenv
|
||||
// https://github.com/motdotla/dotenv-expand
|
||||
dotenvFiles.forEach((dotenvFile) => {
|
||||
if (fs.existsSync(dotenvFile)) {
|
||||
require('dotenv-expand').expand(
|
||||
require('dotenv').config({
|
||||
path: dotenvFile,
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// We support resolving modules according to `NODE_PATH`.
|
||||
// This lets you use absolute paths in imports inside large monorepos:
|
||||
// https://github.com/facebook/create-react-app/issues/253.
|
||||
// It works similar to `NODE_PATH` in Node itself:
|
||||
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
|
||||
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
|
||||
// Otherwise, we risk importing Node.js core modules into an app instead of webpack shims.
|
||||
// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
|
||||
// We also resolve them to make sure all tools using them work consistently.
|
||||
const appDirectory = fs.realpathSync(process.cwd());
|
||||
process.env.NODE_PATH = (process.env.NODE_PATH || '')
|
||||
.split(path.delimiter)
|
||||
.filter((folder) => folder && !path.isAbsolute(folder))
|
||||
.map((folder) => path.resolve(appDirectory, folder))
|
||||
.join(path.delimiter);
|
||||
|
||||
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
|
||||
// injected into the application via DefinePlugin in webpack configuration.
|
||||
const REACT_APP = /^REACT_APP_/i;
|
||||
|
||||
function getClientEnvironment(publicUrl) {
|
||||
const raw = Object.keys(process.env)
|
||||
.filter((key) => REACT_APP.test(key))
|
||||
.reduce(
|
||||
(env, key) => {
|
||||
env[key] = process.env[key];
|
||||
return env;
|
||||
},
|
||||
{
|
||||
// Useful for determining whether we’re running in production mode.
|
||||
// Most importantly, it switches React into the correct mode.
|
||||
NODE_ENV: process.env.NODE_ENV || 'development',
|
||||
// Useful for resolving the correct path to static assets in `public`.
|
||||
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
|
||||
// This should only be used as an escape hatch. Normally you would put
|
||||
// images into the `src` and `import` them in code to get their paths.
|
||||
PUBLIC_URL: publicUrl,
|
||||
// We support configuring the sockjs pathname during development.
|
||||
// These settings let a developer run multiple simultaneous projects.
|
||||
// They are used as the connection `hostname`, `pathname` and `port`
|
||||
// in webpackHotDevClient. They are used as the `sockHost`, `sockPath`
|
||||
// and `sockPort` options in webpack-dev-server.
|
||||
WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST,
|
||||
WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,
|
||||
WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
|
||||
// Whether or not react-refresh is enabled.
|
||||
// react-refresh is not 100% stable at this time,
|
||||
// which is why it's disabled by default.
|
||||
// It is defined here so it is available in the webpackHotDevClient.
|
||||
FAST_REFRESH: process.env.FAST_REFRESH !== 'false',
|
||||
}
|
||||
);
|
||||
// Stringify all values so we can feed into webpack DefinePlugin
|
||||
const stringified = {
|
||||
'process.env': Object.keys(raw).reduce((env, key) => {
|
||||
env[key] = JSON.stringify(raw[key]);
|
||||
return env;
|
||||
}, {}),
|
||||
};
|
||||
|
||||
return { raw, stringified };
|
||||
}
|
||||
|
||||
module.exports = getClientEnvironment;
|
||||
70
config/getHttpsConfig.js
Normal file
70
config/getHttpsConfig.js
Normal file
@@ -0,0 +1,70 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const paths = require('./paths');
|
||||
|
||||
// Ensure the certificate and key provided are valid and if not
|
||||
// throw an easy to debug error
|
||||
function validateKeyAndCerts({ cert, key, keyFile, crtFile }) {
|
||||
let encrypted;
|
||||
try {
|
||||
// publicEncrypt will throw an error with an invalid cert
|
||||
encrypted = crypto.publicEncrypt(cert, Buffer.from('test'));
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
`The certificate "${chalk.yellow(crtFile)}" is invalid.\n${
|
||||
err.message
|
||||
}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// privateDecrypt will throw an error with an invalid key
|
||||
crypto.privateDecrypt(key, encrypted);
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
`The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${
|
||||
err.message
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Read file and throw an error if it doesn't exist
|
||||
function readEnvFile(file, type) {
|
||||
if (!fs.existsSync(file)) {
|
||||
throw new Error(
|
||||
`You specified ${chalk.cyan(
|
||||
type
|
||||
)} in your env, but the file "${chalk.yellow(
|
||||
file
|
||||
)}" can't be found.`
|
||||
);
|
||||
}
|
||||
return fs.readFileSync(file);
|
||||
}
|
||||
|
||||
// Get the https config
|
||||
// Return cert files if provided in env, otherwise just true or false
|
||||
function getHttpsConfig() {
|
||||
const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env;
|
||||
const isHttps = HTTPS === 'true';
|
||||
|
||||
if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) {
|
||||
const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE);
|
||||
const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE);
|
||||
const config = {
|
||||
cert: readEnvFile(crtFile, 'SSL_CRT_FILE'),
|
||||
key: readEnvFile(keyFile, 'SSL_KEY_FILE'),
|
||||
};
|
||||
|
||||
validateKeyAndCerts({ ...config, keyFile, crtFile });
|
||||
return config;
|
||||
}
|
||||
return isHttps;
|
||||
}
|
||||
|
||||
module.exports = getHttpsConfig;
|
||||
134
config/modules.js
Normal file
134
config/modules.js
Normal file
@@ -0,0 +1,134 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const paths = require('./paths');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const resolve = require('resolve');
|
||||
|
||||
/**
|
||||
* Get additional module paths based on the baseUrl of a compilerOptions object.
|
||||
*
|
||||
* @param {Object} options
|
||||
*/
|
||||
function getAdditionalModulePaths(options = {}) {
|
||||
const baseUrl = options.baseUrl;
|
||||
|
||||
if (!baseUrl) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
||||
|
||||
// We don't need to do anything if `baseUrl` is set to `node_modules`. This is
|
||||
// the default behavior.
|
||||
if (path.relative(paths.appNodeModules, baseUrlResolved) === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Allow the user set the `baseUrl` to `appSrc`.
|
||||
if (path.relative(paths.appSrc, baseUrlResolved) === '') {
|
||||
return [paths.appSrc];
|
||||
}
|
||||
|
||||
// If the path is equal to the root directory we ignore it here.
|
||||
// We don't want to allow importing from the root directly as source files are
|
||||
// not transpiled outside of `src`. We do allow importing them with the
|
||||
// absolute path (e.g. `src/Components/Button.js`) but we set that up with
|
||||
// an alias.
|
||||
if (path.relative(paths.appPath, baseUrlResolved) === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Otherwise, throw an error.
|
||||
throw new Error(
|
||||
chalk.red.bold(
|
||||
"Your project's `baseUrl` can only be set to `src` or `node_modules`." +
|
||||
' Create React App does not support other values at this time.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get webpack aliases based on the baseUrl of a compilerOptions object.
|
||||
*
|
||||
* @param {*} options
|
||||
*/
|
||||
function getWebpackAliases(options = {}) {
|
||||
const baseUrl = options.baseUrl;
|
||||
|
||||
if (!baseUrl) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
||||
|
||||
if (path.relative(paths.appPath, baseUrlResolved) === '') {
|
||||
return {
|
||||
src: paths.appSrc,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get jest aliases based on the baseUrl of a compilerOptions object.
|
||||
*
|
||||
* @param {*} options
|
||||
*/
|
||||
function getJestAliases(options = {}) {
|
||||
const baseUrl = options.baseUrl;
|
||||
|
||||
if (!baseUrl) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
||||
|
||||
if (path.relative(paths.appPath, baseUrlResolved) === '') {
|
||||
return {
|
||||
'^src/(.*)$': '<rootDir>/src/$1',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getModules() {
|
||||
// Check if TypeScript is setup
|
||||
const hasTsConfig = fs.existsSync(paths.appTsConfig);
|
||||
const hasJsConfig = fs.existsSync(paths.appJsConfig);
|
||||
|
||||
if (hasTsConfig && hasJsConfig) {
|
||||
throw new Error(
|
||||
'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.'
|
||||
);
|
||||
}
|
||||
|
||||
let config;
|
||||
|
||||
// If there's a tsconfig.json we assume it's a
|
||||
// TypeScript project and set up the config
|
||||
// based on tsconfig.json
|
||||
if (hasTsConfig) {
|
||||
const ts = require(resolve.sync('typescript', {
|
||||
basedir: paths.appNodeModules,
|
||||
}));
|
||||
config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config;
|
||||
// Otherwise we'll check if there is jsconfig.json
|
||||
// for non TS projects.
|
||||
} else if (hasJsConfig) {
|
||||
config = require(paths.appJsConfig);
|
||||
}
|
||||
|
||||
config = config || {};
|
||||
const options = config.compilerOptions || {};
|
||||
|
||||
const additionalModulePaths = getAdditionalModulePaths(options);
|
||||
|
||||
return {
|
||||
additionalModulePaths: additionalModulePaths,
|
||||
webpackAliases: getWebpackAliases(options),
|
||||
jestAliases: getJestAliases(options),
|
||||
hasTsConfig,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = getModules();
|
||||
74
config/paths.js
Normal file
74
config/paths.js
Normal file
@@ -0,0 +1,74 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath');
|
||||
|
||||
// Make sure any symlinks in the project folder are resolved:
|
||||
// https://github.com/facebook/create-react-app/issues/637
|
||||
const appDirectory = fs.realpathSync(process.cwd());
|
||||
const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath);
|
||||
|
||||
// We use `PUBLIC_URL` environment variable or "homepage" field to infer
|
||||
// "public path" at which the app is served.
|
||||
// webpack needs to know it to put the right <script> hrefs into HTML even in
|
||||
// single-page apps that may serve index.html for nested URLs like /todos/42.
|
||||
// We can't use a relative path in HTML because we don't want to load something
|
||||
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
|
||||
const publicUrlOrPath = getPublicUrlOrPath(
|
||||
process.env.NODE_ENV === 'development',
|
||||
require(resolveApp('package.json')).homepage,
|
||||
process.env.PUBLIC_URL
|
||||
);
|
||||
|
||||
const buildPath = process.env.BUILD_PATH || 'dist';
|
||||
|
||||
const moduleFileExtensions = [
|
||||
'web.mjs',
|
||||
'mjs',
|
||||
'web.js',
|
||||
'js',
|
||||
'web.ts',
|
||||
'ts',
|
||||
'web.tsx',
|
||||
'tsx',
|
||||
'json',
|
||||
'web.jsx',
|
||||
'jsx',
|
||||
];
|
||||
|
||||
// Resolve file paths in the same order as webpack
|
||||
const resolveModule = (resolveFn, filePath) => {
|
||||
const extension = moduleFileExtensions.find((extension) =>
|
||||
fs.existsSync(resolveFn(`${filePath}.${extension}`))
|
||||
);
|
||||
|
||||
if (extension) {
|
||||
return resolveFn(`${filePath}.${extension}`);
|
||||
}
|
||||
|
||||
return resolveFn(`${filePath}.js`);
|
||||
};
|
||||
|
||||
// config after eject: we're in ./config/
|
||||
module.exports = {
|
||||
dotenv: resolveApp('.env'),
|
||||
appPath: resolveApp('.'),
|
||||
appBuild: resolveApp(buildPath),
|
||||
appPublic: resolveApp('public'),
|
||||
appHtml: resolveApp('public/index.html'),
|
||||
appIndexJs: resolveModule(resolveApp, 'src/index'),
|
||||
appEditorJs: resolveModule(resolveApp, 'src/editor'),
|
||||
appPackageJson: resolveApp('package.json'),
|
||||
appSrc: resolveApp('src'),
|
||||
appTsConfig: resolveApp('tsconfig.json'),
|
||||
appJsConfig: resolveApp('jsconfig.json'),
|
||||
yarnLockFile: resolveApp('yarn.lock'),
|
||||
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
|
||||
proxySetup: resolveApp('src/setupProxy.js'),
|
||||
appNodeModules: resolveApp('node_modules'),
|
||||
swSrc: resolveModule(resolveApp, 'src/service-worker'),
|
||||
publicUrlOrPath,
|
||||
};
|
||||
|
||||
module.exports.moduleFileExtensions = moduleFileExtensions;
|
||||
35
config/pnpTs.js
Normal file
35
config/pnpTs.js
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
const { resolveModuleName } = require('ts-pnp');
|
||||
|
||||
exports.resolveModuleName = (
|
||||
typescript,
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost
|
||||
) => {
|
||||
return resolveModuleName(
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost,
|
||||
typescript.resolveModuleName
|
||||
);
|
||||
};
|
||||
|
||||
exports.resolveTypeReferenceDirective = (
|
||||
typescript,
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost
|
||||
) => {
|
||||
return resolveModuleName(
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost,
|
||||
typescript.resolveTypeReferenceDirective
|
||||
);
|
||||
};
|
||||
621
config/webpack.config.js
Normal file
621
config/webpack.config.js
Normal file
@@ -0,0 +1,621 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const PnpWebpackPlugin = require('pnp-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
|
||||
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
|
||||
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
|
||||
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
|
||||
// const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
|
||||
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
|
||||
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
|
||||
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
|
||||
const smp = new SpeedMeasurePlugin();
|
||||
|
||||
const apiMocker = require('mocker-api');
|
||||
const paths = require('./paths');
|
||||
const modules = require('./modules');
|
||||
const getClientEnvironment = require('./env');
|
||||
|
||||
// Source maps are resource heavy and can cause out of memory issue for large source files.
|
||||
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
|
||||
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
|
||||
|
||||
const webpackDevClientEntry = require.resolve(
|
||||
'react-dev-utils/webpackHotDevClient'
|
||||
);
|
||||
const reactRefreshOverlayEntry = require.resolve(
|
||||
'react-dev-utils/refreshOverlayInterop'
|
||||
);
|
||||
|
||||
const imageInlineSizeLimit = parseInt(
|
||||
process.env.IMAGE_INLINE_SIZE_LIMIT || '10000'
|
||||
);
|
||||
|
||||
// Get the path to the uncompiled service worker (if it exists).
|
||||
const swSrc = paths.swSrc;
|
||||
|
||||
const cssRegex = /\.css$/;
|
||||
const cssModuleRegex = /\.module\.css$/;
|
||||
const sassRegex = /\.(scss|sass)$/;
|
||||
const sassModuleRegex = /\.module\.(scss|sass)$/;
|
||||
|
||||
webpackConfig = function (webpackEnv) {
|
||||
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');
|
||||
|
||||
// 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 shouldUseReactRefresh = true;//env.raw.FAST_REFRESH;
|
||||
|
||||
const getStyleLoaders = (cssOptions, preProcessor) => {
|
||||
const loaders = [
|
||||
isEnvDevelopment && require.resolve('style-loader'),
|
||||
isEnvProduction && {
|
||||
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: '../../' }
|
||||
: {},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: cssOptions,
|
||||
},
|
||||
{
|
||||
// Options for PostCSS as we reference these options twice
|
||||
// Adds vendor prefixing based on your specified browser support in
|
||||
// package.json
|
||||
loader: require.resolve('postcss-loader'),
|
||||
},
|
||||
].filter(Boolean);
|
||||
if (preProcessor) {
|
||||
loaders.push(
|
||||
{
|
||||
loader: require.resolve('resolve-url-loader'),
|
||||
options: {
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
root: paths.appSrc,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve(preProcessor),
|
||||
options: {
|
||||
sourceMap: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
return loaders;
|
||||
};
|
||||
return {
|
||||
mode: isEnvProduction
|
||||
? 'production'
|
||||
: isEnvDevelopment && 'development',
|
||||
context: path.resolve(__dirname, '../'),
|
||||
entry: {
|
||||
main:
|
||||
isEnvDevelopment && !shouldUseReactRefresh
|
||||
? [
|
||||
// Include an alternative client for WebpackDevServer. A client's job is to
|
||||
// connect to WebpackDevServer by a socket and get notified about changes.
|
||||
// When you save a file, the client will either apply hot updates (in case
|
||||
// of CSS changes), or refresh the page (in case of JS changes). When you
|
||||
// make a syntax error, this client will display a syntax error overlay.
|
||||
// Note: instead of the default WebpackDevServer client, we use a custom one
|
||||
// to bring better experience for Create React App users. You can replace
|
||||
// the line below with these two lines if you prefer the stock client:
|
||||
//
|
||||
// require.resolve('webpack-dev-server/client') + '?/',
|
||||
// require.resolve('webpack/hot/dev-server'),
|
||||
//
|
||||
// When using the experimental react-refresh integration,
|
||||
// the webpack plugin takes care of injecting the dev client for us.
|
||||
webpackDevClientEntry,
|
||||
// Finally, this is your app's code:
|
||||
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.
|
||||
]
|
||||
: paths.appIndexJs
|
||||
// 'editor.worker': 'monaco-editor/esm/vs/editor/editor.worker.js',
|
||||
// 'json.worker': 'monaco-editor/esm/vs/language/json/json.worker',
|
||||
// 'css.worker': 'monaco-editor/esm/vs/language/css/css.worker',
|
||||
// 'html.worker': 'monaco-editor/esm/vs/language/html/html.worker',
|
||||
// 'ts.worker': 'monaco-editor/esm/vs/language/typescript/ts.worker',
|
||||
},
|
||||
output: {
|
||||
// The build folder.
|
||||
path: isEnvProduction ? paths.appBuild : undefined,
|
||||
// Add /* filename */ comments to generated require()s in the output.
|
||||
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/[name].bundle.js',
|
||||
|
||||
// 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',
|
||||
// webpack uses `publicPath` to determine where the app is being served from.
|
||||
// It requires a trailing slash, or the file assets will get an incorrect path.
|
||||
// We inferred the "public path" (such as / or /my-project) from homepage.
|
||||
publicPath: paths.publicUrlOrPath,
|
||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
||||
devtoolModuleFilenameTemplate: isEnvProduction
|
||||
? (info) =>
|
||||
path
|
||||
.relative(paths.appSrc, info.absoluteResourcePath)
|
||||
.replace(/\\/g, '/')
|
||||
: isEnvDevelopment &&
|
||||
((info) =>
|
||||
path
|
||||
.resolve(info.absoluteResourcePath)
|
||||
.replace(/\\/g, '/')),
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
// This is only used in production mode
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
parse: {
|
||||
// We want terser to parse ecma 8 code. However, we don't want it
|
||||
// to apply any minification steps that turns valid ecma 5 code
|
||||
// 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,
|
||||
},
|
||||
compress: {
|
||||
ecma: 5,
|
||||
warnings: false,
|
||||
// Disabled because of an issue with Uglify breaking seemingly valid code:
|
||||
// https://github.com/facebook/create-react-app/issues/2376
|
||||
// Pending further investigation:
|
||||
// https://github.com/mishoo/UglifyJS2/issues/2011
|
||||
comparisons: false,
|
||||
// Disabled because of an issue with Terser breaking valid code:
|
||||
// https://github.com/facebook/create-react-app/issues/5250
|
||||
// Pending further investigation:
|
||||
// https://github.com/terser-js/terser/issues/120
|
||||
inline: 2,
|
||||
},
|
||||
mangle: {
|
||||
safari10: true,
|
||||
},
|
||||
// Added for profiling in devtools
|
||||
// keep_classnames: isEnvProductionProfile,
|
||||
// keep_fnames: isEnvProductionProfile,
|
||||
output: {
|
||||
ecma: 5,
|
||||
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,
|
||||
},
|
||||
},
|
||||
}),
|
||||
// This is only used in production mode
|
||||
new CssMinimizerPlugin({
|
||||
minimizerOptions: {
|
||||
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,
|
||||
},
|
||||
// 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}`,
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js', '.html', '.mjs'],
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, '..', 'src'),
|
||||
},
|
||||
fallback: {
|
||||
"util": false,
|
||||
"path": false,
|
||||
"stream": false,
|
||||
"url": require.resolve('url')
|
||||
},
|
||||
conditionNames: ['require', 'browser', 'web'],
|
||||
// This allows you to set a fallback for where webpack should look for modules.
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebook/create-react-app/issues/253
|
||||
modules: ['node_modules', paths.appNodeModules].concat(
|
||||
modules.additionalModulePaths || []
|
||||
),
|
||||
plugins: [
|
||||
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
|
||||
// guards against forgotten dependencies and such.
|
||||
PnpWebpackPlugin,
|
||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
||||
// This often causes confusion because we only process files within src/ with babel.
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
||||
// new ModuleScopePlugin(paths.appSrc, [
|
||||
// paths.appPackageJson,
|
||||
// reactRefreshOverlayEntry,
|
||||
// ]),
|
||||
],
|
||||
},
|
||||
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),
|
||||
],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
// Disable require.ensure as it's not a standard language feature.
|
||||
// { parser: { requireEnsure: false } },
|
||||
{
|
||||
oneOf: [
|
||||
// {
|
||||
// test: /froala-editor/,
|
||||
// parser: {
|
||||
// amd: false,
|
||||
// },
|
||||
// },
|
||||
{
|
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
|
||||
loader: require.resolve('url-loader'),
|
||||
options: {
|
||||
limit: imageInlineSizeLimit,
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// Process application JS with Babel.
|
||||
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
|
||||
{
|
||||
test: /\.(js|cjs|mjs|jsx|ts|tsx)$/,
|
||||
include: paths.appSrc,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
plugins: [
|
||||
// [
|
||||
// require.resolve('babel-plugin-named-asset-import'),
|
||||
// {
|
||||
// loaderMap: {
|
||||
// svg: {
|
||||
// ReactComponent:
|
||||
// '@svgr/webpack?-svgo,+titleProp,+ref![path]',
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
isEnvDevelopment &&
|
||||
shouldUseReactRefresh &&
|
||||
require.resolve('react-refresh/babel'),
|
||||
].filter(Boolean),
|
||||
cacheDirectory: true,
|
||||
compact: isEnvProduction,
|
||||
},
|
||||
},
|
||||
// "postcss" loader applies autoprefixer to our CSS.
|
||||
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
||||
// "style" loader turns CSS into JS modules that inject <style> tags.
|
||||
// In production, we use MiniCSSExtractPlugin to extract that CSS
|
||||
// to a file, but in development "style" loader enables hot editing
|
||||
// of CSS.
|
||||
// By default we support CSS Modules with the extension .module.css
|
||||
{
|
||||
test: cssRegex,
|
||||
exclude: cssModuleRegex,
|
||||
use: getStyleLoaders({
|
||||
importLoaders: 1,
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
}),
|
||||
// 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,
|
||||
},
|
||||
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
|
||||
// using the extension .module.css
|
||||
{
|
||||
test: cssModuleRegex,
|
||||
use: getStyleLoaders({
|
||||
importLoaders: 1,
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
modules: {
|
||||
getLocalIdent: getCSSModuleLocalIdent,
|
||||
},
|
||||
}),
|
||||
},
|
||||
// Opt-in support for SASS (using .scss or .sass extensions).
|
||||
// By default we support SASS Modules with the
|
||||
// extensions .module.scss or .module.sass
|
||||
{
|
||||
test: sassRegex,
|
||||
exclude: sassModuleRegex,
|
||||
use: getStyleLoaders(
|
||||
{
|
||||
importLoaders: 3,
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
},
|
||||
'sass-loader'
|
||||
),
|
||||
// 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,
|
||||
},
|
||||
// Adds support for CSS Modules, but using SASS
|
||||
// using the extension .module.scss or .module.sass
|
||||
{
|
||||
test: sassModuleRegex,
|
||||
use: getStyleLoaders(
|
||||
{
|
||||
importLoaders: 3,
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
modules: {
|
||||
getLocalIdent: getCSSModuleLocalIdent,
|
||||
},
|
||||
},
|
||||
'sass-loader'
|
||||
),
|
||||
},
|
||||
// "file" loader makes sure those assets get served by WebpackDevServer.
|
||||
// When you `import` an asset, you get its (virtual) filename.
|
||||
// In production, they would get copied to the `build` folder.
|
||||
// This loader doesn't use a "test" so it will catch all modules
|
||||
// that fall through the other loaders.
|
||||
{
|
||||
loader: require.resolve('file-loader'),
|
||||
// Exclude `js` files to keep "css" loader working as it injects
|
||||
// its runtime that would otherwise be processed through "file" loader.
|
||||
// Also exclude `html` and `json` extensions so they get processed
|
||||
// by webpacks internal loaders.
|
||||
exclude: [
|
||||
/\.(js|cjs|mjs|jsx|ts|tsx)$/,
|
||||
/\.html$/,
|
||||
/\.json$/,
|
||||
],
|
||||
options: {
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
// Generates an `index.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin(
|
||||
Object.assign(
|
||||
{},
|
||||
{
|
||||
inject: true,
|
||||
template: paths.appHtml,
|
||||
},
|
||||
isEnvProduction
|
||||
? {
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeRedundantAttributes: true,
|
||||
useShortDoctype: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
keepClosingSlash: true,
|
||||
minifyJS: true,
|
||||
minifyCSS: true,
|
||||
minifyURLs: true,
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
)
|
||||
),
|
||||
// Inlines the webpack runtime script. This script is too small to warrant
|
||||
// a network request.
|
||||
// https://github.com/facebook/create-react-app/issues/5358
|
||||
isEnvProduction &&
|
||||
shouldInlineRuntimeChunk &&
|
||||
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [
|
||||
/runtime-.+[.]js/,
|
||||
]),
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// It will be an empty string unless you specify "homepage"
|
||||
// in `package.json`, in which case it will be the pathname of that URL.
|
||||
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
|
||||
// This gives some necessary context to module not found errors, such as
|
||||
// the requesting resource.
|
||||
new ModuleNotFoundPlugin(paths.appPath),
|
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
|
||||
// It is absolutely essential that NODE_ENV is set to production
|
||||
// during a production build.
|
||||
// Otherwise React will be compiled in the very slow development mode.
|
||||
new webpack.DefinePlugin(env.stringified),
|
||||
// This is necessary to emit hot updates (CSS and Fast Refresh):
|
||||
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
|
||||
// Experimental hot reloading for React .
|
||||
// https://github.com/facebook/react/tree/master/packages/react-refresh
|
||||
isEnvDevelopment &&
|
||||
shouldUseReactRefresh &&
|
||||
new ReactRefreshWebpackPlugin({
|
||||
overlay: {
|
||||
entry: webpackDevClientEntry,
|
||||
// The expected exports are slightly different from what the overlay exports,
|
||||
// so an interop is included here to enable feedback on module-level errors.
|
||||
module: reactRefreshOverlayEntry,
|
||||
// Since we ship a custom dev client and overlay integration,
|
||||
// the bundled socket handling logic can be eliminated.
|
||||
sockIntegration: false,
|
||||
},
|
||||
}),
|
||||
// Watcher doesn't work well if you mistype casing in a path so we use
|
||||
// a plugin that prints an error when you attempt to do this.
|
||||
// See https://github.com/facebook/create-react-app/issues/240
|
||||
isEnvDevelopment && new CaseSensitivePathsPlugin(),
|
||||
isEnvProduction &&
|
||||
new MiniCssExtractPlugin({
|
||||
// Options similar to the same options in webpackOptions.output
|
||||
// both options are optional
|
||||
filename: 'static/css/[name].[contenthash:8].css',
|
||||
chunkFilename:
|
||||
'static/css/[name].[contenthash:8].chunk.css',
|
||||
}),
|
||||
// Generate an asset manifest file with the following content:
|
||||
// - "files" key: Mapping of all asset filenames to their corresponding
|
||||
// output file so that tools can pick it up without having to parse
|
||||
// `index.html`
|
||||
// - "entrypoints" key: Array of files which are included in `index.html`,
|
||||
// can be used to reconstruct the HTML if necessary
|
||||
new WebpackManifestPlugin({
|
||||
fileName: 'asset-manifest.json',
|
||||
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')
|
||||
);
|
||||
|
||||
return {
|
||||
files: manifestFiles,
|
||||
entrypoints: entrypointFiles,
|
||||
};
|
||||
},
|
||||
}),
|
||||
// Moment.js is an extremely popular library that bundles large locale files
|
||||
// by default due to how webpack interprets its code. This is a practical
|
||||
// solution that requires the user to opt into importing specific locales.
|
||||
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
||||
// You can remove this if you don't use Moment.js:
|
||||
new webpack.IgnorePlugin({
|
||||
resourceRegExp: /^\.\/locale$/,
|
||||
contextRegExp: /moment$/,
|
||||
}),
|
||||
isEnvProduction &&
|
||||
fs.existsSync(swSrc) &&
|
||||
new WorkboxWebpackPlugin.InjectManifest({
|
||||
swSrc,
|
||||
dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,
|
||||
exclude: [/\.map$/, /asset-manifest\.json$/, /LICENSE/],
|
||||
// Bump up the default maximum size (2mb) that's precached,
|
||||
// to make lazy-loading failure scenarios less likely.
|
||||
// See https://github.com/cra-template/pwa/issues/13#issuecomment-722667270
|
||||
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
|
||||
}),
|
||||
// useTypeScript &&
|
||||
// new ForkTsCheckerWebpackPlugin({
|
||||
// typescript: resolve.sync('typescript', {
|
||||
// basedir: paths.appNodeModules,
|
||||
// }),
|
||||
// async: isEnvDevelopment,
|
||||
// checkSyntacticErrors: true,
|
||||
// resolveModuleNameModule: process.versions.pnp
|
||||
// ? `${__dirname}/pnpTs.js`
|
||||
// : undefined,
|
||||
// resolveTypeReferenceDirectiveModule: process.versions.pnp
|
||||
// ? `${__dirname}/pnpTs.js`
|
||||
// : undefined,
|
||||
// tsconfig: paths.appTsConfig,
|
||||
// reportFiles: [
|
||||
// // This one is specifically to match during CI tests,
|
||||
// // as micromatch doesn't match
|
||||
// // '../cra-template-typescript/template/src/App.tsx'
|
||||
// // otherwise.
|
||||
// '../**/src/**/*.{ts,tsx}',
|
||||
// '**/src/**/*.{ts,tsx}',
|
||||
// '!**/src/**/__tests__/**',
|
||||
// '!**/src/**/?(*.)(spec|test).*',
|
||||
// '!**/src/setupProxy.*',
|
||||
// '!**/src/setupTests.*',
|
||||
// ],
|
||||
// silent: true,
|
||||
// // The formatter is invoked directly in WebpackDevServerUtils during development
|
||||
// formatter: isEnvProduction ? typescriptFormatter : undefined,
|
||||
// }),
|
||||
// !disableESLintPlugin &&
|
||||
// new ESLintPlugin({
|
||||
// // Plugin options
|
||||
// extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
|
||||
// formatter: require.resolve('react-dev-utils/eslintFormatter'),
|
||||
// eslintPath: require.resolve('eslint'),
|
||||
// failOnError: !(isEnvDevelopment && emitErrorsAsWarnings),
|
||||
// context: paths.appSrc,
|
||||
// cache: true,
|
||||
// cacheLocation: path.resolve(
|
||||
// paths.appNodeModules,
|
||||
// '.cache/.eslintcache'
|
||||
// ),
|
||||
// // ESLint class options
|
||||
// cwd: paths.appPath,
|
||||
// resolvePluginsRelativeTo: __dirname,
|
||||
// baseConfig: {
|
||||
// extends: [require.resolve('eslint-config-react-app/base')],
|
||||
// rules: {
|
||||
// ...(!hasJsxRuntime && {
|
||||
// 'react/react-in-jsx-scope': 'error',
|
||||
// }),
|
||||
// },
|
||||
// },
|
||||
// }),
|
||||
isEnvProduction && new BundleAnalyzerPlugin({ analyzerMode: 'static', generateStatsFile: true })
|
||||
].filter(Boolean),
|
||||
// Turn off performance processing because we utilize
|
||||
// our own hints via the FileSizeReporter
|
||||
performance: false,
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = webpackConfig;
|
||||
// 打印每个模块的执行速度
|
||||
// https://github.com/stephencookdev/speed-measure-webpack-plugin/issues/167
|
||||
// module.exports = smp.wrap(webpackConfig);
|
||||
79
config/webpackDevServer.config.js
Normal file
79
config/webpackDevServer.config.js
Normal file
@@ -0,0 +1,79 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
|
||||
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
|
||||
const ignoredFiles = require('react-dev-utils/ignoredFiles');
|
||||
const redirectServedPath = require('react-dev-utils/redirectServedPathMiddleware');
|
||||
const paths = require('./paths');
|
||||
const getHttpsConfig = require('./getHttpsConfig');
|
||||
|
||||
const host = process.env.HOST || '0.0.0.0';
|
||||
const sockHost = process.env.WDS_SOCKET_HOST;
|
||||
const sockPath = process.env.WDS_SOCKET_PATH; // default: '/sockjs-node'
|
||||
const sockPort = process.env.WDS_SOCKET_PORT;
|
||||
|
||||
module.exports = function (proxy, allowedHost) {
|
||||
const disableFirewall =
|
||||
!proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === "true";
|
||||
return {
|
||||
allowedHosts: disableFirewall ? "all" : [allowedHost],
|
||||
compress: true,
|
||||
static: {
|
||||
directory: paths.appPublic,
|
||||
publicPath: paths.publicUrlOrPath,
|
||||
watch: {
|
||||
ignored: ignoredFiles(paths.appSrc),
|
||||
}
|
||||
},
|
||||
client: {
|
||||
webSocketURL: {
|
||||
hostname: sockHost,
|
||||
pathname: sockPath,
|
||||
port: sockPort,
|
||||
},
|
||||
overlay: true,
|
||||
},
|
||||
devMiddleware: {
|
||||
// It is important to tell WebpackDevServer to use the same "publicPath" path as
|
||||
// we specified in the webpack config. When homepage is '.', default to serving
|
||||
// from the root.
|
||||
// remove last slash so user can land on `/test` instead of `/test/`
|
||||
publicPath: paths.publicUrlOrPath.slice(0, -1),
|
||||
},
|
||||
https: getHttpsConfig(),
|
||||
host,
|
||||
historyApiFallback: {
|
||||
// Paths with dots should still use the history fallback.
|
||||
// See https://github.com/facebook/create-react-app/issues/387.
|
||||
disableDotRule: true,
|
||||
index: paths.publicUrlOrPath,
|
||||
},
|
||||
// `proxy` is run between `before` and `after` `webpack-dev-server` hooks
|
||||
proxy,
|
||||
onBeforeSetupMiddleware(server) {
|
||||
const app = server.app;
|
||||
// Keep `evalSourceMapMiddleware`
|
||||
// middlewares before `redirectServedPath` otherwise will not have any effect
|
||||
// This lets us fetch source contents from webpack for the error overlay
|
||||
app.use(evalSourceMapMiddleware(server));
|
||||
|
||||
if (fs.existsSync(paths.proxySetup)) {
|
||||
// This registers user provided middleware for proxy reasons
|
||||
require(paths.proxySetup)(app);
|
||||
}
|
||||
},
|
||||
onAfterSetupMiddleware(server) {
|
||||
const app = server.app;
|
||||
// Redirect to `PUBLIC_URL` or `homepage` from `package.json` if url not match
|
||||
app.use(redirectServedPath(paths.publicUrlOrPath));
|
||||
|
||||
// This service worker file is effectively a 'no-op' that will reset any
|
||||
// previous service worker registered for the same host:port combination.
|
||||
// We do this in development to avoid hitting the production cache if
|
||||
// it used the same host and port.
|
||||
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
|
||||
app.use(noopServiceWorkerMiddleware(paths.publicUrlOrPath));
|
||||
},
|
||||
};
|
||||
};
|
||||
14
docker/Dockerfile
Normal file
14
docker/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
FROM nginx:stable-alpine
|
||||
ARG version=0.0.0
|
||||
ENV VERSION=${version}
|
||||
# ENV INTERCEPT_POST=true
|
||||
|
||||
COPY pub /etc/nginx/html
|
||||
COPY docker/nginx/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY docker/nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY docker/docker-entrypoint.sh /docker-entrypoint.d/40-dynamic-subpath.sh
|
||||
RUN chmod +x /docker-entrypoint.d/40-dynamic-subpath.sh \
|
||||
&& echo ${version} > /etc/nginx/html/VERSION
|
||||
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
25
docker/Dockerfile.build
Normal file
25
docker/Dockerfile.build
Normal file
@@ -0,0 +1,25 @@
|
||||
# build environment
|
||||
FROM node:16 as builder
|
||||
WORKDIR /app
|
||||
ENV PATH /app/node_modules/.bin:$PATH
|
||||
COPY package.json ./
|
||||
COPY package-lock.json ./
|
||||
RUN npm install
|
||||
COPY . ./
|
||||
RUN npm run build
|
||||
|
||||
# production environment
|
||||
FROM nginx:stable-alpine
|
||||
ARG version=0.0.0
|
||||
ENV VERSION=${version}
|
||||
# ENV INTERCEPT_POST=true
|
||||
|
||||
COPY --from=builder /app/dist /etc/nginx/html
|
||||
COPY docker/nginx/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY docker/nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY docker/docker-entrypoint.sh /docker-entrypoint.d/40-dynamic-subpath.sh
|
||||
RUN chmod +x /docker-entrypoint.d/40-dynamic-subpath.sh \
|
||||
&& echo ${version} > /etc/nginx/html/VERSION
|
||||
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
19
docker/Dockerfile.subpath
Normal file
19
docker/Dockerfile.subpath
Normal file
@@ -0,0 +1,19 @@
|
||||
# build environment
|
||||
FROM node:14 as builder
|
||||
WORKDIR /app
|
||||
ENV PATH /app/node_modules/.bin:$PATH
|
||||
ENV REACT_APP_BASE $BASE_PATH
|
||||
ENV PUBLIC_URL $BASE_PATH/admin
|
||||
COPY package.json ./
|
||||
COPY package-lock.json ./
|
||||
RUN npm install
|
||||
COPY . ./
|
||||
RUN REACT_APP_BASE=/changzhou PUBLIC_URL=/changzhou/admin GENERATE_SOURCEMAP=false node scripts/build.js
|
||||
|
||||
# production environment
|
||||
FROM nginx:stable-alpine
|
||||
COPY --from=builder /app/dist /etc/nginx/html
|
||||
COPY nginx/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
13
docker/docker-entrypoint.sh
Normal file
13
docker/docker-entrypoint.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if [[ "$APP_SUB_PATH" ]]; then
|
||||
sed -i s@/admin/@$APP_SUB_PATH/admin/@g /etc/nginx/html/index.html
|
||||
sed -i s@basePath:\"@basePath:\"$APP_SUB_PATH@g /etc/nginx/html/static/js/main*
|
||||
sed -i s@\"/api/report-api/@\"${APP_SUB_PATH}/api/report-api/@g /etc/nginx/html/static/js/*
|
||||
sed -i s@'/download@'$APP_SUB_PATH/download@g /etc/nginx/html/static/js/*
|
||||
sed -i s@/admin/preview@$APP_SUB_PATH/admin/preview@g /etc/nginx/html/static/js/*
|
||||
sed -i s@baseURL=\"/api\"@baseURL=\"$APP_SUB_PATH/api\"@g /etc/nginx/html/static/js/main*
|
||||
sed -i s@"/admin/"@"$APP_SUB_PATH/admin/"@g /etc/nginx/html/static/css/*
|
||||
fi
|
||||
19
docker/nginx/default.conf
Normal file
19
docker/nginx/default.conf
Normal file
@@ -0,0 +1,19 @@
|
||||
server {
|
||||
|
||||
listen 80;
|
||||
|
||||
location ^~ /admin {
|
||||
alias /etc/nginx/html;
|
||||
try_files $uri $uri/index.html @admin;
|
||||
}
|
||||
|
||||
location @admin {
|
||||
rewrite ^.*$ /admin/index.html last;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
98
docker/nginx/nginx.conf
Normal file
98
docker/nginx/nginx.conf
Normal file
@@ -0,0 +1,98 @@
|
||||
# user www-data;
|
||||
worker_processes 2;
|
||||
pid /run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
# multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
|
||||
##
|
||||
# Basic Settings
|
||||
##
|
||||
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
client_max_body_size 10m;
|
||||
# server_tokens off;
|
||||
|
||||
# server_names_hash_bucket_size 64;
|
||||
# server_name_in_redirect off;
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
##
|
||||
# Logging Settings
|
||||
##
|
||||
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
||||
##
|
||||
# Gzip Settings
|
||||
##
|
||||
|
||||
gzip on;
|
||||
gzip_disable "msie6";
|
||||
|
||||
gzip_vary on;
|
||||
gzip_min_length 1000;
|
||||
gzip_proxied expired no-cache no-store private auth;
|
||||
gzip_comp_level 6;
|
||||
gzip_buffers 16 8k;
|
||||
gzip_http_version 1.1;
|
||||
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript application/x-ipynb+json;
|
||||
|
||||
##
|
||||
# nginx-naxsi config
|
||||
##
|
||||
# Uncomment it if you installed nginx-naxsi
|
||||
##
|
||||
|
||||
#include /etc/nginx/naxsi_core.rules;
|
||||
|
||||
##
|
||||
# nginx-passenger config
|
||||
##
|
||||
# Uncomment it if you installed nginx-passenger
|
||||
##
|
||||
|
||||
#passenger_root /usr;
|
||||
#passenger_ruby /usr/bin/ruby;
|
||||
|
||||
##
|
||||
# Virtual Host Configs
|
||||
##
|
||||
client_header_buffer_size 16k;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
include /etc/nginx/sites-enabled/*;
|
||||
}
|
||||
|
||||
|
||||
#mail {
|
||||
# # See sample authentication script at:
|
||||
# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
|
||||
#
|
||||
# # auth_http localhost/auth.php;
|
||||
# # pop3_capabilities "TOP" "USER";
|
||||
# # imap_capabilities "IMAP4rev1" "UIDPLUS";
|
||||
#
|
||||
# server {
|
||||
# listen localhost:110;
|
||||
# protocol pop3;
|
||||
# proxy on;
|
||||
# }
|
||||
#
|
||||
# server {
|
||||
# listen localhost:143;
|
||||
# protocol imap;
|
||||
# proxy on;
|
||||
# }
|
||||
#}
|
||||
6
mock/index.js
Normal file
6
mock/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
'/api/login': {
|
||||
status: 0,
|
||||
msg: '登录成功',
|
||||
},
|
||||
};
|
||||
31116
package-lock.json
generated
Normal file
31116
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
130
package.json
Normal file
130
package.json
Normal file
@@ -0,0 +1,130 @@
|
||||
{
|
||||
"name": "olms-admin",
|
||||
"version": "1.2.0",
|
||||
"homepage": "admin",
|
||||
"scripts": {
|
||||
"start": "node scripts/start.js",
|
||||
"build": "node scripts/build.js",
|
||||
"build-sub": "REACT_APP_BASE=/tianmuhu PUBLIC_URL=/tianmuhu/admin GENERATE_SOURCEMAP=false node scripts/build.js",
|
||||
"test": "node scripts/test.js",
|
||||
"analyze": "webpack-bundle-analyzer --port 8080 dist/stats.json",
|
||||
"lint": "prettier --config ./.prettierrc.yaml --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@fluentui/react": "^7.161.0",
|
||||
"@rjsf/core": "^2.4.2",
|
||||
"@rjsf/fluent-ui": "^2.4.2",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"amis": "^6.8.0",
|
||||
"amis-core": "^6.8.0",
|
||||
"amis-ui": "^6.8.0",
|
||||
"antd": "^4.18.6",
|
||||
"axios": "^0.24.0",
|
||||
"bootstrap": "^3.4.1",
|
||||
"classnames": "^2.2.6",
|
||||
"copy-to-clipboard": "3.3.2",
|
||||
"crypto-js": "^4.0.0",
|
||||
"date-fns": "^2.16.1",
|
||||
"font-awesome": "^4.7.0",
|
||||
"history": "^4.7.2",
|
||||
"js-cookie": "^2.2.1",
|
||||
"lodash": "^4.17.15",
|
||||
"mammoth": "^1.4.21",
|
||||
"mini-css-extract-plugin": "^2.6.0",
|
||||
"mobx": "4.15.4",
|
||||
"mobx-react": "6.1.8",
|
||||
"mobx-state-tree": "^3.17.3",
|
||||
"promise": "^8.1.0",
|
||||
"promise-polyfill": "8.2.0",
|
||||
"qs": "6.5.1",
|
||||
"react": "^16.10.2",
|
||||
"react-big-calendar": "^0.40.0",
|
||||
"react-dom": "^16.10.2",
|
||||
"react-perfect-scrollbar": "^1.5.8",
|
||||
"react-router": "5.1.2",
|
||||
"react-router-dom": "5.1.2",
|
||||
"resolve": "^1.22.0",
|
||||
"resolve-url-loader": "^5.0.0",
|
||||
"vditor": "^3.10.7",
|
||||
"viewerjs": "^1.10.1",
|
||||
"xlsx": "^0.16.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.17.6",
|
||||
"@babel/core": "^7.17.9",
|
||||
"@babel/plugin-proposal-decorators": "^7.17.9",
|
||||
"@babel/preset-env": "^7.16.11",
|
||||
"@babel/preset-react": "^7.16.7",
|
||||
"@babel/preset-typescript": "^7.16.7",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.5",
|
||||
"@types/js-cookie": "^2.2.6",
|
||||
"@types/lodash": "^4.14.123",
|
||||
"@types/node": "^11.13.8",
|
||||
"@types/qs": "^6.5.3",
|
||||
"@types/react": "^16.8.4",
|
||||
"@types/react-big-calendar": "^0.24.8",
|
||||
"@types/react-dom": "^16.8.4",
|
||||
"@types/react-router-dom": "^5.1.6",
|
||||
"amis-editor": "^6.8.0",
|
||||
"amis-editor-core": "^6.8.0",
|
||||
"axios-mock-adapter": "1.16.0",
|
||||
"babel-loader": "^8.2.4",
|
||||
"bfj": "^7.0.2",
|
||||
"case-sensitive-paths-webpack-plugin": "^2.4.0",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"core-js": "^3.22.0",
|
||||
"css-loader": "^3.4.2",
|
||||
"css-minimizer-webpack-plugin": "^3.4.1",
|
||||
"dotenv": "^16.0.0",
|
||||
"dotenv-expand": "^8.0.3",
|
||||
"file-loader": "^5.0.2",
|
||||
"fs-walk": "0.0.2",
|
||||
"html-minifier": "^4.0.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"http-proxy-middleware": "^2.0.4",
|
||||
"mocker-api": "^1.11.2",
|
||||
"pnp-webpack-plugin": "^1.7.0",
|
||||
"postcss": "^8.4.12",
|
||||
"postcss-custom-properties": "^12.1.7",
|
||||
"postcss-flexbugs-fixes": "4.2.1",
|
||||
"postcss-loader": "^4.3.0",
|
||||
"postcss-normalize": "8.0.1",
|
||||
"postcss-preset-env": "6.7.0",
|
||||
"postcss-safe-parser": "5.0.2",
|
||||
"prettier": "^2.6.2",
|
||||
"react-dev-utils": "^12.0.1",
|
||||
"react-error-overlay": "6.0.9",
|
||||
"sass": "^1.45.2",
|
||||
"sass-loader": "^8.0.2",
|
||||
"speed-measure-webpack-plugin": "^1.5.0",
|
||||
"style-loader": "^1.1.3",
|
||||
"terser-webpack-plugin": "^5.3.1",
|
||||
"ts-pnp": "^1.2.0",
|
||||
"tslib": "^2.3.1",
|
||||
"type-fest": "^2.12.1",
|
||||
"typescript": "^4.4.4",
|
||||
"url": "^0.11.0",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.28.0",
|
||||
"webpack-bundle-analyzer": "^4.5.0",
|
||||
"webpack-cli": "^4.6.0",
|
||||
"webpack-dev-server": "^4.8.1",
|
||||
"webpack-manifest-plugin": "^5.0.0",
|
||||
"workbox-webpack-plugin": "^6.5.3"
|
||||
},
|
||||
"overrides": {
|
||||
"react-error-overlay": "6.0.9"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
]
|
||||
}
|
||||
}
|
||||
17
postcss.config.js
Normal file
17
postcss.config.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const postcssNormalize = require('postcss-normalize');
|
||||
|
||||
module.exports = {
|
||||
plugins: () => [
|
||||
require('postcss-flexbugs-fixes'),
|
||||
require('postcss-preset-env')({
|
||||
autoprefixer: {
|
||||
flexbox: 'no-2009',
|
||||
},
|
||||
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(),
|
||||
],
|
||||
};
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
38
public/index.html
Normal file
38
public/index.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="智医教学实践平台" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>智医教学实践平台</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root" class="app-wrapper"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
--></body>
|
||||
</html>
|
||||
25
public/manifest.json
Normal file
25
public/manifest.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"short_name": "OLMS",
|
||||
"name": "智医教学实践平台",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
3
public/robots.txt
Normal file
3
public/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
215
scripts/build.js
Normal file
215
scripts/build.js
Normal file
@@ -0,0 +1,215 @@
|
||||
'use strict';
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'production';
|
||||
process.env.NODE_ENV = 'production';
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', (err) => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
|
||||
const path = require('path');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const fs = require('fs-extra');
|
||||
const bfj = require('bfj');
|
||||
const webpack = require('webpack');
|
||||
const configFactory = require('../config/webpack.config');
|
||||
const paths = require('../config/paths');
|
||||
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
||||
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
|
||||
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
|
||||
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
|
||||
const printBuildError = require('react-dev-utils/printBuildError');
|
||||
|
||||
const measureFileSizesBeforeBuild =
|
||||
FileSizeReporter.measureFileSizesBeforeBuild;
|
||||
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
|
||||
const useYarn = fs.existsSync(paths.yarnLockFile);
|
||||
|
||||
// These sizes are pretty large. We'll warn for bundles exceeding them.
|
||||
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
|
||||
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
|
||||
|
||||
const isInteractive = process.stdout.isTTY;
|
||||
|
||||
// Warn and crash if required files are missing
|
||||
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const argv = process.argv.slice(2);
|
||||
const writeStatsJson = argv.indexOf('--stats') !== -1;
|
||||
|
||||
// Generate configuration
|
||||
const config = configFactory('production');
|
||||
|
||||
// We require that you explicitly set browsers and do not fall back to
|
||||
// browserslist defaults.
|
||||
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
|
||||
checkBrowsers(paths.appPath, isInteractive)
|
||||
.then(() => {
|
||||
// First, read the current file sizes in build directory.
|
||||
// This lets us display how much they changed later.
|
||||
return measureFileSizesBeforeBuild(paths.appBuild);
|
||||
})
|
||||
.then((previousFileSizes) => {
|
||||
// Remove all content but keep the directory so that
|
||||
// if you're in it, you don't end up in Trash
|
||||
fs.emptyDirSync(paths.appBuild);
|
||||
// Merge with the public folder
|
||||
copyPublicFolder();
|
||||
// Start the webpack build
|
||||
return build(previousFileSizes);
|
||||
})
|
||||
.then(
|
||||
({ stats, previousFileSizes, warnings }) => {
|
||||
if (warnings.length) {
|
||||
console.log(chalk.yellow('Compiled with warnings.\n'));
|
||||
console.log(warnings.join('\n\n'));
|
||||
console.log(
|
||||
'\nSearch for the ' +
|
||||
chalk.underline(chalk.yellow('keywords')) +
|
||||
' to learn more about each warning.'
|
||||
);
|
||||
console.log(
|
||||
'To ignore, add ' +
|
||||
chalk.cyan('// eslint-disable-next-line') +
|
||||
' to the line before.\n'
|
||||
);
|
||||
} else {
|
||||
console.log(chalk.green('Compiled successfully.\n'));
|
||||
}
|
||||
|
||||
console.log('File sizes after gzip:\n');
|
||||
printFileSizesAfterBuild(
|
||||
stats,
|
||||
previousFileSizes,
|
||||
paths.appBuild,
|
||||
WARN_AFTER_BUNDLE_GZIP_SIZE,
|
||||
WARN_AFTER_CHUNK_GZIP_SIZE
|
||||
);
|
||||
console.log();
|
||||
|
||||
const appPackage = require(paths.appPackageJson);
|
||||
const publicUrl = paths.publicUrlOrPath;
|
||||
const publicPath = config.output.publicPath;
|
||||
const buildFolder = path.relative(process.cwd(), paths.appBuild);
|
||||
printHostingInstructions(
|
||||
appPackage,
|
||||
publicUrl,
|
||||
publicPath,
|
||||
buildFolder,
|
||||
useYarn
|
||||
);
|
||||
},
|
||||
(err) => {
|
||||
const tscCompileOnError =
|
||||
process.env.TSC_COMPILE_ON_ERROR === 'true';
|
||||
if (tscCompileOnError) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'Compiled with the following type errors (you may want to check these before deploying your app):\n'
|
||||
)
|
||||
);
|
||||
printBuildError(err);
|
||||
} else {
|
||||
console.log(chalk.red('Failed to compile.\n'));
|
||||
printBuildError(err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
if (err && err.message) {
|
||||
console.log(err.message);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Create the production build and print the deployment instructions.
|
||||
function build(previousFileSizes) {
|
||||
console.log('Creating an optimized production build...');
|
||||
|
||||
const compiler = webpack(config);
|
||||
return new Promise((resolve, reject) => {
|
||||
compiler.run((err, stats) => {
|
||||
let messages;
|
||||
if (err) {
|
||||
if (!err.message) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
let errMessage = err.message;
|
||||
|
||||
// Add additional information for postcss errors
|
||||
if (Object.prototype.hasOwnProperty.call(err, 'postcssNode')) {
|
||||
errMessage +=
|
||||
'\nCompileError: Begins at CSS selector ' +
|
||||
err['postcssNode'].selector;
|
||||
}
|
||||
|
||||
messages = formatWebpackMessages({
|
||||
errors: [errMessage],
|
||||
warnings: [],
|
||||
});
|
||||
} else {
|
||||
messages = formatWebpackMessages(
|
||||
stats.toJson({ all: false, warnings: true, errors: true })
|
||||
);
|
||||
}
|
||||
if (messages.errors.length) {
|
||||
// Only keep the first error. Others are often indicative
|
||||
// of the same problem, but confuse the reader with noise.
|
||||
if (messages.errors.length > 1) {
|
||||
messages.errors.length = 1;
|
||||
}
|
||||
return reject(new Error(messages.errors.join('\n\n')));
|
||||
}
|
||||
if (
|
||||
process.env.CI &&
|
||||
(typeof process.env.CI !== 'string' ||
|
||||
process.env.CI.toLowerCase() !== 'false') &&
|
||||
messages.warnings.length
|
||||
) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'\nTreating warnings as errors because process.env.CI = true.\n' +
|
||||
'Most CI servers set it automatically.\n'
|
||||
)
|
||||
);
|
||||
return reject(new Error(messages.warnings.join('\n\n')));
|
||||
}
|
||||
|
||||
const resolveArgs = {
|
||||
stats,
|
||||
previousFileSizes,
|
||||
warnings: messages.warnings,
|
||||
};
|
||||
|
||||
if (writeStatsJson) {
|
||||
return bfj
|
||||
.write(
|
||||
paths.appBuild + '/bundle-stats.json',
|
||||
stats.toJson()
|
||||
)
|
||||
.then(() => resolve(resolveArgs))
|
||||
.catch((error) => reject(new Error(error)));
|
||||
}
|
||||
|
||||
return resolve(resolveArgs);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function copyPublicFolder() {
|
||||
fs.copySync(paths.appPublic, paths.appBuild, {
|
||||
dereference: true,
|
||||
filter: (file) => file !== paths.appHtml,
|
||||
});
|
||||
}
|
||||
171
scripts/start.js
Normal file
171
scripts/start.js
Normal file
@@ -0,0 +1,171 @@
|
||||
'use strict';
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'development';
|
||||
process.env.NODE_ENV = 'development';
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', (err) => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
|
||||
const fs = require('fs');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const webpack = require('webpack');
|
||||
const WebpackDevServer = require('webpack-dev-server');
|
||||
const clearConsole = require('react-dev-utils/clearConsole');
|
||||
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
||||
const {
|
||||
choosePort,
|
||||
createCompiler,
|
||||
prepareProxy,
|
||||
prepareUrls,
|
||||
} = require('react-dev-utils/WebpackDevServerUtils');
|
||||
const openBrowser = require('react-dev-utils/openBrowser');
|
||||
const semver = require('semver');
|
||||
const paths = require('../config/paths');
|
||||
const configFactory = require('../config/webpack.config');
|
||||
const createDevServerConfig = require('../config/webpackDevServer.config');
|
||||
const getClientEnvironment = require('../config/env');
|
||||
const react = require(require.resolve('react', { paths: [paths.appPath] }));
|
||||
|
||||
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
|
||||
const useYarn = fs.existsSync(paths.yarnLockFile);
|
||||
const isInteractive = process.stdout.isTTY;
|
||||
|
||||
// Warn and crash if required files are missing
|
||||
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Tools like Cloud9 rely on this.
|
||||
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
|
||||
const HOST = process.env.HOST || '0.0.0.0';
|
||||
|
||||
if (process.env.HOST) {
|
||||
console.log(
|
||||
chalk.cyan(
|
||||
`Attempting to bind to HOST environment variable: ${chalk.yellow(
|
||||
chalk.bold(process.env.HOST)
|
||||
)}`
|
||||
)
|
||||
);
|
||||
console.log(
|
||||
`If this was unintentional, check that you haven't mistakenly set it in your shell.`
|
||||
);
|
||||
console.log(
|
||||
`Learn more here: ${chalk.yellow('https://cra.link/advanced-config')}`
|
||||
);
|
||||
console.log();
|
||||
}
|
||||
|
||||
// We require that you explicitly set browsers and do not fall back to
|
||||
// browserslist defaults.
|
||||
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
|
||||
checkBrowsers(paths.appPath, isInteractive)
|
||||
.then(() => {
|
||||
// We attempt to use the default port but if it is busy, we offer the user to
|
||||
// run on a different port. `choosePort()` Promise resolves to the next free port.
|
||||
return choosePort(HOST, DEFAULT_PORT);
|
||||
})
|
||||
.then((port) => {
|
||||
if (port == null) {
|
||||
// We have not found a port.
|
||||
return;
|
||||
}
|
||||
|
||||
const config = configFactory("development");
|
||||
const protocol = process.env.HTTPS === "true" ? "https" : "http";
|
||||
const appName = require(paths.appPackageJson).name;
|
||||
|
||||
const useTypeScript = fs.existsSync(paths.appTsConfig);
|
||||
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === "true";
|
||||
const urls = prepareUrls(
|
||||
protocol,
|
||||
HOST,
|
||||
port,
|
||||
paths.publicUrlOrPath.slice(0, -1)
|
||||
);
|
||||
const devSocket = {
|
||||
warnings: (warnings) =>
|
||||
devServer.sockWrite(devServer.sockets, "warnings", warnings),
|
||||
errors: (errors) =>
|
||||
devServer.sockWrite(devServer.sockets, "errors", errors),
|
||||
};
|
||||
// Create a webpack compiler that is configured with custom messages.
|
||||
const compiler = createCompiler({
|
||||
appName,
|
||||
config,
|
||||
devSocket,
|
||||
urls,
|
||||
useYarn,
|
||||
useTypeScript,
|
||||
tscCompileOnError,
|
||||
webpack,
|
||||
});
|
||||
// Load proxy config
|
||||
const proxySetting = require(paths.appPackageJson).proxy;
|
||||
const proxyConfig = prepareProxy(
|
||||
proxySetting,
|
||||
paths.appPublic,
|
||||
paths.publicUrlOrPath
|
||||
);
|
||||
// Serve webpack assets generated by the compiler over a web server.
|
||||
const serverConfig = createDevServerConfig(
|
||||
proxyConfig,
|
||||
urls.lanUrlForConfig
|
||||
);
|
||||
serverConfig.host = HOST;
|
||||
serverConfig.port = port;
|
||||
serverConfig.setupExitSignals = true;
|
||||
|
||||
const devServer = new WebpackDevServer(serverConfig, compiler);
|
||||
|
||||
// Launch WebpackDevServer.
|
||||
devServer.startCallback((err) => {
|
||||
if (err) {
|
||||
return console.log(err);
|
||||
}
|
||||
if (isInteractive) {
|
||||
clearConsole();
|
||||
}
|
||||
|
||||
if (env.raw.FAST_REFRESH && semver.lt(react.version, "16.10.0")) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`Fast Refresh requires React 16.10 or higher. You are using React ${react.version}.`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
console.log(chalk.cyan("Starting the development server...\n"));
|
||||
openBrowser(urls.localUrlForBrowser);
|
||||
});
|
||||
|
||||
|
||||
['SIGINT', 'SIGTERM'].forEach(function (sig) {
|
||||
process.on(sig, function () {
|
||||
devServer.close();
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
|
||||
if (process.env.CI !== 'true') {
|
||||
// Gracefully exit when stdin ends
|
||||
process.stdin.on('end', function () {
|
||||
devServer.close();
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err && err.message) {
|
||||
console.log(err.message);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
51
scripts/test.js
Normal file
51
scripts/test.js
Normal file
@@ -0,0 +1,51 @@
|
||||
'use strict';
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'test';
|
||||
process.env.NODE_ENV = 'test';
|
||||
process.env.PUBLIC_URL = '';
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', (err) => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
|
||||
const jest = require('jest');
|
||||
const execSync = require('child_process').execSync;
|
||||
let argv = process.argv.slice(2);
|
||||
|
||||
function isInGitRepository() {
|
||||
try {
|
||||
execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isInMercurialRepository() {
|
||||
try {
|
||||
execSync('hg --cwd . root', { stdio: 'ignore' });
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Watch unless on CI or explicitly running all tests
|
||||
if (
|
||||
!process.env.CI &&
|
||||
argv.indexOf('--watchAll') === -1 &&
|
||||
argv.indexOf('--watchAll=false') === -1
|
||||
) {
|
||||
// https://github.com/facebook/create-react-app/issues/5210
|
||||
const hasSourceControl = isInGitRepository() || isInMercurialRepository();
|
||||
argv.push(hasSourceControl ? '--watch' : '--watchAll');
|
||||
}
|
||||
|
||||
jest.run(argv);
|
||||
290
src/App.css
Normal file
290
src/App.css
Normal file
@@ -0,0 +1,290 @@
|
||||
.cxd-Layout-content {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.user-page .cxd-Page-body {
|
||||
padding: 0;
|
||||
}
|
||||
.cxd-DropDown-menu {
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.cxd-Tree {
|
||||
height: 84vh;
|
||||
max-height: inherit!important;
|
||||
}
|
||||
|
||||
.cxd-ListControl-item:hover:active:hover,
|
||||
.cxd-ListControl-item.is-active:hover {
|
||||
background-color: var(--ListControl-item-onActive-bg);
|
||||
}
|
||||
|
||||
.plan-list .cxd-ListControl-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.rbc-allday-cell {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rbc-month-row {
|
||||
min-height: 128px;
|
||||
border-color: rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.copy-list {
|
||||
display: flex;
|
||||
flex-flow: wrap;
|
||||
}
|
||||
|
||||
.copy-list .cxd-GroupedSelection-item {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
/* https://github.com/rjsf-team/react-jsonschema-form/issues/2188 */
|
||||
/* .rjsf .field-undefined> :nth-child(1) {
|
||||
display: none;
|
||||
} */
|
||||
|
||||
.rjsf [for='root']:first-child + #root__description {
|
||||
display: none;
|
||||
}
|
||||
.rjsf .css-42:last-child {
|
||||
display: none;
|
||||
}
|
||||
.rjsf .field-array span:last-child {
|
||||
float: none !important;
|
||||
}
|
||||
.rjsf fieldset {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
.rjsf .btn-add::after {
|
||||
content: '添加';
|
||||
}
|
||||
.rjsf.array-item-move-up::after {
|
||||
content: '上移';
|
||||
}
|
||||
.rjsf.array-item-move-down::after {
|
||||
content: '下移';
|
||||
}
|
||||
.rjsf.array-item-remove::after {
|
||||
content: '删除';
|
||||
}
|
||||
.rjsf .form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.rjsf .form-group label {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.rjsf .form-control {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
background: var(--Form-input-bg);
|
||||
border: var(--Form-input-borderWidth) solid var(--Form-input-borderColor);
|
||||
border-radius: var(--Form-input-borderRadius);
|
||||
line-height: var(--Form-input-lineHeight);
|
||||
padding: var(--Form-input-paddingY) var(--Form-input-paddingX);
|
||||
font-size: var(--Form-input-fontSize);
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.preview table {
|
||||
width: 100%;
|
||||
border: 1px solid #000000;
|
||||
border-collapse: separate;
|
||||
border-left: 0;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
background-color: transparent;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.preview table thead:first-child tr:first-child > th:first-child,
|
||||
.preview table tbody:first-child tr:first-child > td:first-child {
|
||||
-webkit-border-top-left-radius: 4px;
|
||||
border-top-left-radius: 4px;
|
||||
-moz-border-radius-topleft: 4px;
|
||||
}
|
||||
.preview table thead th {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
.preview table th,
|
||||
.preview table td {
|
||||
padding: 4px 8px !important;
|
||||
line-height: 14px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
border-left: 1px solid #000000;
|
||||
border-top: 1px solid #000000;
|
||||
}
|
||||
.preview table th {
|
||||
padding: 4px 8px;
|
||||
line-height: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
/*custom*/
|
||||
.cxd-Select-popover {
|
||||
z-index: 1500;
|
||||
}
|
||||
.tox {
|
||||
z-index: 1400 !important;
|
||||
}
|
||||
.amis-scope .cxd-PopOver {
|
||||
z-index: 1400 !important;
|
||||
}
|
||||
|
||||
#print-preview .cxd-Table-fixedTop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.common-padding {
|
||||
padding: 0px 0 4px 10px !important;
|
||||
}
|
||||
|
||||
/* 部门管理 */
|
||||
.partment-grid .cxd-Grid-col--md3 {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.partment-grid .cxd-Grid-col--md9 {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.partment-height {
|
||||
height: 84vh;
|
||||
max-height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 报告模板 */
|
||||
.rpt-tp-height {
|
||||
height: 81vh;
|
||||
}
|
||||
|
||||
/* 教学计划 */
|
||||
.experiment {
|
||||
display: inline-block;
|
||||
margin-left: 0;
|
||||
width: 230px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.experiment:hover {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: 20px;
|
||||
min-width: 230px;
|
||||
vertical-align: middle;
|
||||
white-space: none;
|
||||
overflow: unset;
|
||||
text-overflow: unset;
|
||||
}
|
||||
|
||||
.class {
|
||||
display: inline-block;
|
||||
width: 140px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.class:hover {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: 20px;
|
||||
min-width: 140px;
|
||||
vertical-align: middle;
|
||||
white-space: none;
|
||||
overflow: unset;
|
||||
text-overflow: unset;
|
||||
}
|
||||
|
||||
.course_select .cxd-Select-menu {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.trash {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.wrapper .is-disabled {
|
||||
background: grey;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.tab .cxd-Tabs-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: auto;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.text-purple {
|
||||
color: rgb(175, 167, 175);
|
||||
}
|
||||
|
||||
.question img {
|
||||
max-width: 100%;
|
||||
}
|
||||
.question-option p {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.question .cxd-Button {
|
||||
display: inline-block;
|
||||
width: 480px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.menu-permission-tree {
|
||||
max-height: none;
|
||||
}
|
||||
.evaluation-height {
|
||||
height: 75vh;
|
||||
max-height: 75vh;
|
||||
}
|
||||
|
||||
.cxd-Modal {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.gallery .cxd-Modal-content {
|
||||
border: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.gallery .cxd-Modal-content.in,
|
||||
.gallery .cxd-Modal-content.out {
|
||||
animation-fill-mode: none;
|
||||
}
|
||||
.device-frame {
|
||||
overflow-y: auto;
|
||||
}
|
||||
.root-41 {
|
||||
display: none;
|
||||
}
|
||||
171
src/App.tsx
Normal file
171
src/App.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
import { alert, confirm, toast } from 'amis-ui';
|
||||
import axios from 'axios';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import _, { isString } from 'lodash';
|
||||
import { Provider } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
import RootRoute from './routes';
|
||||
import { MainStore } from './stores';
|
||||
import './utils/icons';
|
||||
import { request } from './utils/requestInterceptor';
|
||||
import { resRequest } from './utils/resRequestInterceptor';
|
||||
|
||||
import './renderer/AttScoreRule';
|
||||
import './renderer/Calendar';
|
||||
import './renderer/CourseList';
|
||||
import './renderer/DataPreview';
|
||||
import './renderer/DataTrial';
|
||||
import './renderer/ProjectScoreItem';
|
||||
import './renderer/Scheduler';
|
||||
import './renderer/TimeslotPicker';
|
||||
import './renderer/TimeslotPicker/Copy';
|
||||
import './renderer/Workload';
|
||||
// import './renderer/DataPreview1';
|
||||
import './renderer/BatchGroup';
|
||||
import './renderer/CustomImageUpload';
|
||||
import './renderer/CustomRichtext';
|
||||
import './renderer/DataSet';
|
||||
import './renderer/DataSetPicker';
|
||||
import './renderer/DownloadReport';
|
||||
import './renderer/DownloadReportSchedule';
|
||||
import './renderer/ExamImport';
|
||||
import './renderer/ExamPaperRule';
|
||||
import './renderer/ExcelImport';
|
||||
import './renderer/ExcelImportDeduction';
|
||||
import './renderer/ExcelImportGroup';
|
||||
import './renderer/ExcelImportSchedule';
|
||||
import './renderer/ExcelImportStudent';
|
||||
import './renderer/ExcelImportStudentOrg';
|
||||
import './renderer/Group';
|
||||
import './renderer/ImageGallery';
|
||||
import './renderer/Option';
|
||||
import './renderer/TextbookSubmit';
|
||||
import './renderer/Vditor';
|
||||
|
||||
// css
|
||||
import 'font-awesome/css/font-awesome.css';
|
||||
// import 'amis/lib/themes/antd.css';
|
||||
import 'amis-ui/lib/themes/cxd.css';
|
||||
// import './scss/style.scss'
|
||||
// import 'amis/sdk/iconfont.css'
|
||||
import 'amis/lib/helper.css';
|
||||
import './App.css';
|
||||
|
||||
export default function (): JSX.Element {
|
||||
console.log(React);
|
||||
const store = ((window as any).store = MainStore.create(
|
||||
{},
|
||||
{
|
||||
fetcher: ({ url, method, data, config, headers }: any) => {
|
||||
config = config || {};
|
||||
config.headers = config.headers || {};
|
||||
config.withCredentials = true;
|
||||
config.baseURL = '/api';
|
||||
// config.baseURL = '/changzhou/api';
|
||||
// config.baseURL = prefix ? `${prefix}/api` : '/api';
|
||||
|
||||
if (config.cancelExecutor) {
|
||||
config.cancelToken = new axios.CancelToken(config.cancelExecutor);
|
||||
}
|
||||
|
||||
config.headers = headers || {};
|
||||
config.method = method;
|
||||
|
||||
if (method === 'get' && data) {
|
||||
config.params = data;
|
||||
} else if (data && data instanceof FormData) {
|
||||
// config.headers = config.headers || {};
|
||||
// config.headers['Content-Type'] = 'multipart/form-data';
|
||||
} else if (
|
||||
data &&
|
||||
typeof data !== 'string' &&
|
||||
!(data instanceof Blob) &&
|
||||
!(data instanceof ArrayBuffer)
|
||||
) {
|
||||
data = JSON.stringify(data);
|
||||
// config.headers = config.headers || {};
|
||||
config.headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
let match = url.match(/\[(.*?)\]/);
|
||||
while (match) {
|
||||
url = url.replace(match[0], `.${match[1]}`);
|
||||
match = url.match(/\[(.*?)\]/);
|
||||
}
|
||||
|
||||
let apiPrefix = url.match(/^[^/]*\//);
|
||||
if (
|
||||
config.headers.post2rest !== false &&
|
||||
(config.method === 'put' ||
|
||||
config.method === 'patch' ||
|
||||
config.method === 'delete')
|
||||
) {
|
||||
url = _.replace(
|
||||
url,
|
||||
/^[^/]*\//,
|
||||
`${apiPrefix}post2rest/${config.method}/`
|
||||
);
|
||||
config.method = 'post';
|
||||
}
|
||||
|
||||
let token = window.sessionStorage.getItem('authenticated');
|
||||
if (token && url !== '/api/rpc/login') {
|
||||
config.headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
data && (config.data = data);
|
||||
config.url = url;
|
||||
|
||||
return request(config);
|
||||
},
|
||||
resFetcher: ({ url, method, data, config, headers }): any => {
|
||||
config = config || {};
|
||||
config.headers = config.headers || {};
|
||||
config.withCredentials = true;
|
||||
config.baseURL = '';
|
||||
|
||||
if (config.cancelExecutor) {
|
||||
config.cancelToken = new axios.CancelToken(config.cancelExecutor);
|
||||
}
|
||||
|
||||
config.headers = headers || {};
|
||||
config.method = method;
|
||||
|
||||
let auth = window.sessionStorage.getItem('auth');
|
||||
|
||||
if (auth) {
|
||||
config.headers['X-Auth'] = auth;
|
||||
}
|
||||
|
||||
data && (config.data = data);
|
||||
config.url = url;
|
||||
|
||||
return resRequest(config);
|
||||
},
|
||||
isCancel: (e: any) => axios.isCancel(e),
|
||||
notify: (type: 'success' | 'error' | 'info', msg: string) => {
|
||||
if ((isString(msg) && msg) || msg?.message) {
|
||||
toast[type]
|
||||
? toast[type](msg, type === 'error' ? '系统错误' : '系统消息')
|
||||
: console.warn('[Notify]', type, msg);
|
||||
console.log('[notify]', type, msg);
|
||||
}
|
||||
},
|
||||
alert,
|
||||
confirm,
|
||||
copy: (contents: string, options: any = {}) => {
|
||||
const ret = copy(contents, options);
|
||||
ret &&
|
||||
(!options || options.shutup !== true) &&
|
||||
toast.info('内容已拷贝到剪切板');
|
||||
return ret;
|
||||
},
|
||||
}
|
||||
));
|
||||
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<RootRoute store={store} />
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
61
src/components/AMisRenderer.tsx
Normal file
61
src/components/AMisRenderer.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import * as React from 'react';
|
||||
import { render as renderSchema } from 'amis';
|
||||
import { IMainStore } from '@/stores';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { withRouter } from 'react-router';
|
||||
import { Action } from 'amis/lib/types';
|
||||
import renderOptions from '@/utils/renderOptions';
|
||||
|
||||
interface RendererProps {
|
||||
schema?: any;
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
@inject('store')
|
||||
// @ts-ignore
|
||||
@withRouter
|
||||
@observer
|
||||
export default class AMisRenderer extends React.Component<RendererProps, any> {
|
||||
env: any = null;
|
||||
|
||||
handleAction = (e: any, action: Action) => {
|
||||
this.env.alert(`没有识别的动作:${JSON.stringify(action)}`);
|
||||
};
|
||||
|
||||
constructor(props: RendererProps) {
|
||||
super(props);
|
||||
const store = props.store as IMainStore;
|
||||
const history = props.history;
|
||||
|
||||
this.env = renderOptions(store, history);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { schema, store, onAction, ...rest } = this.props;
|
||||
return renderSchema(
|
||||
schema,
|
||||
{
|
||||
data: {
|
||||
user: store.user,
|
||||
currentSemester: store.currentSemester?.id,
|
||||
semester: store.currentSemester,
|
||||
semesterSelect: store.currentSemester?.id,
|
||||
periods: store.periods,
|
||||
basePath: store.basePath,
|
||||
},
|
||||
onAction: (e: any, action: any) => {
|
||||
console.log(action)
|
||||
if (!action || action.actionType === 'url') {
|
||||
return;
|
||||
}
|
||||
if (action.actionType === 'logout') {
|
||||
store.logout();
|
||||
}
|
||||
},
|
||||
theme: store && store.theme,
|
||||
...rest,
|
||||
},
|
||||
this.env
|
||||
);
|
||||
}
|
||||
}
|
||||
215
src/components/UserInfo.tsx
Normal file
215
src/components/UserInfo.tsx
Normal file
@@ -0,0 +1,215 @@
|
||||
import React from 'react';
|
||||
import AMisRenderer from './AMisRenderer';
|
||||
|
||||
interface UserInfoProps {
|
||||
user: any;
|
||||
}
|
||||
interface UserInfoState {
|
||||
open?: boolean;
|
||||
}
|
||||
|
||||
export default class UserInfo extends React.Component<
|
||||
UserInfoProps,
|
||||
UserInfoState
|
||||
> {
|
||||
constructor(props: UserInfoProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
open: false,
|
||||
};
|
||||
this.open = this.open.bind(this);
|
||||
this.close = this.close.bind(this);
|
||||
}
|
||||
|
||||
static defaultProps = {};
|
||||
open() {
|
||||
this.setState({
|
||||
open: true,
|
||||
});
|
||||
}
|
||||
close() {
|
||||
this.setState({
|
||||
open: false,
|
||||
});
|
||||
}
|
||||
handleClickOutside() {
|
||||
this.close();
|
||||
}
|
||||
|
||||
render() {
|
||||
const user = this.props.user;
|
||||
|
||||
const schema = {
|
||||
type: 'page',
|
||||
className: 'user-page',
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '工作台',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'url',
|
||||
args: {
|
||||
url: '/hub/login?jwt=$user.token',
|
||||
params: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
id: 'u:05fc5e4362bc',
|
||||
themeCss: {
|
||||
className: {
|
||||
'padding-and-margin:default': {
|
||||
marginRight: '5px',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// {
|
||||
// type: 'button',
|
||||
// label: '探索',
|
||||
// onEvent: {
|
||||
// click: {
|
||||
// actions: [
|
||||
// {
|
||||
// ignoreError: false,
|
||||
// actionType: 'url',
|
||||
// args: {
|
||||
// url: '/explore?token=$user.token',
|
||||
// params: {},
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
// id: 'u:05fc5e4362bd',
|
||||
// themeCss: {
|
||||
// className: {
|
||||
// 'padding-and-margin:default': {
|
||||
// marginRight: '5px',
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
{
|
||||
type: 'button',
|
||||
label: '资源管理',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'url',
|
||||
args: {
|
||||
url: '/res',
|
||||
params: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
id: 'u:05fc5e4362bc',
|
||||
themeCss: {
|
||||
className: {
|
||||
'padding-and-margin:default': {
|
||||
marginRight: '5px',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'dropdown-button',
|
||||
label: user.name,
|
||||
align: 'right',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
icon: 'fa fa-cog',
|
||||
label: '系统信息 ',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
closeOnEsc: true,
|
||||
title: '系统信息',
|
||||
size: 'sm',
|
||||
type: 'dialog',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<p>智医教学实践平台</p>\n<p>OLMS II</p>',
|
||||
inline: false,
|
||||
id: 'u:369cb33ce9d0',
|
||||
},
|
||||
],
|
||||
showCloseButton: true,
|
||||
showErrorMsg: true,
|
||||
showLoading: true,
|
||||
id: 'u:289af4d19c37',
|
||||
actions: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
icon: 'fa fa-key',
|
||||
label: '修改密码 ',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
closeOnEsc: true,
|
||||
title: '修改登录密码',
|
||||
size: 'sm',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
api: {
|
||||
url: `rest/rpc/change_password`,
|
||||
method: 'post',
|
||||
data: {
|
||||
password: '${password}',
|
||||
},
|
||||
},
|
||||
messages: {
|
||||
saveSuccess: '[密码修改成功] 请使用新密码重新登录',
|
||||
saveFailed: '密码修改失败',
|
||||
},
|
||||
// redirect: '/login',
|
||||
mode: 'horizontal',
|
||||
horizontal: {
|
||||
left: 'col-sm-3',
|
||||
right: 'col-sm-9',
|
||||
},
|
||||
controls: [
|
||||
{
|
||||
type: 'password',
|
||||
name: 'password',
|
||||
required: true,
|
||||
label: '新密码',
|
||||
},
|
||||
// {
|
||||
// type: 'password',
|
||||
// name: 'confirmPassword',
|
||||
// required: true,
|
||||
// label: '重复密码',
|
||||
// },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'logout',
|
||||
level: 'link',
|
||||
icon: 'fa fa-reply',
|
||||
label: '退出登录 ',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return <AMisRenderer schema={schema} />;
|
||||
}
|
||||
}
|
||||
13
src/index.css
Normal file
13
src/index.css
Normal file
@@ -0,0 +1,13 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto',
|
||||
'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans',
|
||||
'Helvetica Neue', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
29
src/index.tsx
Normal file
29
src/index.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import * as React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import App from './App';
|
||||
import 'react-perfect-scrollbar/dist/css/styles.css';
|
||||
import './index.css';
|
||||
|
||||
export function bootstrap(mountTo: HTMLElement) {
|
||||
render(<App />, mountTo);
|
||||
}
|
||||
|
||||
(self as any).MonacoEnvironment = {
|
||||
getWorkerUrl: function (moduleId: any, label: string) {
|
||||
if (label === 'json') {
|
||||
return '/json.worker.bundle.js';
|
||||
}
|
||||
if (label === 'css') {
|
||||
return '/css.worker.bundle.js';
|
||||
}
|
||||
if (label === 'html') {
|
||||
return '/html.worker.bundle.js';
|
||||
}
|
||||
if (label === 'typescript' || label === 'javascript') {
|
||||
return '/ts.worker.bundle.js';
|
||||
}
|
||||
return '/editor.worker.bundle.js';
|
||||
},
|
||||
};
|
||||
|
||||
bootstrap(document.getElementById('root')!);
|
||||
16
src/pages/404.tsx
Normal file
16
src/pages/404.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import * as React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { NotFound } from 'amis';
|
||||
|
||||
export default () => (
|
||||
<NotFound
|
||||
links={
|
||||
<Link to="/" className="list-group-item">
|
||||
<i className="fa fa-chevron-right text-muted" />
|
||||
<i className="fa fa-fw fa-mail-forward m-r-xs" />
|
||||
去首页
|
||||
</Link>
|
||||
}
|
||||
footerText={''}
|
||||
/>
|
||||
);
|
||||
116
src/pages/Editor.tsx
Normal file
116
src/pages/Editor.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Editor, ShortcutKey } from 'amis-editor';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { RouteComponentProps } from 'react-router-dom';
|
||||
import 'amis/lib/themes/cxd.css';
|
||||
import 'amis/lib/helper.css';
|
||||
import 'amis/sdk/iconfont.css';
|
||||
import 'amis-editor-core/lib/style.css';
|
||||
// import 'amis/lib/themes/default.css';
|
||||
import '../scss/editor.scss';
|
||||
|
||||
import { IMainStore } from '../stores';
|
||||
import loadPageByPath from '../utils/loadPageByPath';
|
||||
import renderOptions from '../utils/renderOptions';
|
||||
|
||||
export default inject('store')(
|
||||
observer(function ({
|
||||
store,
|
||||
history,
|
||||
match,
|
||||
}: { store: IMainStore } & RouteComponentProps<{ id: string }>) {
|
||||
const pageId: number = parseInt(match.params.id, 10);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
let nav: any = store.flat_navigations.find((x: any) => x.id === pageId);
|
||||
let pagePath = nav ? nav.schema_path : '';
|
||||
loadPageByPath(pagePath).then((page: any) => {
|
||||
store.updateSchema(page.schema);
|
||||
});
|
||||
})();
|
||||
}, [pageId]);
|
||||
|
||||
function save() {
|
||||
console.log(store.schema);
|
||||
store.copy(JSON.stringify(store.schema));
|
||||
}
|
||||
|
||||
function exit() {
|
||||
history.goBack();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="Editor-Demo">
|
||||
<div className="Editor-header">
|
||||
<div className="Editor-title">可视化编辑器</div>
|
||||
<div className="Editor-view-mode-group-container">
|
||||
<div className="Editor-view-mode-group">
|
||||
<div
|
||||
className={`Editor-view-mode-btn editor-header-icon ${
|
||||
!store.isMobile ? 'is-active' : ''
|
||||
}`}
|
||||
onClick={() => {
|
||||
store.setIsMobile(false);
|
||||
}}
|
||||
>
|
||||
PC
|
||||
</div>
|
||||
<div
|
||||
className={`Editor-view-mode-btn editor-header-icon ${
|
||||
store.isMobile ? 'is-active' : ''
|
||||
}`}
|
||||
onClick={() => {
|
||||
store.setIsMobile(true);
|
||||
}}
|
||||
>
|
||||
H5
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="Editor-header-actions">
|
||||
<ShortcutKey />
|
||||
|
||||
<div
|
||||
className={`header-action-btn m-1 ${
|
||||
store.preview ? 'primary' : ''
|
||||
}`}
|
||||
onClick={() => {
|
||||
store.setPreview(!store.preview);
|
||||
}}
|
||||
>
|
||||
{store.preview ? '编辑' : '预览'}
|
||||
</div>
|
||||
{!store.preview && (
|
||||
<>
|
||||
<div className={`header-action-btn`} onClick={save}>
|
||||
保存
|
||||
</div>
|
||||
<div className={`header-action-btn exit-btn`} onClick={exit}>
|
||||
退出
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="Editor-inner">
|
||||
<Editor
|
||||
theme={store.theme}
|
||||
preview={store.preview}
|
||||
isMobile={store.isMobile}
|
||||
value={store.schema}
|
||||
onChange={(value: any) => store.updateSchema(value)}
|
||||
onPreview={() => {
|
||||
store.setPreview(true);
|
||||
}}
|
||||
onSave={save}
|
||||
className="is-fixed"
|
||||
showCustomRenderersPanel={true}
|
||||
amisEnv={renderOptions(store, history)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
);
|
||||
113
src/pages/Login.tsx
Normal file
113
src/pages/Login.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import { MD5 } from 'crypto-js';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import React from 'react';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import AMisRenderer from '../components/AMisRenderer';
|
||||
import { IMainStore } from '../stores/index';
|
||||
|
||||
interface LoginProps extends RouteComponentProps<any> {
|
||||
store: IMainStore;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'wrapper',
|
||||
body: [
|
||||
{
|
||||
type: 'plain',
|
||||
tpl: '用户登录',
|
||||
inline: false,
|
||||
className: 'block m-t-xxl m-b-xl text-center text-2x text-info',
|
||||
},
|
||||
{
|
||||
type: 'form',
|
||||
submitText: '登录',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/login',
|
||||
data: {
|
||||
code: '${username}',
|
||||
password: '${password}',
|
||||
},
|
||||
requestAdaptor: (api: any) => {
|
||||
api.body.password = MD5(api.body.password).toString();
|
||||
return api;
|
||||
},
|
||||
},
|
||||
wrapWithPanel: false,
|
||||
canAccessSuperData: false,
|
||||
messages: {
|
||||
saveSuccess: '',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
label: '',
|
||||
type: 'input-text',
|
||||
name: 'username',
|
||||
required: true,
|
||||
placeholder: '用户名',
|
||||
},
|
||||
{
|
||||
type: 'input-password',
|
||||
label: '',
|
||||
name: 'password',
|
||||
placeholder: '密码',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'submit',
|
||||
label: '登录',
|
||||
size: 'lg',
|
||||
block: true,
|
||||
level: 'primary',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
className: 'bg-white w-xxl Modal-content',
|
||||
},
|
||||
],
|
||||
className: 'bg-white h-full',
|
||||
};
|
||||
|
||||
@inject('store')
|
||||
// @ts-ignore
|
||||
@withRouter
|
||||
@observer
|
||||
export default class LoginRoute extends React.Component<LoginProps> {
|
||||
handleFormSaved = (value: any) => {
|
||||
const store = this.props.store;
|
||||
const history = this.props.history;
|
||||
let user = null;
|
||||
|
||||
if (value.items) {
|
||||
user = value.items[0].login;
|
||||
} else {
|
||||
user = value;
|
||||
}
|
||||
store.notify('登录成功!');
|
||||
store.user.login(user);
|
||||
history.push('/');
|
||||
store
|
||||
.resFetcher({
|
||||
url: '/res/api/login',
|
||||
method: 'post',
|
||||
data: {
|
||||
username: '',
|
||||
password: '',
|
||||
recaptcha: '',
|
||||
},
|
||||
})
|
||||
.then((res: any) => {
|
||||
console.log(res);
|
||||
document.cookie = `auth=${res.data.data}; Path=/; SameSite=Strict;"`;
|
||||
sessionStorage.setItem('auth', res.data.data);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return <AMisRenderer onFinished={this.handleFormSaved} schema={schema} />;
|
||||
}
|
||||
}
|
||||
124
src/pages/Preview.tsx
Normal file
124
src/pages/Preview.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
import React from 'react';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import { IMainStore } from '../stores/index';
|
||||
import AMisRenderer from '../components/AMisRenderer';
|
||||
|
||||
interface LoginProps extends RouteComponentProps<any> {
|
||||
store: IMainStore;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
type: 'page',
|
||||
bodyClassName: 'w-4/5 m-auto',
|
||||
body: [
|
||||
{
|
||||
type: 'plain',
|
||||
tpl: '实验开放教学登记表',
|
||||
inline: false,
|
||||
className: 'text-center text-2x font-bold',
|
||||
},
|
||||
{
|
||||
type: 'plain',
|
||||
tpl: '实验名称:${project}',
|
||||
inline: false,
|
||||
className: 'm-t-xs',
|
||||
},
|
||||
{
|
||||
type: 'plain',
|
||||
tpl: '课堂编号:${id} 指导教师:${teacher} 地点:${location} \n 实验时间:${date} ${start_time}',
|
||||
inline: false,
|
||||
className: 'm-sm',
|
||||
},
|
||||
{
|
||||
type: 'plain',
|
||||
tpl: '备注:登记表请妥善保存,学期结束时必须交到实验室存档,不能遗失!',
|
||||
inline: false,
|
||||
className: '',
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/user2projects?select=id,student:users!user2project_student_id_fkey(id,code,name,user_orgs(orgs(name)))&schedule_id=eq.${id}&schedule_status=in.(elected,stopped)&order=id.asc',
|
||||
adaptor:
|
||||
'payload.data.items = payload.data.items.map((item, index) => {\r\n return {\r\n ...item,\r\n order_number: index + 1\r\n }\r\n});\r\nreturn payload;',
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
name: 'order_number',
|
||||
label: '序号',
|
||||
type: 'text',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
name: 'student.code',
|
||||
label: '学号',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '姓名',
|
||||
name: 'student.name',
|
||||
},
|
||||
{
|
||||
name: 'student.user_orgs[0].orgs.name',
|
||||
label: '班级',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '考勤',
|
||||
name: 'att',
|
||||
placeholder: '',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '预习',
|
||||
name: 'prepare',
|
||||
placeholder: '',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '操作',
|
||||
name: 'op',
|
||||
placeholder: '',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '报告',
|
||||
name: 'report',
|
||||
placeholder: '',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '备注',
|
||||
name: 'beizhu',
|
||||
placeholder: '',
|
||||
},
|
||||
],
|
||||
columnsTogglable: false,
|
||||
affixHeader: false,
|
||||
showHeader: true,
|
||||
showFooter: true,
|
||||
messages: {},
|
||||
className: 'm-t-sm preview',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<p>教师签字: </p>',
|
||||
inline: false,
|
||||
className: 'text-right',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@inject('store')
|
||||
// @ts-ignore
|
||||
@withRouter
|
||||
@observer
|
||||
export default class LoginRoute extends React.Component<LoginProps> {
|
||||
render() {
|
||||
return <AMisRenderer schema={schema} />;
|
||||
}
|
||||
}
|
||||
41
src/pages/Schema.tsx
Normal file
41
src/pages/Schema.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { RouteComponentProps, useParams } from 'react-router-dom';
|
||||
import { IMainStore } from '../stores/index';
|
||||
import AMisRenderer from '../components/AMisRenderer';
|
||||
import loadPageByPath from '../utils/loadPageByPath';
|
||||
|
||||
export default inject('store')(
|
||||
observer(function ({
|
||||
store,
|
||||
}: { store: IMainStore } & RouteComponentProps<{
|
||||
id: string;
|
||||
}>) {
|
||||
let { centre_id, id } = useParams<any>();
|
||||
const [schema, setSchema] = useState({
|
||||
type: 'page',
|
||||
body: {
|
||||
type: 'spinner',
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setSchema({
|
||||
type: 'page',
|
||||
body: {
|
||||
type: 'spinner',
|
||||
},
|
||||
});
|
||||
let nav: any = store.flat_navigations
|
||||
.filter((x: any) => x.path !== null)
|
||||
.find((x: any) => x.id.toString() === id);
|
||||
let pagePath = nav ? nav.schema_path : '';
|
||||
loadPageByPath(pagePath).then((page: any) => {
|
||||
setSchema(page.schema);
|
||||
});
|
||||
}, [id]);
|
||||
|
||||
// TODO: schema中的api有时没自动加载
|
||||
return <AMisRenderer schema={schema} centre_id={centre_id} />;
|
||||
})
|
||||
);
|
||||
35
src/pages/Student.tsx
Normal file
35
src/pages/Student.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { RouteComponentProps } from 'react-router-dom';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import AMisRenderer from '../components/AMisRenderer';
|
||||
import { IMainStore } from '../stores';
|
||||
|
||||
export default inject('store')(
|
||||
observer(function ({
|
||||
page,
|
||||
store,
|
||||
location,
|
||||
history,
|
||||
match,
|
||||
}: { page: number } & { store: IMainStore } & RouteComponentProps<{
|
||||
id: string;
|
||||
}>) {
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '请使用微信选课',
|
||||
},
|
||||
],
|
||||
aside: [],
|
||||
messages: {},
|
||||
// "initApi": {
|
||||
// "method": "get",
|
||||
// "url": "/api/user2schedules"
|
||||
// }
|
||||
};
|
||||
|
||||
return <AMisRenderer schema={schema} />;
|
||||
})
|
||||
);
|
||||
9
src/pages/admin/Dashboard.tsx
Normal file
9
src/pages/admin/Dashboard.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import schema2component from '@/utils/schema2component';
|
||||
|
||||
const schema = {
|
||||
type: 'page',
|
||||
title: 'Dashboard',
|
||||
body: [],
|
||||
};
|
||||
|
||||
export default schema2component(schema);
|
||||
346
src/pages/admin/index.tsx
Normal file
346
src/pages/admin/index.tsx
Normal file
@@ -0,0 +1,346 @@
|
||||
import RouterGuard from '@/routes/RouterGuard';
|
||||
import { IMainStore } from '@/stores';
|
||||
import { AsideNav, Button, Layout, toast } from 'amis';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Link,
|
||||
Redirect,
|
||||
Route,
|
||||
RouteComponentProps,
|
||||
Switch,
|
||||
matchPath,
|
||||
} from 'react-router-dom';
|
||||
import UserInfo from '../../components/UserInfo';
|
||||
import SchemaRoute from '../Schema';
|
||||
import Student from '../Student';
|
||||
import AMisRenderer from '@/components/AMisRenderer';
|
||||
|
||||
type NavItem = {
|
||||
label: string;
|
||||
children?: Array<NavItem>;
|
||||
icon?: string;
|
||||
path?: string;
|
||||
component?: React.ReactType;
|
||||
getComponent?: () => Promise<React.ReactType>;
|
||||
};
|
||||
let PATH_PREFIX = '';
|
||||
|
||||
function isActive(link: any, location: any) {
|
||||
const ret = matchPath(location.pathname, {
|
||||
path: link ? link.replace(/\?.*$/, '') : '',
|
||||
exact: true,
|
||||
strict: true,
|
||||
});
|
||||
|
||||
return !!ret;
|
||||
}
|
||||
|
||||
export interface AdminProps extends RouteComponentProps<any> {
|
||||
store: IMainStore;
|
||||
}
|
||||
|
||||
@inject('store')
|
||||
@observer
|
||||
export default class Admin extends React.Component<AdminProps, any> {
|
||||
state = {
|
||||
pathname: '',
|
||||
hasLoadMenu: false,
|
||||
navigations: [],
|
||||
};
|
||||
|
||||
logout = () => {
|
||||
const store = this.props.store;
|
||||
store.user.logout();
|
||||
const history = this.props.history;
|
||||
history.replace(`/login`);
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const store = this.props.store;
|
||||
const history = this.props.history;
|
||||
console.log('componentDidMount, store.user:', store.user);
|
||||
if (!store.user.isAuthenticated) {
|
||||
// toast['error']('用户未登陆,请先登陆!', '消息');
|
||||
history.replace(`/login`);
|
||||
}
|
||||
this.refreshMenu();
|
||||
}
|
||||
|
||||
refreshMenu = () => {
|
||||
const store = this.props.store;
|
||||
let pathname = this.props.location.pathname;
|
||||
console.log('location:', pathname);
|
||||
console.log('store.user:', store.user);
|
||||
if (
|
||||
pathname !== 'login' &&
|
||||
!this.state.hasLoadMenu &&
|
||||
store.user.isAuthenticated
|
||||
) {
|
||||
store.updateCentres();
|
||||
store.updateSemesters();
|
||||
store.updatePeriods();
|
||||
}
|
||||
};
|
||||
|
||||
renderHeader() {
|
||||
const store = this.props.store;
|
||||
const theme = 'cxd';
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={`${theme}-Layout-brandBar`}>
|
||||
<button
|
||||
onClick={store.toggleOffScreen}
|
||||
className="pull-right visible-xs"
|
||||
>
|
||||
<i className="glyphicon glyphicon-align-justify"></i>
|
||||
</button>
|
||||
<div className={`${theme}-Layout-brand`}>
|
||||
<i className="fa "></i>
|
||||
<span className="hidden-folded">{store.name}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`${theme}-Layout-headerBar`}
|
||||
style={{ justifyContent: 'space-between' }}
|
||||
>
|
||||
<div className="nav navbar-nav hidden-xs">
|
||||
<Button
|
||||
level="link"
|
||||
className="no-shadow navbar-btn"
|
||||
onClick={store.toggleAsideFolded}
|
||||
tooltip="展开或收起侧边栏"
|
||||
placement="bottom"
|
||||
iconOnly
|
||||
>
|
||||
<i
|
||||
className={store.asideFolded ? 'fa fa-indent' : 'fa fa-dedent'}
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={()=>{
|
||||
window.open('/doc')
|
||||
}}
|
||||
>帮助</Button>
|
||||
</div>
|
||||
|
||||
<div className="hidden-xs">
|
||||
<UserInfo user={store.user} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderCentreAside(
|
||||
prefix: string,
|
||||
items: Array<NavItem>,
|
||||
centre_id: number
|
||||
): Array<NavItem> {
|
||||
if (!items) {
|
||||
return [];
|
||||
}
|
||||
return items.map((item: any) => {
|
||||
if (item.children && item.children.length > 0) {
|
||||
return {
|
||||
...item,
|
||||
children: this.renderCentreAside(prefix, item.children, centre_id),
|
||||
};
|
||||
}
|
||||
|
||||
// let centreSubPath = item.path && item.path.replace('centre/:id/', '');
|
||||
return {
|
||||
...item,
|
||||
path: `${prefix}/${item.id}?centre_id=${centre_id}`,
|
||||
children: undefined,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
renderAside() {
|
||||
const location = this.props.location;
|
||||
const store = this.props.store;
|
||||
let navigations = store.navigations.filter(
|
||||
(item: any) => item.label !== '教学中心'
|
||||
);
|
||||
let centre_navigations = store.navigations.filter(
|
||||
(item: any) => item.label === '教学中心'
|
||||
);
|
||||
let dynamic_navigations: Array<NavItem> = [];
|
||||
this.props.store.centres?.forEach((item: any) => {
|
||||
let centre_path = `centre/${item.id}`;
|
||||
dynamic_navigations.push({
|
||||
label: item.name,
|
||||
icon: 'fa fa-institution',
|
||||
// path: centre_path,
|
||||
children: centre_navigations[0]
|
||||
? this.renderCentreAside(
|
||||
centre_path,
|
||||
centre_navigations[0].children,
|
||||
item.id
|
||||
)
|
||||
: [],
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<AsideNav
|
||||
key={store.asideFolded ? 'folded-aside' : 'aside'}
|
||||
navigations={[{ children: navigations.concat(dynamic_navigations) }]}
|
||||
renderLink={({ link, toggleExpand, classnames: cx, depth }: any) => {
|
||||
if (link.hidden) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let children = [];
|
||||
|
||||
if (link.children && link.children.length > 0) {
|
||||
children.push(
|
||||
<span
|
||||
key="expand-toggle"
|
||||
className={cx('AsideNav-itemArrow')}
|
||||
onClick={(e) => toggleExpand(link, e)}
|
||||
></span>
|
||||
);
|
||||
} else {
|
||||
if (
|
||||
process.env.NODE_ENV === 'development' &&
|
||||
store.user.role === 'dev'
|
||||
) {
|
||||
children.push(
|
||||
<i
|
||||
key="edit"
|
||||
data-tooltip="编辑"
|
||||
data-position="bottom"
|
||||
className={'navbtn fa fa-pencil'}
|
||||
onClick={(e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
this.props.history.push(`/edit/${link.page}`);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
link.badge &&
|
||||
children.push(
|
||||
<b
|
||||
key="badge"
|
||||
className={cx(
|
||||
`AsideNav-itemBadge`,
|
||||
link.badgeClassName || 'bg-info'
|
||||
)}
|
||||
>
|
||||
{link.badge}
|
||||
</b>
|
||||
);
|
||||
|
||||
if (link.icon) {
|
||||
children.push(
|
||||
<i key="icon" className={cx(`AsideNav-itemIcon`, link.icon)} />
|
||||
);
|
||||
} else if (store.asideFolded && depth === 1) {
|
||||
children.push(
|
||||
<i
|
||||
key="icon"
|
||||
className={cx(
|
||||
`AsideNav-itemIcon`,
|
||||
link.children ? 'fa fa-folder' : 'fa fa-info'
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
children.push(
|
||||
<span key="label" className={cx('AsideNav-itemLabel')}>
|
||||
{link.label}
|
||||
</span>
|
||||
);
|
||||
|
||||
return link.schema_path ? (
|
||||
link.active ? (
|
||||
<a>{children}</a>
|
||||
) : (
|
||||
<Link to={`/page/${link.path}`}>{children}</Link>
|
||||
)
|
||||
) : (
|
||||
<a
|
||||
onClick={
|
||||
link.onClick
|
||||
? link.onClick
|
||||
: link.children
|
||||
? () => toggleExpand(link)
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
}}
|
||||
// renderSubLinks={({link,renderLink, depth}:any) =>{
|
||||
// return (link.children && link.children.length ? (
|
||||
// <ul className={'AsideNav-subList'}>
|
||||
// {link.label ? (
|
||||
// <li key="subHeader" className={'AsideNav-subHeader'}>
|
||||
// <a>{link.label}</a>
|
||||
// </li>
|
||||
// ) : null}
|
||||
// {link.children.map((link:any, key:any) =>
|
||||
// renderLink(link, key, {}, depth + 1)
|
||||
// )}
|
||||
// </ul>
|
||||
// ) : link.label && depth === 1 ? (
|
||||
// <div className={'AsideNav-tooltip'}>{link.label}</div>
|
||||
// ) : null)
|
||||
// }}
|
||||
isActive={(link: any) =>
|
||||
isActive(
|
||||
link.path && link.path[0] === '/'
|
||||
? link.path
|
||||
: `${PATH_PREFIX}/page/${link.path}`,
|
||||
location
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const store = this.props.store;
|
||||
let pathname = this.props.location.pathname;
|
||||
console.log('location:', pathname);
|
||||
if (pathname === 'login') {
|
||||
return (
|
||||
<Switch>
|
||||
<RouterGuard />
|
||||
<Redirect to={`/404`} />
|
||||
</Switch>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<Layout
|
||||
theme="cxd"
|
||||
aside={this.renderAside()}
|
||||
header={this.renderHeader()}
|
||||
folded={store.asideFolded}
|
||||
offScreen={store.offScreen}
|
||||
>
|
||||
<Switch>
|
||||
<Redirect to={`/${store.defaultNav}`} from={`/`} exact />
|
||||
|
||||
<Route
|
||||
path="/page/centre/:centre_id/:id"
|
||||
component={SchemaRoute}
|
||||
/>
|
||||
<Route path="/page/:id" component={SchemaRoute} />
|
||||
<RouterGuard />
|
||||
<Redirect to={`/404`} />
|
||||
</Switch>
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
66
src/pages/schema/centre/att/ercode.schema.ts
Normal file
66
src/pages/schema/centre/att/ercode.schema.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
const schema = {
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '软件下载',
|
||||
size: 'md',
|
||||
level: 'primary',
|
||||
block: false,
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
args: {
|
||||
url: 'https://www.platosoft.org/download/ATT.zip',
|
||||
},
|
||||
actionType: 'url',
|
||||
},
|
||||
],
|
||||
weight: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '使用说明',
|
||||
actionType: 'url',
|
||||
size: 'md',
|
||||
level: 'primary',
|
||||
className: 'm-l',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
args: {
|
||||
url: 'https://www.platosoft.org/doc/olms/manual/admin/att_qr.html',
|
||||
},
|
||||
actionType: 'url',
|
||||
},
|
||||
],
|
||||
weight: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'formula',
|
||||
name: 'api_url',
|
||||
formula: 'data.api_url=location.origin + this.basePath + "/api"',
|
||||
},
|
||||
{
|
||||
type: 'property',
|
||||
title: '配置参数',
|
||||
items: [
|
||||
{ label: '接口地址', content: '$api_url', span: 1 },
|
||||
{ label: '本地秘钥', content: 'olms', span: 1 },
|
||||
],
|
||||
column: 1,
|
||||
mode: 'table',
|
||||
className: 'w-xxl m-t',
|
||||
},
|
||||
],
|
||||
title: '二维码考勤',
|
||||
type: 'page',
|
||||
messages: {},
|
||||
};
|
||||
|
||||
export { schema };
|
||||
28
src/pages/schema/centre/att/face.schema.ts
Normal file
28
src/pages/schema/centre/att/face.schema.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
const schema = {
|
||||
body: [{ type: 'html', html: '<h1>人脸考勤使用说明</h1>' }],
|
||||
type: 'page',
|
||||
title: '人脸考勤使用说明',
|
||||
toolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '下载客户端',
|
||||
level: 'success',
|
||||
blank: true,
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
args: {
|
||||
url: '/download/faceApp.zip',
|
||||
},
|
||||
actionType: 'url',
|
||||
},
|
||||
],
|
||||
weight: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export { schema };
|
||||
461
src/pages/schema/centre/att/index.schema.ts
Normal file
461
src/pages/schema/centre/att/index.schema.ts
Normal file
@@ -0,0 +1,461 @@
|
||||
const schema = {
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
className: '',
|
||||
name: 'filter',
|
||||
title: '表单',
|
||||
wrapWithPanel: false,
|
||||
reload:
|
||||
'adopt?organization_id=eq.$centre_id&project_id=$project_id&teacher_id=$teacher_id&location_id=$location_id&date=$date&class_list=$class_list&no_tch=$no_tch',
|
||||
submitOnInit: true,
|
||||
submitOnChange: true,
|
||||
initApi: {
|
||||
method: 'get',
|
||||
url: 'rest/users?role=eq.teacher&id=eq.${user.id}&order=code',
|
||||
adaptor:
|
||||
"if (payload.data.items.length !== 0) {\r\n return {\r\n teacher_id: 'eq.'.concat(payload.data.items[0].id)\r\n }\r\n} else {\r\n return {}\r\n}",
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
name: 'project_id',
|
||||
label: '实验',
|
||||
mode: 'inline',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/projects?organization_id=eq.$centre_id&semester_id=eq.$currentSemester&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: "eq.".concat(item.id)\r\n }\r\n })\r\n}',
|
||||
},
|
||||
size: 'md',
|
||||
checkAll: false,
|
||||
clearable: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'input-date-range',
|
||||
name: 'date',
|
||||
mode: 'inline',
|
||||
label: '日期',
|
||||
clearable: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
value: '',
|
||||
format: 'YYYY-MM-DD',
|
||||
ranges:
|
||||
'yesterday,today,7daysago,prevweek,thismonth,prevmonth,prevquarter',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '地点',
|
||||
name: 'location_id',
|
||||
mode: 'inline',
|
||||
checkAll: false,
|
||||
clearable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/locations?organization_id=eq.$centre_id&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: "eq.".concat(item.id)\r\n }\r\n })\r\n}',
|
||||
},
|
||||
size: 'md',
|
||||
className: 'm-l-sm',
|
||||
inputClassName: 'm-l-xs',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'teacher_id',
|
||||
label: '教师',
|
||||
checkAll: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/users?role=eq.teacher&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: "eq.".concat(item.id)\r\n }\r\n })\r\n}',
|
||||
},
|
||||
mode: 'inline',
|
||||
size: 'md',
|
||||
clearable: true,
|
||||
className: 'm-l-sm',
|
||||
inputClassName: 'm-l-xs',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'tree-select',
|
||||
label: '预留班级',
|
||||
name: 'class_list',
|
||||
mode: 'horizontal',
|
||||
searchable: true,
|
||||
clearable: true,
|
||||
multiple: true,
|
||||
joinValues: true,
|
||||
onlyChildren: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?select=*&path=cd.root&order=name',
|
||||
adaptor:
|
||||
"const recursive = id => {\r\n let nodes = payload.data.items.filter(x => {\r\n return x.parent === id\r\n })\r\n\r\n return nodes.map(item => {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: item.id,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}\r\n\r\nlet school = payload.data.items.find(x => x.type === 'school')\r\n\r\nreturn {\r\n ...payload,\r\n data: recursive(school.id)\r\n}\r\n",
|
||||
},
|
||||
withChildren: true,
|
||||
initiallyOpen: false,
|
||||
heightAuto: true,
|
||||
virtualThreshold: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'switch',
|
||||
name: 'no_tch',
|
||||
mode: 'inline',
|
||||
value: 0,
|
||||
option: '显示无教师场次',
|
||||
optionAtLeft: false,
|
||||
trueValue: 1,
|
||||
falseValue: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
mode: 'inline',
|
||||
},
|
||||
{ type: 'divider', lineStyle: 'solid' },
|
||||
{
|
||||
type: 'crud',
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/schedule_stats?organization_id=eq.$centre_id&semester_id=eq.$currentSemester&is_publish=is.true&order=date.asc,start_time.asc',
|
||||
data: { page: '$page', perPage: '$perPage', '&': '$$' },
|
||||
adaptor:
|
||||
"let supportDateTimeFormat = typeof(Intl.DateTimeFormat) === 'function';\r\npayload.data.items.forEach((item,index) => {\r\n item.day = supportDateTimeFormat ? `${item.date}(${new Intl.DateTimeFormat('zh-CN', { weekday: 'short'}).format(new Date(item.date))})` : item.date;\r\n item.row_number = index + 1\r\n + (api.body.page - 1)\r\n * api.body.perPage\r\n let att_count = item.att_y_count\r\n + item.att_y_late_count\r\n + item.att_y_unclean_count\r\n item.att_num = \"\".concat(att_count, '/', item.current_student_number)\r\n item.att_num_status = att_count === item.current_student_number\r\n ? 1\r\n : 0\r\n item.stu_title = item.project_name.concat(\r\n ' :: ', item.date, ' ', item.period_name,\r\n ' :: ', item.location_name,\r\n ' :: ', item.teacher_name ? item.teacher_name : '无教师认领'\r\n )\r\n})\r\n\r\nreturn {\r\n ...payload\r\n}",
|
||||
requestAdaptor:
|
||||
"let url = api.url.replace(/project_id=&|teacher_id=&|date=&|location_id=&|no_tch=&|no_tch=0/g, '')\r\n\r\nif (api.body.class_list === '') {\r\n url = url.replace('&class_list=', '')\r\n} else {\r\n let class_list = api.body.class_list && api.body.class_list.replace(',', '%2C')\r\n url = url.replace(class_list, 'cs.{'.concat(class_list, '}'))\r\n}\r\n\r\nconst pattern = /(?:date=)(\\d+-\\d+-\\d+)%2C(\\d+-\\d+-\\d+)/g\r\nconst result = pattern.exec(url)\r\nif (result && result[1] === result[2]) {\r\n url = url.replace(result[0], 'date=eq.'.concat(result[1]))\r\n} else if (result) {\r\n url = url.replace(result[0], 'date=gte.'.concat(result[1], '&date=lte.', result[2]))\r\n} else {\r\n url = url.replace('&date=','')\r\n}\r\nconst is_no_tch = /no_tch=1/g.test(url)\r\nconst has_teacher = /teacher_id=.+?(?=&)/g.test(url)\r\nif (is_no_tch && has_teacher) {\r\n const teacher = /teacher_id=.+?(?=&)/g.exec(url)\r\n url = url.replace(teacher, 'or=('.concat(teacher, ',teacher_id.is.null)')).replace('teacher_id=', 'teacher_id.')\r\n url = url.replace(/no_tch=1/g, '')\r\n} else if (is_no_tch && !has_teacher) {\r\n url = url.replace(/no_tch=1/g, 'teacher_id=is.null')\r\n}\r\nreturn {\r\n ...api,\r\n url: url\r\n}",
|
||||
},
|
||||
syncLocation: false,
|
||||
name: 'adopt',
|
||||
columns: [
|
||||
{
|
||||
name: 'row_number',
|
||||
type: 'plain',
|
||||
label: '序号',
|
||||
tpl: '',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
type: 'tpl',
|
||||
placeholder: '-',
|
||||
label: '实验',
|
||||
tpl: '<% if (this.scheduled_class_name) { %>\n <%= this.project_name %>(<%= this.scheduled_class_name %>)\n<% } else { %>\n <%= this.project_name %>\n<% } %>',
|
||||
},
|
||||
{ name: 'day', label: '日期', type: 'text', placeholder: '-' },
|
||||
{ type: 'text', name: 'period_name', label: '节次', placeholder: '-' },
|
||||
{
|
||||
type: 'text',
|
||||
name: 'location_name',
|
||||
placeholder: '-',
|
||||
label: '地点',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '教师',
|
||||
name: '',
|
||||
placeholder: '-',
|
||||
tpl: '<% if (this.teacher_name) { %>\n <span class="label label-info" style="font-size: smaller;"><%= this.teacher_name %></span>\n<% } else { %>\n <span class="label label-warning" style="font-size: smaller;">未认领</span>\n<% } %>',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '考勤状态',
|
||||
placeholder: '-',
|
||||
tpl: '<% if(this.att_num_status) { %>\n <span class="label label-success"><%= this.att_num %></span>\n<% } else { %>\n <span class="label label-warning"><%= this.att_num %></span>\n<% } %>',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
label: '认领场次',
|
||||
type: 'button',
|
||||
actionType: 'ajax',
|
||||
level: 'success',
|
||||
size: 'xs',
|
||||
visibleOn:
|
||||
"this.enableClaim && (this.user.role === 'teacher' && !this.teacher_id)",
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/schedules/$id',
|
||||
data: { teacher_id: '${user.id}' },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '取消认领',
|
||||
actionType: 'ajax',
|
||||
level: 'danger',
|
||||
size: 'xs',
|
||||
visibleOn:
|
||||
'this. enableClaim && this.teacher_code === this.user.code',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/schedules/$id',
|
||||
data: { teacher_id: null },
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '查看场次',
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '学生信息',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/user2projects?select=id,att_status,att_status_text:user2project_att_status_dict_fk(dictvalue),att_time,score,schedule_status:user2project_schedule_status_dict_fk(dictvalue),student:users!user2project_student_id_fkey(id,code,name,user_orgs(orgs(name)))&schedule_id=eq.${id}&schedule_status=in.(elected,stopped)&order=ordernumber_of_schedule',
|
||||
data: null,
|
||||
adaptor:
|
||||
'payload.data.items.forEach((item,index) => {\r\n item.row_number=index+1;\r\n})\r\nreturn {\r\n ...payload\r\n}',
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
label: '序号',
|
||||
type: 'plain',
|
||||
inline: false,
|
||||
name: 'row_number',
|
||||
tpl: '',
|
||||
},
|
||||
{
|
||||
label: '选课状态',
|
||||
type: 'plain',
|
||||
name: 'schedule_status.dictvalue',
|
||||
placeholder: '-',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '学生',
|
||||
name: 'student.name',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '学号',
|
||||
name: 'student.code',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '班级',
|
||||
name: 'student.user_orgs[0].orgs.name',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'date',
|
||||
label: '出勤',
|
||||
name: 'att_time',
|
||||
placeholder: '-',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '考勤状态',
|
||||
name: 'att_status_text.dictvalue',
|
||||
placeholder: '-',
|
||||
groupName: '',
|
||||
remark: '',
|
||||
inline: true,
|
||||
visibleOn:
|
||||
"this.user.role !== 'teacher' || (this.user.role === 'teacher' && this.teacher_id === user.id)",
|
||||
quickEdit: {
|
||||
mode: 'popOver',
|
||||
type: 'select',
|
||||
label: '',
|
||||
name: 'att_status',
|
||||
checkAll: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/dicts?typecode=eq.014',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: item.dictvalue,\r\n value: item.dictkey\r\n }\r\n })\r\n}',
|
||||
},
|
||||
size: 'sm',
|
||||
saveImmediately: {
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/user2projects/${id}',
|
||||
data: { att_status: '${att_status}' },
|
||||
dataType: 'json',
|
||||
requestAdaptor:
|
||||
"let temp = api.data.att_status.slice(0, 5);\r\n\r\nif (temp === 'att_y') {\r\n let date = new Date();\r\n api.data.att_time=`${date.getFullYear()}-${date.getMonth()+1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;\r\n} else {\r\n api.data.att_time = null;\r\n}\r\n\r\nreturn api;",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '考勤状态',
|
||||
name: 'att_status_text.dictvalue',
|
||||
placeholder: '-',
|
||||
groupName: '',
|
||||
remark: '',
|
||||
inline: true,
|
||||
visibleOn:
|
||||
"this.user.role === 'teacher' && this.teacher_id !== user.id",
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
syncLocation: false,
|
||||
bodyClassName: '',
|
||||
className: '',
|
||||
headerToolbar: [
|
||||
'bulkActions',
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '${stu_title}',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '打印',
|
||||
size: 'sm',
|
||||
level: 'primary',
|
||||
className: 'pull-right',
|
||||
id: 'u:114eb8de43c8',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: true,
|
||||
actionType: 'url',
|
||||
args: {
|
||||
url: '/admin/preview?id=${id}&project=${project_name}&location=${location_name}&teacher=${teacher_name}&date=${date}&start_time=${start_time}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'export-excel',
|
||||
label: '全量导出 Excel',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/user2projects?select=id,att_status,att_status_text:user2project_att_status_dict_fk(dictvalue),att_time,score,schedule_status:user2project_schedule_status_dict_fk(dictvalue),student:users!user2project_student_id_fkey(id,code,name,user_orgs(orgs(name)))&schedule_id=eq.${id}&schedule_status=in.(elected,stopped)&order=id.asc',
|
||||
data: null,
|
||||
adaptor:
|
||||
'payload.data.items.forEach((item,index) => {\r\n item.row_number=index+1;\r\n})\r\nreturn {\r\n ...payload\r\n}',
|
||||
},
|
||||
filename: '考勤信息',
|
||||
align: 'right',
|
||||
id: 'u:d74fe0b275db',
|
||||
},
|
||||
],
|
||||
bulkActions: [
|
||||
{
|
||||
label: '批量考勤',
|
||||
actionType: 'dialog',
|
||||
type: 'button',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '批量考勤',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
controls: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '选项',
|
||||
name: 'att',
|
||||
options: [],
|
||||
checkAll: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/dicts?typecode=eq.014',
|
||||
adaptor:
|
||||
'return {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n label: item.dictvalue,\r\n value: item.dictkey\r\n }\r\n })\r\n}',
|
||||
},
|
||||
},
|
||||
{ type: 'hidden', name: 'id', value: '${ids}' },
|
||||
],
|
||||
wrapWithPanel: false,
|
||||
initApi: '',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/user2projects?id=in.(${ids})',
|
||||
data: { att_status: '$att' },
|
||||
requestAdaptor:
|
||||
"let temp = api.data.att_status.slice(0, 5);\r\n\r\nif (temp === 'att_y') {\r\n let date = new Date();\r\n api.data.att_time=`${date.getFullYear()}-${date.getMonth()+1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;\r\n} else {\r\n api.data.att_time = null;\r\n}\r\n\r\nreturn api;",
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
visibleOn:
|
||||
"this.user.role !== 'teacher' || (this.user.role === 'teacher' && this.teacher_id === user.id)",
|
||||
},
|
||||
],
|
||||
footerToolbar: [],
|
||||
perPageField: '',
|
||||
pageField: '',
|
||||
affixHeader: false,
|
||||
perPageAvailable: [10],
|
||||
hideQuickSaveBtn: true,
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
closeOnOutside: false,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
actions: [],
|
||||
},
|
||||
level: 'primary',
|
||||
size: 'xs',
|
||||
reload: 'adopt',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
headerToolbar: [],
|
||||
footerToolbar: [
|
||||
{ type: 'pagination' },
|
||||
{ type: 'switch-per-page' },
|
||||
{ type: 'statistics' },
|
||||
],
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
},
|
||||
],
|
||||
type: 'page',
|
||||
messages: {},
|
||||
bodyClassName: '',
|
||||
title: '',
|
||||
style: {},
|
||||
initApi: {
|
||||
method: 'get',
|
||||
url: 'rest/config?key=eq.system&organization_id=eq.${centre_id}',
|
||||
adaptor:
|
||||
'let item = payload.data.items[0];\r\nreturn {\r\n ...payload,\r\n data: {\r\n enableClaim: payload.data.items[0].value.enableClaim\r\n }\r\n}',
|
||||
},
|
||||
};
|
||||
|
||||
export { schema };
|
||||
305
src/pages/schema/centre/att/machine.schema.ts
Normal file
305
src/pages/schema/centre/att/machine.schema.ts
Normal file
@@ -0,0 +1,305 @@
|
||||
const schema = {
|
||||
body: [
|
||||
{
|
||||
type: 'panel',
|
||||
className: 'Panel--default',
|
||||
title: '',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/users_ex?select=organization_id:org_id,id,code,name,machine_locations(locations(id,name))&role=eq.machine&org_id=eq.$centre_id&order=code',
|
||||
adaptor:
|
||||
"return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n id: item.id,\r\n code: item.code,\r\n name: item.name,\r\n machine_locations: item.machine_locations,\r\n select_loc: item.machine_locations.map(item => {\r\n return ''.concat(item.locations.id)\r\n })\r\n }\r\n })\r\n}",
|
||||
},
|
||||
mode: 'cards',
|
||||
messages: {},
|
||||
card: {
|
||||
type: 'card',
|
||||
header: { title: '${code}', subTitle: '${name}' },
|
||||
body: [
|
||||
{ type: 'divider', label: '关联场地' },
|
||||
{
|
||||
type: 'button-group',
|
||||
label: '',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '关联场地',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/machine_locations',
|
||||
dataType: 'json',
|
||||
requestAdaptor:
|
||||
'return {\r\n ...api,\r\n data: api.data.location_id.map(item => {\r\n return {\r\n user_id: api.data.id,\r\n location_id: item\r\n }\r\n })\r\n}',
|
||||
adaptor: '',
|
||||
},
|
||||
title: '表单',
|
||||
initApi: '',
|
||||
body: [
|
||||
{
|
||||
type: 'checkboxes',
|
||||
label: '场地',
|
||||
mode: '',
|
||||
name: 'location_id',
|
||||
required: true,
|
||||
options: [],
|
||||
joinValues: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/locations?id=not.in.(${select_loc})&organization_id=eq.$centre_id&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => ({\r\n label: item.name,\r\n value: item.id\r\n }))\r\n}',
|
||||
requestAdaptor: '',
|
||||
},
|
||||
extractValue: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
icon: 'fa fa-plus text-primary',
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
tooltip: '关联场地',
|
||||
tooltipPlacement: 'top',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'each',
|
||||
name: 'machine_locations',
|
||||
items: [
|
||||
{
|
||||
type: 'container',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '${locations.name}',
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'ajax',
|
||||
confirmText: '确认取消关联"${locations.name}"?',
|
||||
api: {
|
||||
method: 'delete',
|
||||
url: 'rest/machine_locations?user_id=eq.$id&location_id=eq.${locations.id}',
|
||||
},
|
||||
size: 'xs',
|
||||
icon: 'fa fa-times text-danger',
|
||||
level: 'link',
|
||||
tooltip: '取消场地关联',
|
||||
tooltipPlacement: 'top',
|
||||
},
|
||||
],
|
||||
className: 'wrapper',
|
||||
},
|
||||
],
|
||||
placeholder: '暂无内容',
|
||||
className: '',
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/users/$id',
|
||||
data: {
|
||||
code: '$code',
|
||||
name: '$name',
|
||||
},
|
||||
dataType: 'json',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '账号',
|
||||
name: 'code',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
{
|
||||
label: '名称',
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
title: '修改考勤账号',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
icon: 'fa fa-edit text-info',
|
||||
level: 'link',
|
||||
tooltip: '修改考勤账号',
|
||||
tooltipPlacement: 'top',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '修改密码',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
rules: [
|
||||
{
|
||||
rule: 'this.new_password === this.password',
|
||||
message: '两次密码输入不一致',
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/change_password',
|
||||
data: { password: '$password', user_id: '$id' },
|
||||
},
|
||||
body: [
|
||||
{
|
||||
label: '新密码',
|
||||
type: 'input-password',
|
||||
name: 'new_password',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
{
|
||||
type: 'input-password',
|
||||
label: '确认密码',
|
||||
name: 'password',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'md',
|
||||
},
|
||||
icon: 'fa fa-cog text-warning',
|
||||
tooltip: '修改密码',
|
||||
tooltipPlacement: 'top',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
actionType: 'ajax',
|
||||
icon: 'fa fa-trash text-danger',
|
||||
confirmText: '确认删除"${name}(${code})"?',
|
||||
api: { method: 'delete', url: 'rest/users/$id' },
|
||||
level: 'link',
|
||||
tooltip: '删除考勤账号',
|
||||
tooltipPlacement: 'top',
|
||||
},
|
||||
],
|
||||
},
|
||||
footerToolbar: [],
|
||||
bulkActions: [],
|
||||
className: '',
|
||||
},
|
||||
],
|
||||
header: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '添加考勤账号',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '添加考勤账号',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/create_user',
|
||||
data: {
|
||||
role: 'machine',
|
||||
orgs_id: '$centre_id',
|
||||
code: '$code',
|
||||
name: '$name',
|
||||
password: '$password',
|
||||
},
|
||||
dataType: 'json',
|
||||
headers: { Prefer: 'params=single-object' },
|
||||
requestAdaptor: '',
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
rule: 'this.new_password === this.password',
|
||||
message: '两次密码输入不一致',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'code',
|
||||
label: '账号',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '名称',
|
||||
name: 'name',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
{
|
||||
label: '密码',
|
||||
type: 'input-password',
|
||||
name: 'new_password',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
{
|
||||
type: 'input-password',
|
||||
label: '确认密码',
|
||||
name: 'password',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
level: 'primary',
|
||||
align: 'left',
|
||||
className: 'm-t-xs m-l',
|
||||
},
|
||||
],
|
||||
headerClassName: 'panel-heading',
|
||||
},
|
||||
],
|
||||
type: 'page',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
316
src/pages/schema/centre/basic/admin.schema.ts
Normal file
316
src/pages/schema/centre/basic/admin.schema.ts
Normal file
@@ -0,0 +1,316 @@
|
||||
const schema = {
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
columns: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'code',
|
||||
label: '账号',
|
||||
placeholder: '-',
|
||||
id: 'u:c3eb8f9b177e',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
label: '姓名',
|
||||
placeholder: '-',
|
||||
id: 'u:6f74a11657ef',
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
icon: 'fa fa-pencil text-info',
|
||||
visibleOn:
|
||||
"this.user.role === 'dev' || this.user.role === 'admin'",
|
||||
tooltip: '修改',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
id: 'u:13770aa6289a',
|
||||
editorState: 'default',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '修改账号',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/users/$id',
|
||||
data: {
|
||||
code: '$code',
|
||||
name: '$name',
|
||||
},
|
||||
dataType: 'json',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
label: '账号',
|
||||
type: 'input-text',
|
||||
name: 'code',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:a614008003f6',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '姓名',
|
||||
name: 'name',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:5973b1eb26ab',
|
||||
},
|
||||
],
|
||||
id: 'u:c59fb46c3665',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: 'Submit',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:366fea2f5de5',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
debounce: { wait: 100 },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
confirmText: '确认删除账号"${name}(${code})"?',
|
||||
level: 'link',
|
||||
icon: 'fa fa-times text-danger',
|
||||
size: 'md',
|
||||
tooltip: '删除',
|
||||
tooltipPlacement: 'bottom',
|
||||
visibleOn:
|
||||
"this.user.role === 'dev' || this.user.role === 'admin'",
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
id: 'u:221693061e9e',
|
||||
editorState: 'default',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
outputVar: 'responseResult',
|
||||
actionType: 'ajax',
|
||||
options: {},
|
||||
api: {
|
||||
url: 'rest/users/$id',
|
||||
method: 'delete',
|
||||
requestAdaptor: '',
|
||||
adaptor: '',
|
||||
messages: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
componentId: 'u:366fea2f5de5',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
},
|
||||
],
|
||||
debounce: { wait: 100 },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: '-',
|
||||
width: 200,
|
||||
id: 'u:96b309d3bf00',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/users?select=*&role=eq.centreadmin&user_orgs.orgs.id=eq.$centre_id&order=code',
|
||||
data: { page: '$page', perPage: '$perPage' },
|
||||
adaptor:
|
||||
'payload.data.items.sort(\r\n (a, b) => {\r\n if (a.name === b.name) {\r\n return a.code.localeCompare(b.code)\r\n } else {\r\n return a.name.localeCompare(b.name)\r\n }\r\n }\r\n)\r\n\r\nreturn {\r\n ...payload\r\n}',
|
||||
},
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
icon: 'fa fa-plus text-primary',
|
||||
size: 'lg',
|
||||
level: 'link',
|
||||
tooltip: '添加',
|
||||
tooltipPlacement: 'left',
|
||||
align: 'right',
|
||||
label: '',
|
||||
visibleOn: "this.user.role === 'dev' || this.user.role === 'admin'",
|
||||
className: 'p-l-none',
|
||||
id: 'u:76187972a571',
|
||||
editorState: 'default',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '添加管理员',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/create_user',
|
||||
data: {
|
||||
code: '$code',
|
||||
name: '$name',
|
||||
password: '$password',
|
||||
new_password: '$new_password',
|
||||
role: 'centreadmin',
|
||||
orgs_id: '$centre_id',
|
||||
},
|
||||
dataType: 'json',
|
||||
headers: { Prefer: 'params=single-object' },
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '账号',
|
||||
name: 'code',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:6dca4e78622c',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
label: '姓名',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:56021033191e',
|
||||
},
|
||||
{
|
||||
type: 'input-password',
|
||||
name: 'password',
|
||||
label: '密码',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:1b5ed3926797',
|
||||
},
|
||||
{
|
||||
type: 'input-password',
|
||||
name: 'new_password',
|
||||
label: '确认密码',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:7c1e9ce050c0',
|
||||
},
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
rule: 'this.password === this.new_password',
|
||||
message: '两次密码输入不一致,请重新输入',
|
||||
},
|
||||
],
|
||||
id: 'u:6ecfef8353a7',
|
||||
actions: [
|
||||
{ type: 'submit', label: '提交', primary: true },
|
||||
],
|
||||
feat: 'Insert',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:366fea2f5de5',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:6179e3a79d93',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:e62e24a0fc22',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:f7524893881b',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
debounce: { wait: 100 },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
name: 'admin',
|
||||
footerToolbar: [
|
||||
{ type: 'pagination' },
|
||||
{ type: 'switch-per-page' },
|
||||
{ type: 'statistics' },
|
||||
],
|
||||
perPageField: 'perPage',
|
||||
bulkActions: [],
|
||||
itemActions: [],
|
||||
pageField: 'page',
|
||||
affixHeader: true,
|
||||
id: 'u:366fea2f5de5',
|
||||
},
|
||||
],
|
||||
type: 'page',
|
||||
messages: {},
|
||||
title: '',
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
id: 'u:4c19431ee5db',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
474
src/pages/schema/centre/basic/assistant.schema.ts
Normal file
474
src/pages/schema/centre/basic/assistant.schema.ts
Normal file
@@ -0,0 +1,474 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
columns: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'code',
|
||||
label: '账号',
|
||||
sortable: true,
|
||||
searchable: true,
|
||||
id: 'u:91821b736555',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
label: '姓名',
|
||||
sortable: true,
|
||||
searchable: true,
|
||||
id: 'u:a81612f6d91c',
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
icon: 'fa fa-pencil text-info',
|
||||
tooltip: '修改',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
label: '',
|
||||
api: { method: 'get', url: 'rest/users/$id' },
|
||||
id: 'u:483a2e18377b',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '修改账号',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/users/$id',
|
||||
data: { code: '$code', name: '$name' },
|
||||
dataType: 'json',
|
||||
},
|
||||
mode: 'normal',
|
||||
body: [
|
||||
{
|
||||
label: '账号',
|
||||
type: 'input-text',
|
||||
name: 'code',
|
||||
required: true,
|
||||
id: 'u:bcbd9ab0fd9e',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '姓名',
|
||||
name: 'name',
|
||||
required: true,
|
||||
id: 'u:3b7829fa16f8',
|
||||
},
|
||||
],
|
||||
id: 'u:7a2ac32b9e2b',
|
||||
actions: [
|
||||
{ type: 'submit', label: '提交', primary: true },
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:f1b328630134',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:49ea67f4ccca',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:e22fba76fe33',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:46273e3a4090',
|
||||
},
|
||||
],
|
||||
actionType: 'dialog',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
confirmText: '确认删除"${name}(${code})"?',
|
||||
level: 'link',
|
||||
icon: 'fa fa-times text-danger',
|
||||
size: 'md',
|
||||
tooltip: '删除',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
label: '',
|
||||
id: 'u:c3026515a2de',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
outputVar: 'responseResult',
|
||||
actionType: 'ajax',
|
||||
options: {},
|
||||
api: {
|
||||
url: 'rest/users/$id',
|
||||
method: 'delete',
|
||||
requestAdaptor: '',
|
||||
adaptor: '',
|
||||
messages: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
componentId: 'u:f1b328630134',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: { resetPage: false },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: '-',
|
||||
width: 200,
|
||||
id: 'u:e8398dbbd899',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/users?select=*,user_orgs(orgs(id))&code=${code}&name=${name}&role=eq.assistant&user_orgs.orgs.id=eq.$centre_id&order=code',
|
||||
data: {
|
||||
page: '${page}',
|
||||
perPage: '${perPage}',
|
||||
orderBy: '${orderBy}',
|
||||
orderDir: '${orderDir}',
|
||||
code: 'like.%${code}%',
|
||||
name: 'like.%${name}%',
|
||||
},
|
||||
requestAdaptor:
|
||||
"if (api.query.code === 'like.%%') {\r\n api.url = api.url.replace('&code=like.%25%25', '')\r\n}\r\n\r\nif (api.query.name === 'like.%%') {\r\n api.url = api.url.replace('&name=like.%25%25', '')\r\n}\r\n\r\nif (api.query.orderDir) {\r\n api.url = api.url.replace('&order=code%2Cname', '');\r\n api.url = api.url.replace('&orderBy=' + api.query.orderBy + '&orderDir=' + api.query.orderDir, '&order=' + api.query.orderBy + '.' + api.query.orderDir);\r\n} else if (api.query.orderBy) {\r\n api.url = api.url.replace('&order=code%2Cname', '');\r\n api.url = api.url.replace('&orderBy=' + api.query.orderBy + '&orderDir=', '&order=' + api.query.orderBy);\r\n} else {\r\n api.url = api.url.replace('&orderBy=&orderDir=', '');\r\n}\r\n\r\nreturn api;",
|
||||
},
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
icon: 'fa fa-plus',
|
||||
level: 'default',
|
||||
align: 'right',
|
||||
label: '添加',
|
||||
className: 'm-l-xs',
|
||||
id: 'u:ab332275c1fa',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '添加助教',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'code',
|
||||
label: '账号',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:487b8805e663',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
label: '姓名',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:89de05a91f91',
|
||||
},
|
||||
{
|
||||
type: 'input-password',
|
||||
name: 'password',
|
||||
label: '密码',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:0fb31870de06',
|
||||
},
|
||||
{
|
||||
type: 'input-password',
|
||||
name: 'new_password',
|
||||
label: '确认密码',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:c78103928eff',
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/create_user',
|
||||
data: {
|
||||
code: '$code',
|
||||
name: '$name',
|
||||
password: '$password',
|
||||
new_password: '$new_password',
|
||||
role: 'assistant',
|
||||
orgs_id: '$centre_id',
|
||||
},
|
||||
requestAdaptor: '',
|
||||
dataType: 'json',
|
||||
headers: { Prefer: 'params=single-object' },
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
rule: 'this.password === this.new_password',
|
||||
message: '两次密码输入不一致,请重新输入',
|
||||
},
|
||||
],
|
||||
id: 'u:b610d21f422f',
|
||||
actions: [
|
||||
{ type: 'submit', label: '提交', primary: true },
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:f1b328630134',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
closeOnOutside: false,
|
||||
id: 'u:2fbf0844aca1',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:deb6bbf65621',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:bbdffe4baf61',
|
||||
},
|
||||
],
|
||||
actionType: 'dialog',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
tooltip: '',
|
||||
tooltipPlacement: 'left',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
align: 'right',
|
||||
label: '导入',
|
||||
icon: 'fa fa-upload',
|
||||
level: 'warning',
|
||||
id: 'u:6581af7e35f0',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '助教导入',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
body: [
|
||||
{
|
||||
type: 'input-excel',
|
||||
name: 'assistants',
|
||||
label: '',
|
||||
mode: 'normal',
|
||||
id: 'u:5f0eca8a64fc',
|
||||
},
|
||||
{
|
||||
type: 'input-table',
|
||||
name: 'assistants',
|
||||
label: '',
|
||||
columns: [
|
||||
{
|
||||
label: '账号',
|
||||
name: '账号',
|
||||
placeholder: '-',
|
||||
quickEdit: {
|
||||
mode: 'inline',
|
||||
id: 'u:fcf8c77a0302',
|
||||
},
|
||||
id: 'u:328345552f87',
|
||||
},
|
||||
{
|
||||
label: '姓名',
|
||||
name: '姓名',
|
||||
placeholder: '-',
|
||||
quickEdit: {
|
||||
mode: 'inline',
|
||||
id: 'u:3801de195dcc',
|
||||
},
|
||||
id: 'u:e3d8eb8dae09',
|
||||
},
|
||||
],
|
||||
mode: 'normal',
|
||||
visibleOn: 'this.assistants',
|
||||
strictMode: true,
|
||||
removable: true,
|
||||
editable: false,
|
||||
id: 'u:d73c846629ed',
|
||||
clearValueOnHidden: true,
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/create_user',
|
||||
data: {
|
||||
orgs_id: '${centre_id}',
|
||||
assistants: '${assistants}',
|
||||
},
|
||||
requestAdaptor:
|
||||
"return {\r\n ...api,\r\n data: api.data.assistants.map(item => {\r\n return {\r\n name: item.姓名,\r\n code: item.账号,\r\n role: 'assistant',\r\n orgs_id: api.data.orgs_id\r\n }\r\n })\r\n}",
|
||||
dataType: 'json',
|
||||
headers: { Prefer: 'params=single-object' },
|
||||
},
|
||||
visibleOn: '',
|
||||
name: '',
|
||||
id: 'u:feda7d9a6c8e',
|
||||
actions: [
|
||||
{ type: 'submit', label: '提交', primary: true },
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:f1b328630134',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'xl',
|
||||
actionType: 'dialog',
|
||||
id: 'u:7cc3f8103868',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:908813bc5040',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:280fe6e082d4',
|
||||
},
|
||||
],
|
||||
closeOnOutside: false,
|
||||
showErrorMsg: true,
|
||||
showLoading: true,
|
||||
draggable: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'export-excel',
|
||||
align: 'right',
|
||||
label: '导出',
|
||||
icon: 'fa fa-download',
|
||||
level: 'primary',
|
||||
api: 'rest/users?select=*,user_orgs(orgs(id))&role=eq.assistant&user_orgs.orgs.id=eq.$centre_id&order=code',
|
||||
filename: '助教管理',
|
||||
exportColumns: [
|
||||
{ label: '账号', name: 'code' },
|
||||
{ name: 'name', label: '姓名' },
|
||||
],
|
||||
id: 'u:556c3974c544',
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
footerToolbar: [
|
||||
{ type: 'pagination' },
|
||||
{ type: 'switch-per-page' },
|
||||
{ type: 'statistics' },
|
||||
],
|
||||
perPageField: 'perPage',
|
||||
pageField: 'page',
|
||||
affixHeader: true,
|
||||
title: '',
|
||||
initApi: '',
|
||||
bodyClassName: '',
|
||||
id: 'u:f1b328630134',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
title: '',
|
||||
id: 'u:31753e0403ea',
|
||||
asideResizor: false,
|
||||
pullRefresh: { disabled: true },
|
||||
definitions: {},
|
||||
};
|
||||
|
||||
export { schema };
|
||||
493
src/pages/schema/centre/basic/location.schema.ts
Normal file
493
src/pages/schema/centre/basic/location.schema.ts
Normal file
@@ -0,0 +1,493 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
columns: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'code',
|
||||
label: '编号',
|
||||
sortable: true,
|
||||
searchable: true,
|
||||
id: 'u:f01745729d6f',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
label: '名称',
|
||||
sortable: true,
|
||||
searchable: true,
|
||||
id: 'u:0db1a83903b6',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '类型',
|
||||
id: 'u:00332fcfb6b6',
|
||||
name: 'dicts.dictvalue',
|
||||
searchable: true,
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
dialog: { $ref: 'dialog-ref-2' },
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
icon: 'fa fa-pencil text-info',
|
||||
tooltip: '修改',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
id: 'u:2fbcae5dec1e',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: { $ref: 'dialog-ref-2' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
confirmText: '确认删除"${name}(${code})"?',
|
||||
level: 'link',
|
||||
icon: 'fa fa-times text-danger',
|
||||
size: 'md',
|
||||
tooltip: '删除',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
id: 'u:259ec9a26f44',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
outputVar: 'responseResult',
|
||||
actionType: 'ajax',
|
||||
options: {},
|
||||
api: {
|
||||
url: 'rest/locations/$id',
|
||||
method: 'delete',
|
||||
requestAdaptor: '',
|
||||
adaptor: '',
|
||||
messages: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
componentId: 'u:96cb5c2c87a4',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: { resetPage: false },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: '-',
|
||||
width: 200,
|
||||
id: 'u:328bf6132313',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/locations?select=id,code,name,type,dicts!inner(dictvalue)&code=${code}&name=${name}&dicts.dictvalue=${dicts.dictvalue}&order=code',
|
||||
data: {
|
||||
page: '${page}',
|
||||
perPage: '${perPage}',
|
||||
orderBy: '${orderBy}',
|
||||
orderDir: '${orderDir}',
|
||||
code: 'like.%${code}%',
|
||||
name: 'like.%${name}%',
|
||||
'dicts.dictvalue': 'like.%${dicts.dictvalue}%',
|
||||
},
|
||||
requestAdaptor:
|
||||
"if (api.query.code === 'like.%%') {\r\n api.url = api.url.replace('&code=like.%25%25', '')\r\n}\r\n\r\nif (api.query.name === 'like.%%') {\r\n api.url = api.url.replace('&name=like.%25%25', '')\r\n}\r\n\r\nif (api.query.dicts.dictvalue === 'like.%%') {\r\n api.url = api.url.replace('&dicts[dictvalue]=like.%25%25', '')\r\n}\r\n\r\nif (api.query.orderDir) {\r\n api.url = api.url.replace('&order=code%2Cname', '');\r\n api.url = api.url.replace('&orderBy=' + api.query.orderBy + '&orderDir=' + api.query.orderDir, '&order=' + api.query.orderBy + '.' + api.query.orderDir);\r\n} else if (api.query.orderBy) {\r\n api.url = api.url.replace('&order=code%2Cname', '');\r\n api.url = api.url.replace('&orderBy=' + api.query.orderBy + '&orderDir=', '&order=' + api.query.orderBy);\r\n} else {\r\n api.url = api.url.replace('&orderBy=&orderDir=', '');\r\n}\r\n\r\nreturn api;",
|
||||
},
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
icon: 'fa fa-plus',
|
||||
level: 'default',
|
||||
align: 'right',
|
||||
label: '添加',
|
||||
className: 'm-l-xs',
|
||||
id: 'u:64fb6bec0fff',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: { $ref: 'dialog-ref-1' },
|
||||
args: { fromCurrentDialog: true },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
actionType: 'dialog',
|
||||
dialog: { $ref: 'dialog-ref-1' },
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
align: 'right',
|
||||
label: '导入',
|
||||
icon: 'fa fa-upload',
|
||||
level: 'warning',
|
||||
id: 'u:a2c775bee2a9',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '场地导入',
|
||||
size: 'lg',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
id: 'u:2d97745994e5',
|
||||
title: '表单',
|
||||
body: [
|
||||
{
|
||||
type: 'input-excel',
|
||||
name: 'locations',
|
||||
label: '',
|
||||
mode: 'normal',
|
||||
id: 'u:5f0eca8a64fc',
|
||||
},
|
||||
{
|
||||
type: 'input-table',
|
||||
name: 'locations',
|
||||
label: '',
|
||||
columns: [
|
||||
{
|
||||
label: '编号',
|
||||
name: '编号',
|
||||
placeholder: '-',
|
||||
quickEdit: {
|
||||
mode: 'inline',
|
||||
id: 'u:931a41e1011d',
|
||||
},
|
||||
id: 'u:0c6672aa1858',
|
||||
},
|
||||
{
|
||||
label: '名称',
|
||||
name: '名称',
|
||||
placeholder: '-',
|
||||
quickEdit: {
|
||||
mode: 'inline',
|
||||
id: 'u:6ebc04992e03',
|
||||
},
|
||||
id: 'u:a03852887668',
|
||||
},
|
||||
{
|
||||
label: '类型',
|
||||
name: '类型',
|
||||
placeholder: '-',
|
||||
quickEdit: {
|
||||
mode: 'inline',
|
||||
id: 'u:a75f5e419328',
|
||||
},
|
||||
id: 'u:efedd284d265',
|
||||
},
|
||||
],
|
||||
mode: 'normal',
|
||||
visibleOn: 'this.locations',
|
||||
strictMode: true,
|
||||
removable: true,
|
||||
editable: false,
|
||||
id: 'u:d73c846629ed',
|
||||
clearValueOnHidden: true,
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/locations',
|
||||
data: {
|
||||
orgs_id: '${centre_id}',
|
||||
locations: '${locations}',
|
||||
},
|
||||
requestAdaptor:
|
||||
"return {\r\n ...api,\r\n data: api.data.locations.map(item => {\r\n let type;\r\n switch (item.类型)\r\n {\r\n case '实验室':\r\n type = 'laboratory';\r\n break;\r\n case '教室':\r\n type = 'classroom';\r\n break;\r\n case '办公室':\r\n type = 'office';\r\n break;\r\n case '仓库':\r\n typ = 'warehouse';\r\n break;\r\n }\r\n\r\n return {\r\n name: item.名称,\r\n code: item.编号,\r\n type: type,\r\n organization_id: api.data.orgs_id\r\n }\r\n })\r\n}",
|
||||
dataType: 'json',
|
||||
},
|
||||
visibleOn: '',
|
||||
name: '',
|
||||
actions: [
|
||||
{ type: 'submit', label: '提交', primary: true },
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
labelAlign: 'left',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:96cb5c2c87a4',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
actionType: 'dialog',
|
||||
id: 'u:438767684afb',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:c664b67b3e6b',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:f8fa25c49d82',
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'export-excel',
|
||||
align: 'right',
|
||||
label: '导出',
|
||||
icon: 'fa fa-download',
|
||||
level: 'primary',
|
||||
api: 'rest/locations?select=id,code,name,type,dicts(dictvalue)&order=code',
|
||||
filename: '实验场地',
|
||||
exportColumns: [
|
||||
{ label: '编号', name: 'code' },
|
||||
{ name: 'name', label: '名称' },
|
||||
{ name: 'dicts.dictvalue', label: '类型' },
|
||||
],
|
||||
id: 'u:ab01cbc28399',
|
||||
perPageAvailable: [10],
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
footerToolbar: [
|
||||
{ type: 'pagination' },
|
||||
{ type: 'switch-per-page' },
|
||||
{ type: 'statistics' },
|
||||
],
|
||||
perPageField: 'perPage',
|
||||
pageField: 'page',
|
||||
title: '',
|
||||
initApi: '',
|
||||
bodyClassName: '',
|
||||
id: 'u:96cb5c2c87a4',
|
||||
initFetch: '',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
title: '',
|
||||
id: 'u:4ffd31a5304f',
|
||||
definitions: {
|
||||
'dialog-ref-1': {
|
||||
type: 'dialog',
|
||||
title: '添加实验场地',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'code',
|
||||
label: '编号',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:9736564ec7fc',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
label: '名称',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:dcb5e881be78',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'type',
|
||||
label: '类型',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
checkAll: false,
|
||||
source: {
|
||||
url: 'rest/dicts?typecode=eq.006',
|
||||
method: 'get',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map((item) => {\r\n return {\r\n label: item.dictvalue,\r\n value: item.dictkey\r\n };\r\n })\r\n}',
|
||||
},
|
||||
value: 'laboratory',
|
||||
options: [{ label: '实验室', value: 'laboratory' }],
|
||||
id: 'u:af3f658e33cf',
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/locations',
|
||||
data: {
|
||||
code: '$code',
|
||||
name: '$name',
|
||||
type: '$type',
|
||||
organization_id: '${centre_id}',
|
||||
},
|
||||
dataType: 'json',
|
||||
},
|
||||
id: 'u:510d0236a371',
|
||||
actions: [{ type: 'submit', label: '提交', primary: true }],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:96cb5c2c87a4',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:9cae782cb189',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:5217a0785f6c',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:1e10c45aa1a2',
|
||||
},
|
||||
],
|
||||
$$ref: 'dialog-ref-1',
|
||||
},
|
||||
'dialog-ref-2': {
|
||||
type: 'dialog',
|
||||
title: '修改实验场地',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/locations?id=eq.$id',
|
||||
data: { code: '$code', name: '$name', type: '$type' },
|
||||
dataType: 'json',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
label: '编号',
|
||||
type: 'input-text',
|
||||
name: 'code',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:339d0f396a34',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '名称',
|
||||
name: 'name',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:3f1f8e6f2f13',
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
type: 'select',
|
||||
label: '类型',
|
||||
source: {
|
||||
url: 'rest/dicts?typecode=eq.006',
|
||||
method: 'get',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map((item) => {\r\n return {\r\n label: item.dictvalue,\r\n value: item.dictkey,\r\n };\r\n }),\r\n};',
|
||||
},
|
||||
required: true,
|
||||
checkAll: false,
|
||||
mode: 'normal',
|
||||
id: 'u:fda206c44f88',
|
||||
},
|
||||
],
|
||||
id: 'u:3ab6c9e29cf6',
|
||||
actions: [{ type: 'submit', label: '提交', primary: true }],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:96cb5c2c87a4',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:786fa30ccac8',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:d81e2b6865c1',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:e76ad8a24b48',
|
||||
},
|
||||
],
|
||||
$$ref: 'dialog-ref-2',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export { schema };
|
||||
1164
src/pages/schema/centre/basic/settings.schema.ts
Normal file
1164
src/pages/schema/centre/basic/settings.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
485
src/pages/schema/centre/basic/teacher.schema.ts
Normal file
485
src/pages/schema/centre/basic/teacher.schema.ts
Normal file
@@ -0,0 +1,485 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
columns: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'code',
|
||||
label: '账号',
|
||||
sortable: true,
|
||||
searchable: true,
|
||||
id: 'u:815ab28420f2',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
label: '姓名',
|
||||
sortable: true,
|
||||
searchable: true,
|
||||
id: 'u:14ae63b2e9b2',
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
label: '操作',
|
||||
id: 'u:70fc12c2dc93',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
icon: 'fa fa-pencil text-info',
|
||||
tooltip: '修改',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
label: '',
|
||||
api: { method: 'get', url: 'rest/users/$id' },
|
||||
id: 'u:0e1e6868a15e',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '修改账号',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/users/$id',
|
||||
data: { code: '$code', name: '$name' },
|
||||
dataType: 'json',
|
||||
},
|
||||
mode: 'normal',
|
||||
id: 'u:752cc90e2438',
|
||||
body: [
|
||||
{
|
||||
label: '账号',
|
||||
type: 'input-text',
|
||||
name: 'code',
|
||||
required: true,
|
||||
id: 'u:dab5b6824746',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '姓名',
|
||||
name: 'name',
|
||||
required: true,
|
||||
id: 'u:95086c61998e',
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{ type: 'submit', label: '提交', primary: true },
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:de2533791a72',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:41bebd8f0aba',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:7aabbd45b9b9',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:d1ba4fdace4d',
|
||||
},
|
||||
],
|
||||
actionType: 'dialog',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
confirmText: '确认删除"${name}(${code})"?',
|
||||
level: 'link',
|
||||
icon: 'fa fa-times text-danger',
|
||||
size: 'md',
|
||||
tooltip: '删除',
|
||||
tooltipPlacement: 'bottom',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
label: '',
|
||||
id: 'u:b9864828c5ea',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
outputVar: 'responseResult',
|
||||
actionType: 'ajax',
|
||||
options: {},
|
||||
api: {
|
||||
url: 'rest/users/$id',
|
||||
method: 'delete',
|
||||
requestAdaptor: '',
|
||||
adaptor: '',
|
||||
messages: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
componentId: 'u:de2533791a72',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: { resetPage: false },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
actionType: 'ajax',
|
||||
size: 'md',
|
||||
level: 'link',
|
||||
icon: 'fa fa-cog text-warning',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/reset_password',
|
||||
data: { user_id: '$id' },
|
||||
},
|
||||
confirmText: '确认重置"${name}"的密码?',
|
||||
visibleOn: '',
|
||||
tooltip: '重置密码',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
id: 'u:b7b4f65ce939',
|
||||
},
|
||||
],
|
||||
placeholder: '-',
|
||||
width: 200,
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/users?select=*,user_orgs(orgs(id))&code=${code}&name=${name}&role=eq.teacher&user_orgs.orgs.id=eq.$centre_id&order=code',
|
||||
data: {
|
||||
page: '${page}',
|
||||
perPage: '${perPage}',
|
||||
orderBy: '${orderBy}',
|
||||
orderDir: '${orderDir}',
|
||||
code: 'like.%${code}%',
|
||||
name: 'like.%${name}%',
|
||||
},
|
||||
requestAdaptor:
|
||||
"if (api.query.code === 'like.%%') {\r\n api.url = api.url.replace('&code=like.%25%25', '')\r\n}\r\n\r\nif (api.query.name === 'like.%%') {\r\n api.url = api.url.replace('&name=like.%25%25', '')\r\n}\r\n\r\nif (api.query.orderDir) {\r\n api.url = api.url.replace('&order=code%2Cname', '');\r\n api.url = api.url.replace('&orderBy=' + api.query.orderBy + '&orderDir=' + api.query.orderDir, '&order=' + api.query.orderBy + '.' + api.query.orderDir);\r\n} else if (api.query.orderBy) {\r\n api.url = api.url.replace('&order=code%2Cname', '');\r\n api.url = api.url.replace('&orderBy=' + api.query.orderBy + '&orderDir=', '&order=' + api.query.orderBy);\r\n} else {\r\n api.url = api.url.replace('&orderBy=&orderDir=', '');\r\n}\r\n\r\nreturn api;",
|
||||
},
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
icon: 'fa fa-plus',
|
||||
level: 'default',
|
||||
align: 'right',
|
||||
label: '添加',
|
||||
className: 'm-l-xs',
|
||||
id: 'u:f4a47bb1a6d3',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '添加任课教师',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'code',
|
||||
label: '账号',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:bda0263ab8b8',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
label: '姓名',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:36976961498d',
|
||||
},
|
||||
{
|
||||
type: 'input-password',
|
||||
name: 'password',
|
||||
label: '密码',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:ec15585cc409',
|
||||
},
|
||||
{
|
||||
type: 'input-password',
|
||||
name: 'new_password',
|
||||
label: '确认密码',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:e364b38586a8',
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/create_user',
|
||||
data: {
|
||||
code: '$code',
|
||||
name: '$name',
|
||||
password: '$password',
|
||||
new_password: '$new_password',
|
||||
role: 'teacher',
|
||||
orgs_id: '$centre_id',
|
||||
},
|
||||
requestAdaptor: '',
|
||||
dataType: 'json',
|
||||
headers: { Prefer: 'params=single-object' },
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
rule: 'this.password === this.new_password',
|
||||
message: '两次密码输入不一致,请重新输入',
|
||||
},
|
||||
],
|
||||
id: 'u:9b27303ee8c7',
|
||||
actions: [
|
||||
{ type: 'submit', label: '提交', primary: true },
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:de2533791a72',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:feafc93d557c',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:836089a67684',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:51adce35589e',
|
||||
},
|
||||
],
|
||||
actionType: 'dialog',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
align: 'right',
|
||||
label: '导入',
|
||||
icon: 'fa fa-upload',
|
||||
level: 'warning',
|
||||
id: 'u:25df91a187d4',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '教师导入',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
body: [
|
||||
{
|
||||
type: 'input-excel',
|
||||
name: 'teachers',
|
||||
label: '',
|
||||
mode: 'normal',
|
||||
id: 'u:5f0eca8a64fc',
|
||||
},
|
||||
{
|
||||
type: 'input-table',
|
||||
name: 'teachers',
|
||||
label: '',
|
||||
columns: [
|
||||
{
|
||||
label: '账号',
|
||||
name: '账号',
|
||||
placeholder: '-',
|
||||
quickEdit: {
|
||||
mode: 'inline',
|
||||
id: 'u:e92d65ada484',
|
||||
},
|
||||
id: 'u:67eb5050eb4d',
|
||||
},
|
||||
{
|
||||
label: '姓名',
|
||||
name: '姓名',
|
||||
placeholder: '-',
|
||||
quickEdit: {
|
||||
mode: 'inline',
|
||||
id: 'u:2d4bdf8c7035',
|
||||
},
|
||||
id: 'u:74137b900acf',
|
||||
},
|
||||
],
|
||||
mode: 'normal',
|
||||
visibleOn: 'this.teachers',
|
||||
strictMode: true,
|
||||
removable: true,
|
||||
editable: false,
|
||||
id: 'u:d73c846629ed',
|
||||
clearValueOnHidden: true,
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/create_user',
|
||||
data: {
|
||||
orgs_id: '${centre_id}',
|
||||
teachers: '${teachers}',
|
||||
},
|
||||
requestAdaptor:
|
||||
"return {\r\n ...api,\r\n data: api.data.teachers.map(item => {\r\n return {\r\n name: item.姓名,\r\n code: item.账号,\r\n role: 'teacher',\r\n orgs_id: api.data.orgs_id\r\n }\r\n })\r\n}",
|
||||
dataType: 'json',
|
||||
headers: { Prefer: 'params=single-object' },
|
||||
},
|
||||
id: 'u:e60b2ab481ec',
|
||||
actions: [
|
||||
{ type: 'submit', label: '提交', primary: true },
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
labelAlign: 'left',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:de2533791a72',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
actionType: 'dialog',
|
||||
id: 'u:249cb4918177',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:2d7cd1af378c',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:2dca052d9127',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'export-excel',
|
||||
align: 'right',
|
||||
label: '导出',
|
||||
icon: 'fa fa-download',
|
||||
level: 'primary',
|
||||
api: 'rest/users?select=*,user_orgs(orgs(id))&role=eq.teacher&user_orgs.orgs.id=eq.$centre_id&order=code',
|
||||
filename: '任课教师',
|
||||
exportColumns: [
|
||||
{ label: '账号', name: 'code' },
|
||||
{ name: 'name', label: '姓名' },
|
||||
],
|
||||
id: 'u:e2ad66d916a5',
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
footerToolbar: [
|
||||
{ type: 'pagination' },
|
||||
{ type: 'switch-per-page' },
|
||||
{ type: 'statistics' },
|
||||
],
|
||||
perPageField: 'perPage',
|
||||
pageField: 'page',
|
||||
title: '',
|
||||
initApi: '',
|
||||
bodyClassName: '',
|
||||
id: 'u:de2533791a72',
|
||||
affixHeader: true,
|
||||
placeholder: '-',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
title: '',
|
||||
id: 'u:1d83fac554fc',
|
||||
definitions: {},
|
||||
};
|
||||
|
||||
export { schema };
|
||||
485
src/pages/schema/centre/basic/team.schema.ts
Normal file
485
src/pages/schema/centre/basic/team.schema.ts
Normal file
@@ -0,0 +1,485 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/teams?select=*,team_members(users(id,code,name))&organization_id=eq.$centre_id&order=code',
|
||||
data: {
|
||||
page: '$page',
|
||||
perPage: '$perPage',
|
||||
},
|
||||
adaptor:
|
||||
'return {\r\n ...payload.data,\r\n items: payload.data.items.map(item=> {\r\n return {\r\n ...item,\r\n selected_members: item.team_members.map(x => x.users.id)\r\n }\r\n })\r\n}\r\n',
|
||||
},
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
icon: 'fa fa-plus',
|
||||
level: 'primary',
|
||||
align: 'right',
|
||||
label: '添加',
|
||||
className: 'm-l-xs',
|
||||
id: 'u:3c4c79023e6d',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '添加教学团队',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'code',
|
||||
label: '编号',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:9c6265d6605a',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
label: '名称',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:fb25ddbf9ea3',
|
||||
},
|
||||
{
|
||||
type: 'input-rich-text',
|
||||
name: 'intro',
|
||||
label: '描述',
|
||||
mode: 'normal',
|
||||
required: false,
|
||||
options: {
|
||||
menubar: false,
|
||||
},
|
||||
id: 'u:c88442289c84',
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/teams',
|
||||
data: {
|
||||
organization_id: '$centre_id',
|
||||
code: '$code',
|
||||
name: '$name',
|
||||
intro: '$intro',
|
||||
},
|
||||
dataType: 'json',
|
||||
},
|
||||
id: 'u:5e5757012047',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:6b104aa599d9',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:f020a946e699',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:d93f54d6db83',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:cc9454abf750',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
perPageAvailable: [9, 27, 54],
|
||||
footerToolbar: [
|
||||
{
|
||||
type: 'pagination',
|
||||
},
|
||||
{
|
||||
type: 'switch-per-page',
|
||||
tpl: '内容',
|
||||
},
|
||||
],
|
||||
perPageField: 'perPage',
|
||||
pageField: 'page',
|
||||
bulkActions: [],
|
||||
itemActions: [],
|
||||
mode: 'cards',
|
||||
card: {
|
||||
type: 'card',
|
||||
header: {
|
||||
title: '$name',
|
||||
subTitle: '$code',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '${intro|raw}',
|
||||
id: 'u:e89fc74f9f0a',
|
||||
},
|
||||
{
|
||||
type: 'each',
|
||||
name: 'team_members',
|
||||
placeholder: '未添加团队成员',
|
||||
items: [
|
||||
{
|
||||
type: 'plain',
|
||||
tpl: '${users.name}',
|
||||
inline: true,
|
||||
className: 'm-l-sm',
|
||||
id: 'u:53d23e452416',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
level: 'link',
|
||||
icon: 'fa fa-times text-danger',
|
||||
iconClassName: 'text-danger',
|
||||
confirmText: '确认删除"${users.name}"?',
|
||||
tooltip: '删除成员',
|
||||
tooltipPlacement: 'top',
|
||||
id: 'u:13cfdb0af771',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
outputVar: 'responseResult',
|
||||
actionType: 'ajax',
|
||||
options: {},
|
||||
api: {
|
||||
url: 'rest/team_members?team_id=eq.${id}&teacher_id=eq.${users.id}',
|
||||
method: 'delete',
|
||||
requestAdaptor: '',
|
||||
adaptor: '',
|
||||
messages: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
componentId: 'u:6b104aa599d9',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
id: 'u:c6b9bef8b674',
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
label: '',
|
||||
type: 'button',
|
||||
icon: 'fa fa-edit text-info',
|
||||
tooltip: '修改教学团队',
|
||||
tooltipPlacement: 'top',
|
||||
size: 'md',
|
||||
id: 'u:15a30654e625',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/teams/${id}',
|
||||
data: {
|
||||
code: '$code',
|
||||
name: '$name',
|
||||
intro: '$intro',
|
||||
teacher_list: '{${teacher_list}}',
|
||||
},
|
||||
dataType: 'json',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '编号',
|
||||
name: 'code',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:21b2f1785596',
|
||||
},
|
||||
{
|
||||
label: '名称',
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:765ddf946178',
|
||||
},
|
||||
{
|
||||
label: '描述',
|
||||
type: 'input-rich-text',
|
||||
name: 'intro',
|
||||
options: {
|
||||
menubar: false,
|
||||
},
|
||||
mode: 'normal',
|
||||
id: 'u:454ab08215fc',
|
||||
},
|
||||
],
|
||||
id: 'u:51ad45e350ee',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:6b104aa599d9',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
data: {},
|
||||
dataMergeMode: 'override',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
title: '修改教学团队',
|
||||
id: 'u:071e18a84182',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:07f56791977b',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:5e08c0b4a2c2',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
icon: 'fa fa-trash text-danger',
|
||||
confirmText: '确认删除"${name}(${code})"?',
|
||||
iconClassName: 'text-danger',
|
||||
tooltip: '删除教学团队',
|
||||
tooltipPlacement: 'top',
|
||||
size: 'md',
|
||||
id: 'u:481d5762073e',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
outputVar: 'responseResult',
|
||||
actionType: 'ajax',
|
||||
options: {},
|
||||
api: {
|
||||
url: 'rest/teams?id=eq.${id}',
|
||||
method: 'delete',
|
||||
requestAdaptor: '',
|
||||
adaptor: '',
|
||||
messages: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
componentId: 'u:6b104aa599d9',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
icon: 'fa fa-group text-success',
|
||||
size: 'md',
|
||||
tooltip: '添加成员',
|
||||
tooltipPlacement: 'top',
|
||||
id: 'u:8ab4c946e195',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '添加成员',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/team_members',
|
||||
data: {
|
||||
select: '$select',
|
||||
team_id: '${id}',
|
||||
},
|
||||
dataType: 'json',
|
||||
requestAdaptor:
|
||||
"return {\r\n ...api,\r\n data: api.data.select.split(',').map(item => {\r\n return {\r\n team_id: api.data.team_id,\r\n teacher_id: item\r\n }\r\n })\r\n}",
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '',
|
||||
name: 'select',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
checkAll: false,
|
||||
defaultCheckAll: false,
|
||||
checkAllLabel: '全选',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/users?select=*,user_orgs(organization_id)&role=eq.teacher&user_orgs.organization_id=eq.${centre_id}&id=not.in.(${selected_members})&order=code',
|
||||
adaptor:
|
||||
'return {\r\n data: payload.data.items.map(x=> {\r\n return {\r\n label: x.name,\r\n value: x.id\r\n }\r\n })\r\n}',
|
||||
},
|
||||
multiple: true,
|
||||
joinValues: true,
|
||||
id: 'u:d8d80248a021',
|
||||
},
|
||||
],
|
||||
id: 'u:518d0cab0bb2',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:6b104aa599d9',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:ca9566592590',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:537639203d7b',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:bdee93cc8455',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
id: 'u:c2cda94e1dbb',
|
||||
},
|
||||
placeholder: '暂无数据',
|
||||
columnsCount: 3,
|
||||
defaultParams: {
|
||||
perPage: 9,
|
||||
},
|
||||
affixHeader: true,
|
||||
id: 'u:6b104aa599d9',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
title: '',
|
||||
id: 'u:418dc967756c',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
592
src/pages/schema/centre/chart/evaluation.schema.ts
Normal file
592
src/pages/schema/centre/chart/evaluation.schema.ts
Normal file
@@ -0,0 +1,592 @@
|
||||
const schema = {
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
wrapWithPanel: false,
|
||||
submitOnChange: true,
|
||||
reload: '',
|
||||
messages: {},
|
||||
target:
|
||||
'teacher2?semesterSelect=${semesterSelect}&course_id=${course_id}&parent_id=${parent_id},teacher1?semesterSelect=${semesterSelect}&course_id=${course_id}',
|
||||
body: [
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '学期',
|
||||
name: 'semesterSelect',
|
||||
mode: 'inline',
|
||||
size: 'lg',
|
||||
clearable: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/semesters',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}(${item.code})`,\r\n value: item.id\r\n }\r\n })\r\n}',
|
||||
},
|
||||
checkAll: false,
|
||||
searchable: true,
|
||||
selectFirst: false,
|
||||
inputClassName: 'm-l-xs',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '课程',
|
||||
name: 'course_id',
|
||||
mode: 'inline',
|
||||
size: 'md',
|
||||
clearable: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/courses?organization_id=eq.${centre_id}&semester_id=eq.${semesterSelect}&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.length > 0 && payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}(${item.code})`,\r\n value: item.id\r\n }\r\n })\r\n}',
|
||||
sendOn: 'this.semesterSelect',
|
||||
},
|
||||
checkAll: false,
|
||||
searchable: true,
|
||||
selectFirst: false,
|
||||
visibleOn: '',
|
||||
clearValueOnHidden: false,
|
||||
inputClassName: 'm-l-xs',
|
||||
className: 'm-l-sm',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '一级指标',
|
||||
name: 'parent_id',
|
||||
mode: 'inline',
|
||||
size: 'md',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/teaching_evaluation_items?organization_id=eq.${centre_id}&semester_id=eq.${semesterSelect}&parent=is.null&order=order',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id\r\n }\r\n })\r\n }\r\n}',
|
||||
sendOn: 'this.course_id',
|
||||
},
|
||||
checkAll: false,
|
||||
searchable: true,
|
||||
selectFirst: false,
|
||||
visibleOn: 'this.course_id',
|
||||
clearValueOnHidden: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'grid',
|
||||
columns: [
|
||||
{
|
||||
body: [
|
||||
{
|
||||
type: 'flex',
|
||||
items: [
|
||||
{
|
||||
type: 'wrapper',
|
||||
body: [
|
||||
{
|
||||
type: 'chart',
|
||||
config: {
|
||||
title: [{ text: '教师统计(一级指标)' }],
|
||||
series: [
|
||||
{
|
||||
data: '${series_data}',
|
||||
type: 'radar',
|
||||
emphasis: { lineStyle: { width: 4 } },
|
||||
tooltip: { trigger: 'item' },
|
||||
},
|
||||
],
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
orient: 'vertical',
|
||||
right: 10,
|
||||
top: 20,
|
||||
bottom: 20,
|
||||
data: '${legend}',
|
||||
},
|
||||
radar: [
|
||||
{
|
||||
indicator: '${items}',
|
||||
radius: 200,
|
||||
startAngle: 90,
|
||||
splitNumber: 5,
|
||||
name: {
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
backgroundColor: '#666',
|
||||
borderRadius: 3,
|
||||
padding: [3, 5],
|
||||
},
|
||||
},
|
||||
center: ['35%', '55%'],
|
||||
},
|
||||
],
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: {
|
||||
show: true,
|
||||
title: '数据视图',
|
||||
lang: ['数据视图', '关闭', '刷新'],
|
||||
},
|
||||
saveAsImage: {
|
||||
show: true,
|
||||
title: '保存为图片',
|
||||
type: 'png',
|
||||
},
|
||||
},
|
||||
left: 170,
|
||||
top: -2,
|
||||
},
|
||||
},
|
||||
_mode: '2',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/evaluation_stats_teacher',
|
||||
adaptor:
|
||||
'let legend_set = new Set()\r\n\r\npayload.data.rows && payload.data.rows.forEach(item => {\r\n if(item.teacher_id) {\r\n legend_set.add(item.teacher_name.concat("(", item.teacher_id, ")"));\r\n } else {\r\n legend_set.add(\'未指定\');\r\n }\r\n})\r\n\r\nlet legend = Array.from(legend_set)\r\n\r\nlet series_data = legend\r\n ? legend.map(legend => {\r\n return {\r\n value: payload.data.rows.filter(\r\n item => item.teacher_id ? item.teacher_name.concat("(", item.teacher_id, ")") === legend : legend === \'未指定\'\r\n ).map(item => item.score),\r\n name: legend,\r\n symbol: "rect",\r\n symbolSize: 12,\r\n lineStyle: {\r\n type: "dashed"\r\n }\r\n }\r\n })\r\n : []\r\n\r\nreturn {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items\r\n ? payload.data.items.sort(\r\n (a, b) => a.id - b.id\r\n ).map(item => {\r\n return {\r\n text: item.name,\r\n max: 5\r\n }\r\n })\r\n : [],\r\n legend: legend,\r\n series_data: series_data\r\n }\r\n}\r\n',
|
||||
sendOn: 'this.course_id',
|
||||
data: {
|
||||
course_id: '${course_id}',
|
||||
organization_id: '${centre_id}',
|
||||
},
|
||||
requestAdaptor: '',
|
||||
},
|
||||
dataFilter: '',
|
||||
height: 480,
|
||||
name: 'teacher1',
|
||||
},
|
||||
],
|
||||
size: 'xs',
|
||||
className: 'bg-white',
|
||||
},
|
||||
],
|
||||
direction: 'column',
|
||||
justify: 'start',
|
||||
alignItems: 'stretch',
|
||||
className: '',
|
||||
__mode: true,
|
||||
style: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
body: [
|
||||
{
|
||||
type: 'flex',
|
||||
items: [
|
||||
{
|
||||
type: 'wrapper',
|
||||
body: [
|
||||
{
|
||||
type: 'chart',
|
||||
config: {
|
||||
title: [{ text: '教师统计(二级指标)' }],
|
||||
series: [
|
||||
{
|
||||
data: '${series_data}',
|
||||
type: 'radar',
|
||||
emphasis: { lineStyle: { width: 4 } },
|
||||
tooltip: { trigger: 'item' },
|
||||
},
|
||||
],
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
orient: 'vertical',
|
||||
right: 10,
|
||||
top: 20,
|
||||
bottom: 20,
|
||||
data: '${legend}',
|
||||
},
|
||||
radar: [
|
||||
{
|
||||
indicator: '${items}',
|
||||
radius: 200,
|
||||
startAngle: 90,
|
||||
splitNumber: 5,
|
||||
name: {
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
backgroundColor: '#666',
|
||||
borderRadius: 3,
|
||||
padding: [3, 5],
|
||||
},
|
||||
},
|
||||
center: ['35%', '55%'],
|
||||
},
|
||||
],
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: {
|
||||
show: true,
|
||||
title: '数据视图',
|
||||
lang: ['数据视图', '关闭', '刷新'],
|
||||
},
|
||||
saveAsImage: {
|
||||
show: true,
|
||||
title: '保存为图片',
|
||||
type: 'png',
|
||||
},
|
||||
},
|
||||
left: 170,
|
||||
top: -2,
|
||||
},
|
||||
},
|
||||
_mode: '2',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/evaluation_stats_teacher',
|
||||
adaptor:
|
||||
'let legend_set = new Set()\r\n\r\npayload.data.rows && payload.data.rows.forEach(item => {\r\n if(item.teacher_id) {\r\n legend_set.add(item.teacher_name.concat("(", item.teacher_id, ")"));\r\n } else {\r\n legend_set.add(\'未指定\');\r\n }\r\n})\r\n\r\nlet legend = Array.from(legend_set)\r\n\r\nlet series_data = legend\r\n ? legend.map(legend => {\r\n return {\r\n value: payload.data.rows.filter(\r\n item => item.teacher_id ? item.teacher_name.concat("(", item.teacher_id, ")") === legend : legend === \'未指定\'\r\n ).map(item => item.score),\r\n name: legend,\r\n symbol: "rect",\r\n symbolSize: 12,\r\n lineStyle: {\r\n type: "dashed"\r\n }\r\n }\r\n })\r\n : []\r\n\r\nreturn {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items\r\n ? payload.data.items.sort(\r\n (a, b) => a.id - b.id\r\n ).map(item => {\r\n return {\r\n text: item.name,\r\n max: 5\r\n }\r\n })\r\n : [],\r\n legend: legend,\r\n series_data: series_data\r\n }\r\n}\r\n',
|
||||
sendOn: 'this.parent_id',
|
||||
data: {
|
||||
course_id: '${course_id}',
|
||||
organization_id: '${centre_id}',
|
||||
parent: '${parent_id}',
|
||||
},
|
||||
requestAdaptor: '',
|
||||
},
|
||||
dataFilter: '',
|
||||
height: 480,
|
||||
name: 'teacher2',
|
||||
},
|
||||
],
|
||||
size: 'xs',
|
||||
className: 'bg-white',
|
||||
},
|
||||
],
|
||||
direction: 'column',
|
||||
justify: 'start',
|
||||
alignItems: 'stretch',
|
||||
className: '',
|
||||
style: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ type: 'divider', lineStyle: 'solid' },
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
wrapWithPanel: false,
|
||||
submitOnChange: true,
|
||||
reload: '',
|
||||
messages: {},
|
||||
target:
|
||||
'location2?semesterSelect=${semesterSelect}&course_id=${course_id}&group_id=${group_id}&parent_id=${parent_id},location1?semesterSelect=${semesterSelect}&course_id=${course_id}&group_id=${group_id}',
|
||||
body: [
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '学期',
|
||||
name: 'semesterSelect',
|
||||
mode: 'inline',
|
||||
size: 'lg',
|
||||
clearable: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/semesters?order=is_open.desc,since.desc',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}(${item.code})`,\r\n value: item.id\r\n }\r\n })\r\n}',
|
||||
},
|
||||
checkAll: false,
|
||||
searchable: true,
|
||||
selectFirst: false,
|
||||
inputClassName: 'm-l-xs',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '课程',
|
||||
name: 'course_id',
|
||||
mode: 'inline',
|
||||
size: 'md',
|
||||
clearable: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/courses?organization_id=eq.${centre_id}&semester_id=eq.${semesterSelect}&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.length > 0 && payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}(${item.code})`,\r\n value: item.id\r\n }\r\n })\r\n}',
|
||||
sendOn: 'this.semesterSelect',
|
||||
},
|
||||
checkAll: false,
|
||||
searchable: true,
|
||||
selectFirst: false,
|
||||
visibleOn: '',
|
||||
clearValueOnHidden: false,
|
||||
inputClassName: 'm-l-xs',
|
||||
className: 'm-l-sm',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '场地组合',
|
||||
name: 'group_id',
|
||||
mode: 'inline',
|
||||
size: 'lg',
|
||||
clearable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/locations?organization_id=eq.${centre_id}&type=eq.laboratory&is_valid=is.true&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.length > 0 && payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id\r\n }\r\n })\r\n}',
|
||||
},
|
||||
checkAll: true,
|
||||
searchable: true,
|
||||
selectFirst: false,
|
||||
visibleOn: '',
|
||||
clearValueOnHidden: false,
|
||||
inputClassName: 'm-l-xs',
|
||||
className: 'm-l-sm',
|
||||
multiple: true,
|
||||
joinValues: true,
|
||||
valuesNoWrap: false,
|
||||
defaultCheckAll: false,
|
||||
checkAllLabel: '全选',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '一级指标',
|
||||
name: 'parent_id',
|
||||
mode: 'inline',
|
||||
size: 'md',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/teaching_evaluation_items?organization_id=eq.${centre_id}&semester_id=eq.${semesterSelect}&parent=is.null&order=order',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id\r\n }\r\n })\r\n }\r\n}',
|
||||
sendOn: 'this.course_id',
|
||||
},
|
||||
checkAll: false,
|
||||
searchable: true,
|
||||
selectFirst: false,
|
||||
visibleOn: 'this.course_id',
|
||||
clearValueOnHidden: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'grid',
|
||||
columns: [
|
||||
{
|
||||
body: [
|
||||
{
|
||||
type: 'flex',
|
||||
items: [
|
||||
{
|
||||
type: 'wrapper',
|
||||
body: [
|
||||
{
|
||||
type: 'chart',
|
||||
config: {
|
||||
title: [{ text: '场地统计(一级指标)' }],
|
||||
series: [
|
||||
{
|
||||
data: '${series_data}',
|
||||
type: 'radar',
|
||||
emphasis: { lineStyle: { width: 4 } },
|
||||
tooltip: { trigger: 'item' },
|
||||
},
|
||||
],
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
orient: 'vertical',
|
||||
right: 10,
|
||||
top: 20,
|
||||
bottom: 20,
|
||||
data: '${legend}',
|
||||
},
|
||||
radar: [
|
||||
{
|
||||
indicator: '${items}',
|
||||
radius: 200,
|
||||
startAngle: 90,
|
||||
splitNumber: 5,
|
||||
name: {
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
backgroundColor: '#666',
|
||||
borderRadius: 3,
|
||||
padding: [3, 5],
|
||||
},
|
||||
},
|
||||
center: ['35%', '55%'],
|
||||
},
|
||||
],
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: {
|
||||
show: true,
|
||||
title: '数据视图',
|
||||
lang: ['数据视图', '关闭', '刷新'],
|
||||
},
|
||||
saveAsImage: {
|
||||
show: true,
|
||||
title: '保存为图片',
|
||||
type: 'png',
|
||||
},
|
||||
},
|
||||
left: 170,
|
||||
top: -2,
|
||||
},
|
||||
},
|
||||
_mode: '2',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/evaluation_stats_location',
|
||||
adaptor:
|
||||
'let legend_set = new Set()\r\n\r\npayload.data.rows && payload.data.rows.forEach(item => {\r\n legend_set.add(item.location_name)\r\n})\r\n\r\nlet legend = Array.from(legend_set)\r\n\r\nlet series_data = legend\r\n ? legend.map(legend => {\r\n return {\r\n value: payload.data.rows.filter(\r\n item => item.location_name === legend\r\n ).map(item => item.score),\r\n name: legend,\r\n symbol: "rect",\r\n symbolSize: 12,\r\n lineStyle: {\r\n type: "dashed"\r\n }\r\n }\r\n })\r\n : []\r\n\r\nreturn {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items\r\n ? payload.data.items.sort(\r\n (a, b) => a.id - b.id\r\n ).map(item => {\r\n return {\r\n text: item.name,\r\n max: 5\r\n }\r\n })\r\n : [],\r\n legend: legend,\r\n series_data: series_data\r\n }\r\n}\r\n',
|
||||
sendOn: 'this.course_id',
|
||||
data: {
|
||||
course_id: '${course_id}',
|
||||
organization_id: '${centre_id}',
|
||||
group: '${group_id}',
|
||||
},
|
||||
requestAdaptor:
|
||||
"if (api.body.group === '') {\r\n delete api.body.group\r\n}\r\n\r\nreturn {\r\n ...api\r\n}",
|
||||
},
|
||||
dataFilter: '',
|
||||
height: 480,
|
||||
name: 'location1',
|
||||
},
|
||||
],
|
||||
size: 'xs',
|
||||
className: 'bg-white',
|
||||
},
|
||||
],
|
||||
direction: 'column',
|
||||
justify: 'start',
|
||||
alignItems: 'stretch',
|
||||
className: '',
|
||||
__mode: true,
|
||||
style: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
body: [
|
||||
{
|
||||
type: 'flex',
|
||||
items: [
|
||||
{
|
||||
type: 'wrapper',
|
||||
body: [
|
||||
{
|
||||
type: 'chart',
|
||||
config: {
|
||||
title: [{ text: '场地统计(二级指标)' }],
|
||||
series: [
|
||||
{
|
||||
data: '${series_data}',
|
||||
type: 'radar',
|
||||
emphasis: { lineStyle: { width: 4 } },
|
||||
tooltip: { trigger: 'item' },
|
||||
},
|
||||
],
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
orient: 'vertical',
|
||||
right: 10,
|
||||
top: 20,
|
||||
bottom: 20,
|
||||
data: '${legend}',
|
||||
},
|
||||
radar: [
|
||||
{
|
||||
indicator: '${items}',
|
||||
radius: 200,
|
||||
startAngle: 90,
|
||||
splitNumber: 5,
|
||||
name: {
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
backgroundColor: '#666',
|
||||
borderRadius: 3,
|
||||
padding: [3, 5],
|
||||
},
|
||||
},
|
||||
center: ['35%', '55%'],
|
||||
},
|
||||
],
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: {
|
||||
show: true,
|
||||
title: '数据视图',
|
||||
lang: ['数据视图', '关闭', '刷新'],
|
||||
},
|
||||
saveAsImage: {
|
||||
show: true,
|
||||
title: '保存为图片',
|
||||
type: 'png',
|
||||
},
|
||||
},
|
||||
left: 170,
|
||||
top: -2,
|
||||
},
|
||||
},
|
||||
_mode: '2',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/evaluation_stats_location',
|
||||
adaptor:
|
||||
'let legend_set = new Set()\r\n\r\npayload.data.rows && payload.data.rows.forEach(item => {\r\n legend_set.add(item.location_name)\r\n})\r\n\r\nlet legend = Array.from(legend_set)\r\n\r\nlet series_data = legend\r\n ? legend.map(legend => {\r\n return {\r\n value: payload.data.rows.filter(\r\n item => item.location_name === legend\r\n ).map(item => item.score),\r\n name: legend,\r\n symbol: "rect",\r\n symbolSize: 12,\r\n lineStyle: {\r\n type: "dashed"\r\n }\r\n }\r\n })\r\n : []\r\n\r\nreturn {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items\r\n ? payload.data.items.sort(\r\n (a, b) => a.id - b.id\r\n ).map(item => {\r\n return {\r\n text: item.name,\r\n max: 5\r\n }\r\n })\r\n : [],\r\n legend: legend,\r\n series_data: series_data\r\n }\r\n}\r\n',
|
||||
sendOn: 'this.parent_id',
|
||||
data: {
|
||||
course_id: '${course_id}',
|
||||
organization_id: '${centre_id}',
|
||||
parent: '${parent_id}',
|
||||
group: '${group_id}',
|
||||
},
|
||||
requestAdaptor:
|
||||
"if (api.body.group === '') {\r\n delete api.body.group\r\n}\r\n\r\nreturn {\r\n ...api\r\n}",
|
||||
},
|
||||
dataFilter: '',
|
||||
height: 480,
|
||||
name: 'location2',
|
||||
},
|
||||
],
|
||||
size: 'xs',
|
||||
className: 'bg-white',
|
||||
},
|
||||
],
|
||||
direction: 'column',
|
||||
justify: 'start',
|
||||
alignItems: 'stretch',
|
||||
className: '',
|
||||
style: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
type: 'page',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
209
src/pages/schema/centre/chart/grade.schema.ts
Normal file
209
src/pages/schema/centre/chart/grade.schema.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
submitOnChange: true,
|
||||
reload: 'statistic?course_id=$course_id&org_id=$org_id',
|
||||
wrapWithPanel: false,
|
||||
messages: {
|
||||
fetchFailed: '初始化失败',
|
||||
saveSuccess: '统计成功',
|
||||
saveFailed: '保存失败',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
label: '学期',
|
||||
type: 'select',
|
||||
name: 'semesterSelect',
|
||||
mode: 'inline',
|
||||
clearable: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/semesters?order=is_open.desc,since.desc',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}(${item.code})`,\r\n value: item.id\r\n }\r\n })\r\n}',
|
||||
},
|
||||
checkAll: false,
|
||||
size: 'lg',
|
||||
searchable: true,
|
||||
selectFirst: false,
|
||||
id: 'u:b1122bff1df9',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '课程',
|
||||
name: 'course_id',
|
||||
mode: 'inline',
|
||||
clearable: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/courses?organization_id=eq.$centre_id&semester_id=eq.$semesterSelect&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.length > 0 && payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}(${item.code})`,\r\n value: item.id\r\n }\r\n })\r\n}',
|
||||
sendOn: 'this.semesterSelect',
|
||||
},
|
||||
checkAll: false,
|
||||
size: 'lg',
|
||||
searchable: true,
|
||||
selectFirst: false,
|
||||
visibleOn: '',
|
||||
clearValueOnHidden: false,
|
||||
id: 'u:2e2358141406',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '班级',
|
||||
name: 'org_id',
|
||||
mode: 'inline',
|
||||
clearable: true,
|
||||
size: 'lg',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?select=*,course2orgs!inner(*)&path=cd.root&course2orgs.course_id=eq.$course_id&type=in.(school,centre,faculty,class,reelectclass)&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => ({\r\n label: item.name,\r\n value: item.id\r\n }))\r\n }\r\n}',
|
||||
sendOn: 'this.course_id',
|
||||
requestAdaptor: '',
|
||||
messages: {},
|
||||
},
|
||||
searchable: true,
|
||||
visibleOn: 'this.course_id',
|
||||
clearValueOnHidden: true,
|
||||
multiple: true,
|
||||
joinValues: true,
|
||||
delimiter: ',',
|
||||
cascade: false,
|
||||
initiallyOpen: false,
|
||||
unfoldedLevel: 2,
|
||||
onlyChildren: true,
|
||||
withChildren: false,
|
||||
extractValue: true,
|
||||
id: 'u:84b51bcafda6',
|
||||
autoCheckChildren: true,
|
||||
enableNodePath: false,
|
||||
showIcon: true,
|
||||
checkAll: false,
|
||||
},
|
||||
{
|
||||
type: 'control',
|
||||
label: '',
|
||||
remark: null,
|
||||
mode: 'inline',
|
||||
visibleOn: 'this.is_cursem && this.course_id',
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
level: 'primary',
|
||||
actionType: 'ajax',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/calculate_grade',
|
||||
data: {
|
||||
course_id: '$course_id',
|
||||
org_id: '$org_id',
|
||||
},
|
||||
requestAdaptor:
|
||||
'const { course_id, org_id } = api.data\r\n\r\nif (org_id) {\r\n return {\r\n ...api\r\n }\r\n} else {\r\n return {\r\n ...api,\r\n data: { course_id }\r\n }\r\n}',
|
||||
},
|
||||
reload: 'statistic',
|
||||
messages: {},
|
||||
label: '统计',
|
||||
id: 'u:a147092cff8f',
|
||||
},
|
||||
],
|
||||
id: 'u:4944fd7962d5',
|
||||
},
|
||||
{
|
||||
type: 'service',
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/semesters?id=eq.$semesterSelect',
|
||||
sendOn: 'this.semesterSelect',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n is_cursem: payload.data.items.length > 0 ? payload.data.items[0].is_open : false\r\n }\r\n}',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'hidden',
|
||||
name: 'is_cursem',
|
||||
id: 'u:39ca4db8501a',
|
||||
},
|
||||
],
|
||||
id: 'u:3e13ab87d15b',
|
||||
dsType: 'api',
|
||||
},
|
||||
],
|
||||
id: 'u:970b1b98cc6a',
|
||||
feat: 'Insert',
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/grade_stat',
|
||||
data: {
|
||||
page: '$page',
|
||||
course_id: '$course_id',
|
||||
perpage: '$perPage',
|
||||
org_id: '$org_id',
|
||||
},
|
||||
adaptor: '',
|
||||
requestAdaptor:
|
||||
'const { course_id, org_id, page, perpage } = api.data\r\n\r\nif (course_id && org_id) {\r\n return {\r\n ...api\r\n }\r\n} else if (course_id) {\r\n return {\r\n ...api,\r\n data: { course_id, page, perpage }\r\n }\r\n} else {\r\n return {\r\n ...api,\r\n data: {}\r\n }\r\n}',
|
||||
dataType: 'form',
|
||||
sendOn: '',
|
||||
},
|
||||
columns: [],
|
||||
messages: {},
|
||||
syncLocation: false,
|
||||
pageField: 'page',
|
||||
perPageField: 'perPage',
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'export-excel',
|
||||
label: '全量导出 Excel',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/grade_stat',
|
||||
data: {
|
||||
course_id: '$course_id',
|
||||
org_id: '$org_id',
|
||||
},
|
||||
dataType: 'form',
|
||||
requestAdaptor:
|
||||
'const { course_id, org_id } = api.data\r\n\r\nif (course_id && org_id) {\r\n return {\r\n ...api\r\n }\r\n} else if (course_id) {\r\n return {\r\n ...api,\r\n data: { course_id }\r\n }\r\n} else {\r\n return {\r\n ...api,\r\n data: {}\r\n }\r\n}',
|
||||
},
|
||||
filename: '$filename',
|
||||
id: 'u:8d80c123da31',
|
||||
},
|
||||
],
|
||||
footerToolbar: [
|
||||
{
|
||||
type: 'pagination',
|
||||
tpl: '内容',
|
||||
},
|
||||
{
|
||||
type: 'switch-per-page',
|
||||
tpl: '内容',
|
||||
},
|
||||
],
|
||||
name: 'statistic',
|
||||
title: '',
|
||||
bodyClassName: 'common-height',
|
||||
body: [],
|
||||
toolbar: [],
|
||||
alwaysShowPagination: false,
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
affixHeader: false,
|
||||
id: 'u:ec1754a6f5a9',
|
||||
},
|
||||
],
|
||||
title: '',
|
||||
messages: {},
|
||||
id: 'u:e3bc67e03bea',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
411
src/pages/schema/centre/chart/workload.schema.ts
Normal file
411
src/pages/schema/centre/chart/workload.schema.ts
Normal file
@@ -0,0 +1,411 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'grid',
|
||||
columns: [
|
||||
{
|
||||
md: 8,
|
||||
body: [
|
||||
{
|
||||
type: 'chart',
|
||||
config: {
|
||||
title: [
|
||||
{
|
||||
text: '实验项目开课次数统计',
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
data: '$yAxis_les',
|
||||
type: 'pie',
|
||||
name: '项目',
|
||||
radius: '55%',
|
||||
center: ['40%', '50%'],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b} : {c} ({d}%)',
|
||||
},
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
orient: 'vertical',
|
||||
top: 20,
|
||||
bottom: 20,
|
||||
data: '$xAxis_exp',
|
||||
right: 10,
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: {
|
||||
show: true,
|
||||
title: '数据视图',
|
||||
lang: ['数据视图', '关闭', '刷新'],
|
||||
},
|
||||
saveAsImage: {
|
||||
show: true,
|
||||
title: '保存为图片',
|
||||
type: 'png',
|
||||
},
|
||||
},
|
||||
left: 190,
|
||||
top: -2,
|
||||
},
|
||||
},
|
||||
_mode: '2',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/schedules?select=*,projects:projects!schedule_project_id_fkey(id,name)&semester_id=eq.${semesterSelect}&teacher_id=in.(${teacherSelect})',
|
||||
requestAdaptor:
|
||||
"api.url = api.url.replace('&teacher_id=in.%28%29', '')\r\n\r\nreturn api",
|
||||
adaptor:
|
||||
'let xAxis_exp = []\r\nlet yAxis_les = []\r\npayload.data.items.forEach(item => {\r\n const index_exp = xAxis_exp.findIndex(i => i === item.projects.name)\r\n if (index_exp === -1) {\r\n xAxis_exp.push(item.projects.name)\r\n const index_exp = xAxis_exp.findIndex(i => i === item.projects.name)\r\n yAxis_les[index_exp] = {\r\n name: item.projects.name,\r\n value: 1\r\n }\r\n } else {\r\n yAxis_les[index_exp].value += 1\r\n }\r\n})\r\nreturn {\r\n ...payload,\r\n data: {\r\n xAxis_exp: xAxis_exp,\r\n yAxis_les: yAxis_les\r\n }\r\n}',
|
||||
error: {
|
||||
message: '',
|
||||
},
|
||||
},
|
||||
dataFilter: '',
|
||||
height: 450,
|
||||
name: 'chart1',
|
||||
id: 'u:4f3d6cd9e92a',
|
||||
},
|
||||
],
|
||||
id: 'u:3a343388441f',
|
||||
},
|
||||
{
|
||||
body: [
|
||||
{
|
||||
type: 'chart',
|
||||
config: {
|
||||
title: [
|
||||
{
|
||||
text: '教师课堂次数统计',
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
data: '$yAxis_les',
|
||||
type: 'pie',
|
||||
name: '教师',
|
||||
radius: '55%',
|
||||
center: ['40%', '50%'],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b} : {c} ({d}%)',
|
||||
},
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
orient: 'vertical',
|
||||
right: 10,
|
||||
top: 20,
|
||||
bottom: 20,
|
||||
data: '$xAxis_exp',
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: {
|
||||
show: true,
|
||||
title: '数据视图',
|
||||
lang: ['数据视图', '关闭', '刷新'],
|
||||
},
|
||||
saveAsImage: {
|
||||
show: true,
|
||||
title: '保存为图片',
|
||||
type: 'png',
|
||||
},
|
||||
},
|
||||
left: 155,
|
||||
top: -2,
|
||||
},
|
||||
},
|
||||
_mode: '2',
|
||||
dataFilter: '',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/users?select=*,schedules:schedules!schedule_teacher_id_fkey(*,projects:projects!schedule_project_id_fkey(id,name))&role=eq.teacher&schedules.semester_id=eq.${semesterSelect}&id=in.(${teacherSelect})&order=code',
|
||||
requestAdaptor:
|
||||
"api.url = api.url.replace('&id=in.%28%29', '')\r\n\r\nreturn api",
|
||||
adaptor:
|
||||
'let xAxis_tch = []\r\nlet yAxis_les = []\r\npayload.data.items.forEach(payload_item => {\r\n let elected = 0\r\n xAxis_tch.push(payload_item.name)\r\n yAxis_les.push({\r\n name: payload_item.name,\r\n value: payload_item.schedules.length ? payload_item.schedules.length : 0\r\n })\r\n})\r\nreturn {\r\n ...payload,\r\n data: {\r\n xAxis_tch: xAxis_tch,\r\n yAxis_les: yAxis_les\r\n }\r\n}',
|
||||
},
|
||||
height: 450,
|
||||
name: 'chart2',
|
||||
id: 'u:ed31d767d8d5',
|
||||
},
|
||||
],
|
||||
id: 'u:989c40ed8233',
|
||||
},
|
||||
],
|
||||
id: 'u:63c06d3a12fc',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
lineStyle: 'solid',
|
||||
id: 'u:b0c8cc501851',
|
||||
},
|
||||
{
|
||||
type: 'chart',
|
||||
config: {
|
||||
title: [
|
||||
{
|
||||
text: '实验项目相关人次统计',
|
||||
},
|
||||
],
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#283b56',
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: ['可选人数', '已选人数', '签到人数', '未签到人数'],
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: '$xAxis_exp',
|
||||
axisLabel: {
|
||||
rotate: -10,
|
||||
interval: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '人数',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: '$yAxis_unsel',
|
||||
type: 'bar',
|
||||
name: '可选人数',
|
||||
barWidth: 20,
|
||||
},
|
||||
{
|
||||
data: '$yAxis_sel',
|
||||
type: 'bar',
|
||||
name: '已选人数',
|
||||
barWidth: 20,
|
||||
},
|
||||
{
|
||||
data: '$yAxis_fin',
|
||||
type: 'bar',
|
||||
name: '签到人数',
|
||||
barWidth: 10,
|
||||
stack: '已选人数',
|
||||
},
|
||||
{
|
||||
data: '$yAxis_unfin',
|
||||
type: 'bar',
|
||||
name: '未签到人数',
|
||||
barWidth: 10,
|
||||
stack: '已选人数',
|
||||
},
|
||||
],
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: {
|
||||
show: true,
|
||||
title: '数据视图',
|
||||
lang: ['数据视图', '关闭', '刷新'],
|
||||
},
|
||||
saveAsImage: {
|
||||
show: true,
|
||||
title: '保存为图片',
|
||||
type: 'png',
|
||||
},
|
||||
},
|
||||
left: 190,
|
||||
top: -2,
|
||||
},
|
||||
},
|
||||
_mode: '2',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/workload_stats?select=*,projects:projects!schedule_project_id_fkey(id,name)&semester_id=eq.$semesterSelect&teacher_id=in.(${teacherSelect})&organization_id=eq.$centre_id',
|
||||
requestAdaptor:
|
||||
"api.url = api.url.replace('&teacher_id=in.%28%29', '')\r\n\r\nreturn api",
|
||||
adaptor:
|
||||
'let xAxis_exp = []\r\nlet yAxis_sel = []\r\nlet yAxis_unsel = []\r\nlet yAxis_fin = []\r\nlet yAxis_unfin = []\r\n\r\npayload.data.items.forEach(item => {\r\n const index_exp = xAxis_exp.findIndex(i => i === item.projects.name)\r\n\r\n if (index_exp === -1) {\r\n xAxis_exp.push(item.projects.name)\r\n const index_exp = xAxis_exp.findIndex(i => i === item.projects.name)\r\n yAxis_sel[index_exp] = item.current_student_number\r\n yAxis_unsel[index_exp] = item.max_student_number - item.current_student_number\r\n yAxis_fin[index_exp] = item.finished_count\r\n yAxis_unfin[index_exp] = item.unfinished_count\r\n } else {\r\n yAxis_sel[index_exp] += item.current_student_number\r\n yAxis_unsel[index_exp] += (item.max_student_number - item.current_student_number)\r\n yAxis_fin[index_exp] += item.finished_count\r\n yAxis_unfin[index_exp] += item.unfinished_count\r\n }\r\n})\r\n\r\nreturn {\r\n ...payload,\r\n data: {\r\n xAxis_exp: xAxis_exp,\r\n yAxis_sel: yAxis_sel,\r\n yAxis_unsel: yAxis_unsel,\r\n yAxis_fin: yAxis_fin,\r\n yAxis_unfin: yAxis_unfin\r\n }\r\n}',
|
||||
},
|
||||
replaceChartOption: false,
|
||||
dataFilter: '',
|
||||
height: 450,
|
||||
name: 'chart3',
|
||||
id: 'u:5357767552e5',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
lineStyle: 'solid',
|
||||
id: 'u:01c9e40a887d',
|
||||
},
|
||||
{
|
||||
type: 'chart',
|
||||
config: {
|
||||
title: [
|
||||
{
|
||||
text: '教师课堂相关人次统计',
|
||||
},
|
||||
],
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#283b56',
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: ['已选人数', '签到人数', '未签到人数'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '人数',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: '$yAxis_sel',
|
||||
type: 'bar',
|
||||
name: '已选人数',
|
||||
barWidth: 20,
|
||||
},
|
||||
{
|
||||
data: '$yAxis_fin',
|
||||
type: 'bar',
|
||||
name: '签到人数',
|
||||
barWidth: 10,
|
||||
stack: '已选人数',
|
||||
},
|
||||
{
|
||||
data: '$yAxis_unfin',
|
||||
type: 'bar',
|
||||
name: '未签到人数',
|
||||
barWidth: 10,
|
||||
stack: '已选人数',
|
||||
},
|
||||
],
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: '$xAxis_tch',
|
||||
},
|
||||
],
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: {
|
||||
show: true,
|
||||
title: '数据视图',
|
||||
lang: ['数据视图', '关闭', '刷新'],
|
||||
},
|
||||
saveAsImage: {
|
||||
show: true,
|
||||
title: '保存为图片',
|
||||
type: 'png',
|
||||
},
|
||||
},
|
||||
left: 190,
|
||||
top: -2,
|
||||
},
|
||||
},
|
||||
_mode: '2',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/users?select=*,workload_stats:workload_stats!schedule_teacher_id_fkey(*,projects:projects!schedule_project_id_fkey(id,name))&role=eq.teacher&workload_stats.semester_id=eq.$semesterSelect&id=in.(${teacherSelect})&workload_stats.organization_id=eq.$centre_id&order=code',
|
||||
requestAdaptor:
|
||||
"api.url = api.url.replace('&id=in.%28%29', '')\r\n\r\nreturn api",
|
||||
adaptor:
|
||||
'let xAxis_tch = []\r\nlet yAxis_sel = []\r\nlet yAxis_fin = []\r\nlet yAxis_unfin = []\r\n\r\npayload.data.items.forEach(payload_item => {\r\n let elected = 0\r\n let finished = 0\r\n let unfinished = 0\r\n\r\n payload_item.workload_stats.length > 0 && payload_item.workload_stats.forEach(workload_item => {\r\n elected += workload_item.current_student_number\r\n finished += workload_item.finished_count\r\n unfinished += workload_item.unfinished_count\r\n })\r\n\r\n xAxis_tch.push(payload_item.name)\r\n yAxis_sel.push(elected)\r\n yAxis_fin.push(finished)\r\n yAxis_unfin.push(unfinished)\r\n})\r\n\r\nreturn {\r\n ...payload,\r\n data: {\r\n xAxis_tch: xAxis_tch,\r\n yAxis_sel: yAxis_sel,\r\n yAxis_fin: yAxis_fin,\r\n yAxis_unfin: yAxis_unfin\r\n }\r\n}',
|
||||
},
|
||||
replaceChartOption: false,
|
||||
dataFilter: '',
|
||||
height: 450,
|
||||
name: 'chart4',
|
||||
id: 'u:9c6e8236a44f',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
lineStyle: 'solid',
|
||||
id: 'u:e448f96e6b83',
|
||||
},
|
||||
],
|
||||
toolbar: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
wrapWithPanel: false,
|
||||
submitOnChange: true,
|
||||
target: 'chart1,chart2,chart3,chart4',
|
||||
canAccessSuperData: true,
|
||||
body: [
|
||||
{
|
||||
label: '学期',
|
||||
type: 'select',
|
||||
name: 'semesterSelect',
|
||||
mode: 'inline',
|
||||
clearable: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/semesters?order=is_open.desc,since.desc',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}(${item.code})`,\r\n value: item.id\r\n }\r\n })\r\n}',
|
||||
},
|
||||
checkAll: false,
|
||||
size: 'lg',
|
||||
searchable: true,
|
||||
selectFirst: false,
|
||||
className: 'm',
|
||||
id: 'u:bdeea7faf818',
|
||||
},
|
||||
{
|
||||
label: '教师',
|
||||
type: 'select',
|
||||
name: 'teacherSelect',
|
||||
mode: 'inline',
|
||||
clearable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/users?role=eq.teacher&order=name,code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}(${item.code})`,\r\n value: item.id\r\n }\r\n })\r\n}',
|
||||
},
|
||||
checkAll: true,
|
||||
size: 'lg',
|
||||
searchable: true,
|
||||
selectFirst: false,
|
||||
multiple: true,
|
||||
className: 'm',
|
||||
id: 'u:bdeea7faf818',
|
||||
},
|
||||
],
|
||||
id: 'u:5003bb8ac5e6',
|
||||
},
|
||||
],
|
||||
id: 'u:b5376a388009',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
148
src/pages/schema/centre/computing/dataset.schema.ts
Normal file
148
src/pages/schema/centre/computing/dataset.schema.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'dataset-renderer',
|
||||
name: 'storage',
|
||||
bucket: 'dataset',
|
||||
addFolder: {
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
reload: 'storage',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '新建文件夹',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'storage/v1/object/dataset/$path/$name/.empty',
|
||||
requestAdaptor:
|
||||
"api.url = api.url.replace('//', '/');return api",
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '文件夹名称',
|
||||
name: 'name',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
},
|
||||
moveFile: {
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
reload: 'storage',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '重命名',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'storage/v1/object/move',
|
||||
data: {
|
||||
bucketId: 'dataset',
|
||||
sourceKey: '$sourceKey',
|
||||
dest: '$dest',
|
||||
path: '$path',
|
||||
},
|
||||
requestAdaptor:
|
||||
"console.log(api)\r\nreturn {\r\n ...api,\r\n data: {\r\n bucketId: api.data.bucketId,\r\n sourceKey: api.data.sourceKey,\r\n destinationKey: api.data.path === '' ? api.data.dest : api.data.path + '/' + api.data.dest\r\n }\r\n}",
|
||||
},
|
||||
body: [
|
||||
{
|
||||
name: 'dest',
|
||||
type: 'input-text',
|
||||
label: '名称',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
},
|
||||
delFile: {
|
||||
type: 'button',
|
||||
actionType: 'ajax',
|
||||
reload: 'storage',
|
||||
label: false,
|
||||
api: {
|
||||
method: 'delete',
|
||||
url: '/storage/v1/object/dataset',
|
||||
data: {
|
||||
prefixes: '$prefixes',
|
||||
},
|
||||
headers: {
|
||||
post2rest: false,
|
||||
},
|
||||
},
|
||||
confirmText: '确认删除吗?',
|
||||
},
|
||||
delFiles: {
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
reload: 'storage',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '批量删除文件',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'delete',
|
||||
url: 'storage/v1/object/dataset',
|
||||
data: { prefixes: '$prefixes', path: '$path' },
|
||||
headers: { post2rest: false },
|
||||
requestAdaptor:
|
||||
"return {\r\n ...api,\r\n data: {\r\n prefixes: api.data.prefixes.map(item => api.data.path === '' ? item : api.data.path + '/' + item)\r\n }\r\n}",
|
||||
},
|
||||
body: [
|
||||
{
|
||||
name: 'prefixes',
|
||||
type: 'checkboxes',
|
||||
label: false,
|
||||
inline: false,
|
||||
checkAll: true,
|
||||
joinValues: false,
|
||||
extractValue: true,
|
||||
required: true,
|
||||
source: {
|
||||
method: 'post',
|
||||
url: '/storage/v1/object/list/dataset',
|
||||
data: {
|
||||
prefix: '$path',
|
||||
sortBy: {
|
||||
column: 'name',
|
||||
order: 'asc',
|
||||
},
|
||||
},
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.filter(item => item.id && item.name.charAt(0) !== ".").map(item => {\r\n return {\r\n label: item.name,\r\n value: item.name\r\n }\r\n })\r\n}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
,
|
||||
],
|
||||
};
|
||||
|
||||
export { schema };
|
||||
667
src/pages/schema/centre/computing/docrecommend.schema.ts
Normal file
667
src/pages/schema/centre/computing/docrecommend.schema.ts
Normal file
@@ -0,0 +1,667 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'container',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
className: '',
|
||||
submitOnChange: true,
|
||||
reload: 'notebooks?author=$author&type=$type&publish_at=$publish_at',
|
||||
wrapWithPanel: false,
|
||||
canAccessSuperData: false,
|
||||
mode: 'inline',
|
||||
name: 'filter',
|
||||
body: [
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
mode: 'inline',
|
||||
label: '作者',
|
||||
name: 'author',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/users?order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: item.name + "(" + item.code + ")",\r\n value: "eq." + item.code\r\n }\r\n })\r\n}',
|
||||
},
|
||||
checkAll: false,
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
clearable: true,
|
||||
className: 'm-l-sm',
|
||||
inputClassName: 'm-l-xs',
|
||||
id: 'u:f2ef198e2dcf',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '类型',
|
||||
name: 'type',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/dicts?typecode=eq.028&order=id',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: item.dictvalue,\r\n value: "eq." + item.dictkey\r\n }\r\n })\r\n}',
|
||||
},
|
||||
mode: 'inline',
|
||||
inputClassName: 'm-l-xs',
|
||||
size: 'md',
|
||||
clearable: true,
|
||||
id: 'u:28cd5a8d0d4f',
|
||||
},
|
||||
{
|
||||
type: 'input-date-range',
|
||||
name: 'publish_at',
|
||||
mode: 'inline',
|
||||
label: '发布日期',
|
||||
clearable: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
value: '',
|
||||
format: 'YYYY-MM-DD',
|
||||
ranges:
|
||||
'yesterday,today,7daysago,prevweek,thismonth,prevmonth,prevquarter',
|
||||
id: 'u:0b09573c1ed9',
|
||||
},
|
||||
],
|
||||
id: 'u:e19f8840c465',
|
||||
},
|
||||
],
|
||||
id: 'u:83f2045dc8aa',
|
||||
feat: 'Insert',
|
||||
},
|
||||
{ type: 'divider', lineStyle: 'solid', id: 'u:b12bc5cfec5e' },
|
||||
{
|
||||
type: 'crud',
|
||||
name: 'notebooks',
|
||||
syncLocation: false,
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/notebooks?select=*,type:dicts!notebook_type_fkey(*),status:dicts!notebook_status_fkey(*)&name=like.*$keywords*&isprivate=is.false&author=$author&type=$type&order=publish_at.desc.nullslast',
|
||||
data: {
|
||||
page: '$page',
|
||||
perPage: 8,
|
||||
orderBy: '$orderBy',
|
||||
orderDir: '$orderDir',
|
||||
publish_at: '$publish_at',
|
||||
},
|
||||
requestAdaptor:
|
||||
"if (api.query.author === '') api.url = api.url.replace('&author=', '')\r\n\r\nif (api.query.type === '') api.url = api.url.replace('&type=', '')\r\n\r\nif (api.body.publish_at === '') {\r\n api.url = api.url.replace('&publish_at=', '')\r\n} else {\r\n let date_former = api.body.publish_at && api.body.publish_at.split(',')[0]\r\n let date_latter = api.body.publish_at && api.body.publish_at.split(',')[1]\r\n\r\n if (date_former === date_latter) {\r\n api.url = api.url.replace(\r\n '&publish_at=' + date_former + '%2C' + date_latter,\r\n '&publish_at=gte.' + date_former + ' 00:00:00&publish_at=lte.' + date_former + ' 23:59:59'\r\n )\r\n } else {\r\n api.url = api.url.replace(\r\n '&publish_at=' + date_former + '%2C' + date_latter,\r\n '&publish_at=gte.' + date_former + ' 00:00:00&publish_at=lte.' + date_latter + ' 23:59:59'\r\n )\r\n }\r\n}\r\n\r\nif (api.query.orderBy && api.query.orderDir) {\r\n api.url = api.url.replace('order=publish_at.desc.nullslast', '');\r\n api.url = api.url.replace('&orderBy=' + api.query.orderBy + '&orderDir=' + api.query.orderDir, '&order=' + api.query.orderBy + '.' + api.query.orderDir);\r\n} else {\r\n api.url = api.url.replace('&orderBy=' + api.query.orderBy + '&orderDir=' + api.query.orderDir, '');\r\n}\r\n\r\nreturn api;",
|
||||
},
|
||||
footerToolbar: [
|
||||
{ type: 'pagination' },
|
||||
{ type: 'switch-per-page' },
|
||||
{ type: 'statistics' },
|
||||
],
|
||||
perPageAvailable: [8,],
|
||||
bulkActions: [],
|
||||
itemActions: [],
|
||||
id: 'u:46a2b5faa785',
|
||||
filter: {
|
||||
title: '',
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'keywords',
|
||||
label: '名称关键字',
|
||||
id: 'u:a9a2048f6f68',
|
||||
},
|
||||
{
|
||||
type: 'submit',
|
||||
label: '搜索',
|
||||
actionType: 'submit',
|
||||
id: 'u:decd16bff04d',
|
||||
level: 'primary',
|
||||
},
|
||||
],
|
||||
reload: 'notebooks?keywords=$keywords',
|
||||
canAccessSuperData: false,
|
||||
wrapWithPanel: false,
|
||||
autoFocus: false,
|
||||
mode: 'inline',
|
||||
id: 'u:1888123468b3',
|
||||
feat: 'Insert',
|
||||
},
|
||||
messages: {},
|
||||
card: {
|
||||
type: 'card',
|
||||
header: {
|
||||
title: '',
|
||||
subTitle: '',
|
||||
avatar: '/olms/notebook/${pid}/cover.png',
|
||||
desc: '',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
name: 'name',
|
||||
label: '',
|
||||
type: 'text',
|
||||
sortable: true,
|
||||
id: 'u:02b589f37127',
|
||||
inline: true,
|
||||
tpl: '名称:${name}',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
name: 'author',
|
||||
label: '',
|
||||
type: 'text',
|
||||
sortable: true,
|
||||
id: 'u:3c98eb214a1b',
|
||||
inline: true,
|
||||
tpl: '创建者:${author}'
|
||||
},
|
||||
{
|
||||
type: 'input-tag',
|
||||
label: '',
|
||||
name: 'tag',
|
||||
id: 'u:7302818b79ff',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/tags?order=name',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map((item) => {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: item.name,\r\n };\r\n }),\r\n};',
|
||||
},
|
||||
multiple: true,
|
||||
joinValues: false,
|
||||
extractValue: true,
|
||||
mode: 'normal',
|
||||
clearable: false,
|
||||
creatable: true,
|
||||
createBtnLabel: '新增标签',
|
||||
addControls: [
|
||||
{
|
||||
label: '名称',
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
value: '',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
],
|
||||
addApi: {
|
||||
method: 'post',
|
||||
url: 'rest/tags',
|
||||
data: { name: '${name}' },
|
||||
dataType: 'json',
|
||||
},
|
||||
editable: false,
|
||||
editControls: [
|
||||
{
|
||||
label: '名称',
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
value: '',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
],
|
||||
editApi: {
|
||||
method: 'patch',
|
||||
url: 'rest/tags?id=eq.${id}',
|
||||
data: { name: '${name}' },
|
||||
dataType: 'json',
|
||||
},
|
||||
removable: true,
|
||||
deleteApi: { method: 'delete', url: 'rest/tags?id=eq.${id}' },
|
||||
optionsTip: '最近您使用的标签',
|
||||
className: 'm-b-sm m-t-sm',
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: '描述',
|
||||
type: 'text',
|
||||
sortable: true,
|
||||
id: 'u:67cf5873f23e',
|
||||
inline: true,
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
name: 'type.dictvalue',
|
||||
label: '类型',
|
||||
type: 'text',
|
||||
sortable: true,
|
||||
id: 'u:c3eb052e801c',
|
||||
inline: true,
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
name: 'publish_at',
|
||||
label: '发布日期',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
sortable: true,
|
||||
id: 'u:fdb56224ba1b',
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'tag',
|
||||
label: '标签',
|
||||
placeholder: '-',
|
||||
id: 'u:d3e2a072de3c',
|
||||
inline: true,
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
type: 'mapping',
|
||||
map: {
|
||||
false: '<span class="label label-danger">否</span>',
|
||||
true: '<span class="label label-success">是</span>',
|
||||
},
|
||||
label: '首页展示',
|
||||
name: 'isrecommend',
|
||||
sortable: true,
|
||||
id: 'u:c717e7e057a9',
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
name: 'recommendOrder',
|
||||
label: '展示次序',
|
||||
type: 'text',
|
||||
sortable: true,
|
||||
id: 'u:77fb26a7f92e',
|
||||
inline: true,
|
||||
hidden: true,
|
||||
},
|
||||
// {
|
||||
// type: 'button-group',
|
||||
// buttons: [
|
||||
// {
|
||||
// type: 'button',
|
||||
// actionType: 'dialog',
|
||||
// dialog: {
|
||||
// title: '修改标签',
|
||||
// body: [
|
||||
// {
|
||||
// type: 'form',
|
||||
// title: '表单',
|
||||
// api: {
|
||||
// method: 'patch',
|
||||
// url: 'rest/notebooks?id=eq.${id}',
|
||||
// data: { tag: '${tag}' },
|
||||
// dataType: 'json',
|
||||
// },
|
||||
// body: [
|
||||
// {
|
||||
// label: '标签',
|
||||
// type: 'select',
|
||||
// name: 'tag',
|
||||
// checkAll: true,
|
||||
// source: {
|
||||
// method: 'get',
|
||||
// url: 'rest/tags?order=name',
|
||||
// adaptor:
|
||||
// 'return {\r\n ...payload,\r\n data: payload.data.items.map((item) => {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: item.name,\r\n };\r\n }),\r\n};',
|
||||
// },
|
||||
// multiple: true,
|
||||
// joinValues: false,
|
||||
// extractValue: true,
|
||||
// mode: 'normal',
|
||||
// clearable: true,
|
||||
// creatable: true,
|
||||
// createBtnLabel: '新增标签',
|
||||
// addControls: [
|
||||
// {
|
||||
// label: '名称',
|
||||
// type: 'text',
|
||||
// name: 'name',
|
||||
// value: '',
|
||||
// required: true,
|
||||
// mode: 'normal',
|
||||
// },
|
||||
// ],
|
||||
// addApi: {
|
||||
// method: 'post',
|
||||
// url: 'rest/tags',
|
||||
// data: { name: '${name}' },
|
||||
// dataType: 'json',
|
||||
// },
|
||||
// editable: true,
|
||||
// editControls: [
|
||||
// {
|
||||
// label: '名称',
|
||||
// type: 'text',
|
||||
// name: 'name',
|
||||
// value: '',
|
||||
// required: true,
|
||||
// mode: 'normal',
|
||||
// },
|
||||
// ],
|
||||
// editApi: {
|
||||
// method: 'patch',
|
||||
// url: 'rest/tags?id=eq.${id}',
|
||||
// data: { name: '${name}' },
|
||||
// dataType: 'json',
|
||||
// },
|
||||
// removable: true,
|
||||
// deleteApi: {
|
||||
// method: 'delete',
|
||||
// url: 'rest/tags?id=eq.${id}',
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// type: 'dialog',
|
||||
// closeOnEsc: true,
|
||||
// showCloseButton: true,
|
||||
// },
|
||||
// level: 'link',
|
||||
// icon: 'fa fa-pencil text-info',
|
||||
// size: 'md',
|
||||
// tooltip: '修改标签',
|
||||
// tooltipPlacement: 'top',
|
||||
// iconClassName: 'pull-left',
|
||||
// className: 'p-r-none p-l-none',
|
||||
// id: 'u:39235263c3cb',
|
||||
// },
|
||||
// {
|
||||
// type: 'button',
|
||||
// label: '',
|
||||
// level: 'link',
|
||||
// size: 'md',
|
||||
// icon: 'fa fa-eye text-warning',
|
||||
// iconClassName: 'pull-left',
|
||||
// actionType: 'dialog',
|
||||
// tooltip: '预览',
|
||||
// tooltipPlacement: 'top',
|
||||
// className: 'p-r-none p-l-none',
|
||||
// dialog: {
|
||||
// title: '预览',
|
||||
// body: [
|
||||
// {
|
||||
// type: 'form',
|
||||
// title: '表单',
|
||||
// body: [
|
||||
// {
|
||||
// type: 'formula',
|
||||
// name: 'iframeSrc',
|
||||
// formula:
|
||||
// '"http://192.168.71.19"+ "/notebook/" + pid + "/" + post',
|
||||
// },
|
||||
// {
|
||||
// type: 'iframe',
|
||||
// src: '${iframeSrc}',
|
||||
// height: '700px',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// type: 'dialog',
|
||||
// closeOnEsc: false,
|
||||
// closeOnOutside: false,
|
||||
// showCloseButton: true,
|
||||
// size: 'xl',
|
||||
// actions: [],
|
||||
// },
|
||||
// id: 'u:1aa082ee65f0',
|
||||
// },
|
||||
// {
|
||||
// type: 'button',
|
||||
// label: '',
|
||||
// actionType: 'dialog',
|
||||
// size: 'md',
|
||||
// tooltip: '首页展示',
|
||||
// tooltipPlacement: 'top',
|
||||
// iconClassName: 'pull-left',
|
||||
// className: 'p-r-none p-l-none',
|
||||
// icon: 'fa fa-thumbs-up text-info',
|
||||
// level: 'link',
|
||||
// visibleOn: 'this.type.dictkey === "notebook"',
|
||||
// dialog: {
|
||||
// type: 'dialog',
|
||||
// title: '首页展示',
|
||||
// body: [
|
||||
// {
|
||||
// type: 'form',
|
||||
// title: '表单',
|
||||
// api: {
|
||||
// method: 'patch',
|
||||
// url: 'rest/notebooks/$id',
|
||||
// data: { '&': '$$' },
|
||||
// requestAdaptor:
|
||||
// 'if (api.data.recommendOrder) api.data.isrecommend = true;\r\nelse api.data.isrecommend = false;\r\n\r\nreturn api;',
|
||||
// },
|
||||
// reload: 'notebooks',
|
||||
// body: [
|
||||
// {
|
||||
// type: 'select',
|
||||
// label: '序号',
|
||||
// name: 'recommendOrder',
|
||||
// clearable: true,
|
||||
// options: [
|
||||
// { label: '1', value: 1 },
|
||||
// { label: '2', value: 2 },
|
||||
// { label: '3', value: 3 },
|
||||
// { label: '4', value: 4 },
|
||||
// { label: '5', value: 5 },
|
||||
// { label: '6', value: 6 },
|
||||
// { label: '7', value: 7 },
|
||||
// { label: '8', value: 8 },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// closeOnEsc: true,
|
||||
// showCloseButton: true,
|
||||
// closeOnOutside: false,
|
||||
// size: 'sm',
|
||||
// },
|
||||
// id: 'u:98e68e904df9',
|
||||
// },
|
||||
// {
|
||||
// type: 'button',
|
||||
// label: '',
|
||||
// level: 'link',
|
||||
// icon: 'fa fa-times text-danger',
|
||||
// size: 'md',
|
||||
// tooltip: '删除',
|
||||
// tooltipPlacement: 'top',
|
||||
// iconClassName: 'pull-left',
|
||||
// className: 'p-r-none p-l-none',
|
||||
// actionType: 'ajax',
|
||||
// api: { method: 'post', url: 'nb/cancel_notebook/$id/$pid' },
|
||||
// id: 'u:713ccf6aa5fc',
|
||||
// },
|
||||
// ],
|
||||
// id: 'u:2408808996c7',
|
||||
// label: '',
|
||||
// },
|
||||
{
|
||||
type: 'button-group',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '预览',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'hidden',
|
||||
componentId: 'u:5fe9a2ffdbe1',
|
||||
ignoreError: false,
|
||||
},
|
||||
{
|
||||
componentId: 'u:16d9090a3803',
|
||||
ignoreError: false,
|
||||
actionType: 'show',
|
||||
},
|
||||
{
|
||||
componentId: 'my_service1',
|
||||
actionType: 'setValue',
|
||||
args: { value: '${event.data}' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
level: 'light',
|
||||
size: 'sm',
|
||||
id: 'u:1aa082ee65f0',
|
||||
themeCss: {
|
||||
className: {
|
||||
'border:default': {
|
||||
'top-border-style': 'solid',
|
||||
'left-border-style': 'solid',
|
||||
'right-border-style': 'solid',
|
||||
'bottom-border-style': 'solid',
|
||||
'top-border-width': 'none',
|
||||
'left-border-width': 'none',
|
||||
'right-border-width': 'none',
|
||||
'bottom-border-width': 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// type: 'button',
|
||||
// label: '预览',
|
||||
// onEvent: {
|
||||
// click: {
|
||||
// actions: [
|
||||
// {
|
||||
// actionType: 'hidden',
|
||||
// componentId: 'u:5fe9a2ffdbe1',
|
||||
// ignoreError: false,
|
||||
// },
|
||||
// {
|
||||
// componentId: 'u:16d9090a3803',
|
||||
// ignoreError: false,
|
||||
// actionType: 'show',
|
||||
// },
|
||||
// {
|
||||
// componentId: 'my_service1',
|
||||
// actionType: 'setValue',
|
||||
// args: { value: '${event.data}' },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
// id: 'u:60093cd4c74f',
|
||||
// level: 'light',
|
||||
// size: 'xs',
|
||||
// },
|
||||
],
|
||||
actions: [],
|
||||
id: 'u:8f1fae00368b',
|
||||
imageClassName: 'w-full h-sm',
|
||||
avatarClassName: 'w-full',
|
||||
},
|
||||
mode: 'cards',
|
||||
},
|
||||
],
|
||||
style: {
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
inset: 'auto',
|
||||
flexWrap: 'nowrap',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
size: 'none',
|
||||
wrapperBody: false,
|
||||
id: 'u:5fe9a2ffdbe1',
|
||||
},
|
||||
{
|
||||
type: 'container',
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '返回',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:5fe9a2ffdbe1',
|
||||
ignoreError: false,
|
||||
actionType: 'show',
|
||||
},
|
||||
{
|
||||
componentId: 'u:16d9090a3803',
|
||||
ignoreError: false,
|
||||
actionType: 'hidden',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
id: 'u:a0f4fc5477c0',
|
||||
themeCss: {
|
||||
className: {
|
||||
'border:default': {
|
||||
'top-border-width': 'none',
|
||||
'left-border-width': 'none',
|
||||
'right-border-width': 'none',
|
||||
'bottom-border-width': 'none',
|
||||
},
|
||||
'font:default': {
|
||||
color: '#36586b',
|
||||
fontWeight: '500',
|
||||
fontSize: '24px',
|
||||
},
|
||||
},
|
||||
},
|
||||
size: 'lg',
|
||||
block: false,
|
||||
level: 'link',
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'service',
|
||||
data: {
|
||||
pid: '490f5308c4bd486fb2225f1e70198768',
|
||||
post: '00_实践总览.ipynb',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'formula',
|
||||
name: 'iframeSrc',
|
||||
formula:
|
||||
'location.origin + "/notebook/" + this.pid + "/" + this.post',
|
||||
id: 'u:479855e2164e',
|
||||
},
|
||||
{
|
||||
type: 'iframe',
|
||||
src: '${iframeSrc}/?token=$user.token',
|
||||
height: '700px',
|
||||
id: 'u:d2171dd0883e',
|
||||
},
|
||||
],
|
||||
id: 'my_service1',
|
||||
dsType: 'api',
|
||||
},
|
||||
],
|
||||
id: 'u:d57881a83b7f',
|
||||
},
|
||||
],
|
||||
style: {
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
inset: 'auto',
|
||||
flexWrap: 'nowrap',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
size: 'none',
|
||||
wrapperBody: false,
|
||||
id: 'u:16d9090a3803',
|
||||
isFixedHeight: false,
|
||||
isFixedWidth: false,
|
||||
hidden: true,
|
||||
visible: false,
|
||||
},
|
||||
],
|
||||
id: 'u:4b1560f3b2db',
|
||||
asideResizor: false,
|
||||
pullRefresh: { disabled: true },
|
||||
definitions: {},
|
||||
};
|
||||
|
||||
export { schema };
|
||||
295
src/pages/schema/centre/computing/homework.schema.ts
Normal file
295
src/pages/schema/centre/computing/homework.schema.ts
Normal file
@@ -0,0 +1,295 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
className: '',
|
||||
submitOnChange: true,
|
||||
submitOnInit: true,
|
||||
reload:
|
||||
'manage_loc?semesterSelect=$semesterSelect&course_id=$course_id&project_id=$project_id&organization_id=$org_id',
|
||||
wrapWithPanel: false,
|
||||
canAccessSuperData: false,
|
||||
mode: 'inline',
|
||||
name: 'filter',
|
||||
body: [
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '学期',
|
||||
name: 'semesterSelect',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/semesters?order=is_open.desc,since.desc',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map((item) => {\r\n return {\r\n label: item.name,\r\n value: item.id,\r\n };\r\n }),\r\n};',
|
||||
},
|
||||
submitOnChange: true,
|
||||
mode: 'inline',
|
||||
inputClassName: 'm-l-xs',
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
selectFirst: true,
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
mode: 'inline',
|
||||
label: '课程',
|
||||
name: 'course_id',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/courses?organization_id=eq.$centre_id&semester_id=eq.$semesterSelect&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}(${item.code})`,\r\n value: "eq.".concat(item.id)\r\n }\r\n })\r\n}',
|
||||
},
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
selectFirst: true,
|
||||
className: 'm-l-sm',
|
||||
inputClassName: 'm-l-xs',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '项目',
|
||||
name: 'project_id',
|
||||
mode: 'inline',
|
||||
clearable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/course2projects?select=projects(id,name)&projects.organization_id=eq.${centre_id}&projects.semester_id=eq.${semesterSelect}&projects.free_schedule=eq.false&course_id=${course_id}',
|
||||
adaptor:
|
||||
'let projects = [];\r\npayload.data.items.filter(item => item.projects !== null).forEach(item => {\r\n if (projects.findIndex(x => x.value === item.projects.id) === -1) {\r\n projects.push({\r\n label: item.projects.name,\r\n value: "eq.".concat(item.projects.id)\r\n });\r\n }\r\n});\r\nreturn {\r\n data: projects\r\n}',
|
||||
requestAdaptor:
|
||||
"if (api.query.course_id === '') {\r\n api.url = api.url.replace('&course_id=', '');\r\n}\r\n\r\nreturn api;",
|
||||
sendOn: 'this.course_id',
|
||||
},
|
||||
checkAll: false,
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
className: 'm-l-sm',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '班级',
|
||||
name: 'org_id',
|
||||
mode: 'inline',
|
||||
size: 'md',
|
||||
clearable: true,
|
||||
searchable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?select=*,course2orgs!inner(*)&type=eq.class&path=cd.root.1&course2orgs.course_id=${course_id}&order=name,code',
|
||||
requestAdaptor:
|
||||
'if (api.query.course2orgs.course_id === "eq.") {\r\n api.url = api.url.replace("&course2orgs[course_id]=eq.", "")\r\n}\r\nif (api.query.course2orgs.course_id === "eq.undefined") {\r\n api.url = api.url.replace("&course2orgs[course_id]=eq.undefined", "")\r\n}',
|
||||
adaptor:
|
||||
'return {\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: "eq." + item.id\r\n }\r\n })\r\n}',
|
||||
sendOn: 'this.course_id',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
lineStyle: 'solid',
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/users_ex1?semester_id=eq.$semesterSelect&name=like.*$keywords*&role=eq.student&course_id=$course_id&project_id=$project_id&organization_id=$organization_id&organization_type=eq.class&order=name,code',
|
||||
data: {
|
||||
page: '$page',
|
||||
perPage: '$perPage',
|
||||
orderBy: '$orderBy',
|
||||
orderDir: '$orderDir',
|
||||
},
|
||||
adaptor: '',
|
||||
requestAdaptor:
|
||||
"api.url = api.url.replace('&semesterSelect=', '&semester_id=eq.')\r\napi.url = api.url.replace('&keywords=' + api.query.keywords, '')\r\n\r\nif (api.query.project_id === '') {\r\n api.url = api.url.replace('&project_id=', '')\r\n}\r\n\r\nif (api.query.organization_id === '') {\r\n api.url = api.url.replace('&organization_id=', '')\r\n}\r\n\r\nif (api.query.id === '') {\r\n api.url = api.url.replace('&id=', '')\r\n}\r\n\r\nif (api.query.orderBy && api.query.orderDir) {\r\n api.url = api.url.replace('&order=name%2Ccode', '');\r\n api.url = api.url.replace('&orderBy=' + api.query.orderBy + '&orderDir=' + api.query.orderDir, '&order=' + api.query.orderBy + '.' + api.query.orderDir);\r\n} else {\r\n api.url = api.url.replace('&orderBy=' + api.query.orderBy + '&orderDir=' + api.query.orderDir, '');\r\n}\r\n\r\nreturn {\r\n ...api\r\n}",
|
||||
sendOn: 'this.course_id',
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
type: 'text',
|
||||
label: '学生',
|
||||
name: 'name',
|
||||
placeholder: '-',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '学号',
|
||||
name: 'code',
|
||||
placeholder: '-',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '班级',
|
||||
name: 'organization_name',
|
||||
placeholder: '-',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '课程',
|
||||
name: 'course_name',
|
||||
placeholder: '-',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '项目',
|
||||
name: 'project_name',
|
||||
placeholder: '-',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '作业',
|
||||
name: 'notebook_name',
|
||||
placeholder: '-',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '作业状态',
|
||||
name: 'status_name',
|
||||
placeholder: '-',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '作业分数',
|
||||
name: 'score',
|
||||
placeholder: '-',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '批阅',
|
||||
level: 'primary',
|
||||
size: 'xs',
|
||||
actionType: 'dialog',
|
||||
visibleOn:
|
||||
'this.status === "notebook_submit" || this.status === "notebook_checked"',
|
||||
dialog: {
|
||||
title: '批阅作业',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/project_scores?on_conflict=user_id,project_id,item',
|
||||
data: {
|
||||
score: '$score',
|
||||
user_id: '$id',
|
||||
project_id: '$project_id',
|
||||
item: 'exp_notebook',
|
||||
},
|
||||
headers: {
|
||||
Prefer: 'resolution=merge-duplicates',
|
||||
},
|
||||
},
|
||||
initApi: {
|
||||
method: 'get',
|
||||
url: 'rest/project_scores?user_id=eq.$id&project_id=eq.$project_id&item=eq.exp_notebook',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'formula',
|
||||
name: 'iframeSrc',
|
||||
formula:
|
||||
'location.origin + "/notebook/" + notebook_pid + "/" + notebook_post',
|
||||
},
|
||||
{
|
||||
type: 'iframe',
|
||||
src: '${iframeSrc}',
|
||||
height: '700px',
|
||||
},
|
||||
{
|
||||
type: 'input-number',
|
||||
label: '分数',
|
||||
name: 'score',
|
||||
required: true,
|
||||
mode: '',
|
||||
value: 0,
|
||||
min: '0',
|
||||
max: '100',
|
||||
step: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: false,
|
||||
closeOnOutside: false,
|
||||
showCloseButton: true,
|
||||
size: 'xl',
|
||||
},
|
||||
placeholder: '-',
|
||||
},
|
||||
],
|
||||
id: 'u:aab0a651813d',
|
||||
label: '操作',
|
||||
placeholder: '-',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
syncLocation: false,
|
||||
pageField: 'page',
|
||||
perPageField: 'perPage',
|
||||
headerToolbar: [],
|
||||
footerToolbar: [
|
||||
{
|
||||
type: 'pagination',
|
||||
},
|
||||
{
|
||||
type: 'switch-per-page',
|
||||
},
|
||||
{
|
||||
type: 'statistics',
|
||||
},
|
||||
],
|
||||
name: 'manage_loc',
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
filter: {
|
||||
title: '',
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'keywords',
|
||||
label: '学生关键字',
|
||||
},
|
||||
{
|
||||
type: 'submit',
|
||||
label: '搜索',
|
||||
actionType: 'submit',
|
||||
id: 'u:decd16bff04d',
|
||||
level: 'primary',
|
||||
},
|
||||
],
|
||||
reload: 'manage_loc?keywords=$keywords',
|
||||
canAccessSuperData: false,
|
||||
wrapWithPanel: false,
|
||||
autoFocus: false,
|
||||
mode: 'inline',
|
||||
},
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
bodyClassName: '',
|
||||
title: '',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
329
src/pages/schema/centre/computing/metric.schema.ts
Normal file
329
src/pages/schema/centre/computing/metric.schema.ts
Normal file
@@ -0,0 +1,329 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'service',
|
||||
body: [
|
||||
{
|
||||
type: 'flex',
|
||||
items: [
|
||||
{
|
||||
type: 'container',
|
||||
id: 'u:cf018a54788b',
|
||||
style: {
|
||||
flexGrow: 1,
|
||||
flexBasis: '0px',
|
||||
flex: '1 1 auto',
|
||||
display: 'flex',
|
||||
position: 'static',
|
||||
flexWrap: 'nowrap',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
isFixedHeight: false,
|
||||
body: [
|
||||
{
|
||||
type: 'progress',
|
||||
id: 'u:9ae23711aada',
|
||||
value: '$cpu',
|
||||
placeholder: '-',
|
||||
progressClassName: '',
|
||||
strokeWidth: 10,
|
||||
map: [
|
||||
{ color: '#28a745', value: 30 },
|
||||
{ color: '#fad733', value: 70 },
|
||||
{ color: '#dc3545', value: 100 },
|
||||
],
|
||||
gapDegree: 75,
|
||||
gapPosition: 'bottom',
|
||||
mode: 'dashboard',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: 'CPU',
|
||||
inline: false,
|
||||
wrapperComponent: '',
|
||||
id: 'u:44a9ee228557',
|
||||
themeCss: {
|
||||
baseControlClassName: {
|
||||
'font:default': { 'text-align': 'center' },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'container',
|
||||
body: [
|
||||
{
|
||||
type: 'progress',
|
||||
id: 'u:2fc35c429a6c',
|
||||
value: '$memory',
|
||||
placeholder: '-',
|
||||
progressClassName: '',
|
||||
strokeWidth: 10,
|
||||
map: [
|
||||
{ color: '#28a745', value: 30 },
|
||||
{ color: '#fad733', value: 70 },
|
||||
{ color: '#dc3545', value: 100 },
|
||||
],
|
||||
gapDegree: 75,
|
||||
gapPosition: 'bottom',
|
||||
mode: 'dashboard',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '内存',
|
||||
inline: false,
|
||||
wrapperComponent: '',
|
||||
id: 'u:fd25e194e7b0',
|
||||
themeCss: {
|
||||
baseControlClassName: {
|
||||
'font:default': { 'text-align': 'center' },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
size: 'none',
|
||||
style: {
|
||||
position: 'static',
|
||||
display: 'flex',
|
||||
flex: '1 1 auto',
|
||||
flexGrow: 1,
|
||||
flexWrap: 'nowrap',
|
||||
alignItems: 'center',
|
||||
flexBasis: '0px',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
wrapperBody: false,
|
||||
isFixedHeight: false,
|
||||
isFixedWidth: false,
|
||||
id: 'u:10e16e97e026',
|
||||
},
|
||||
{
|
||||
type: 'container',
|
||||
body: [
|
||||
{
|
||||
type: 'progress',
|
||||
id: 'u:c1f3e318e1d2',
|
||||
value: '$disk',
|
||||
placeholder: '-',
|
||||
progressClassName: '',
|
||||
strokeWidth: 10,
|
||||
map: [
|
||||
{ color: '#28a745', value: 30 },
|
||||
{ color: '#fad733', value: 70 },
|
||||
{ color: '#dc3545', value: 100 },
|
||||
],
|
||||
gapDegree: 75,
|
||||
gapPosition: 'bottom',
|
||||
mode: 'dashboard',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '磁盘',
|
||||
inline: false,
|
||||
wrapperComponent: '',
|
||||
id: 'u:11b1d296b866',
|
||||
themeCss: {
|
||||
baseControlClassName: {
|
||||
'font:default': { 'text-align': 'center' },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
size: 'none',
|
||||
style: {
|
||||
position: 'static',
|
||||
display: 'flex',
|
||||
flex: '1 1 auto',
|
||||
flexGrow: 1,
|
||||
flexWrap: 'nowrap',
|
||||
alignItems: 'center',
|
||||
flexBasis: '0px',
|
||||
justifyContent: 'center',
|
||||
overflowY: 'visible',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
wrapperBody: false,
|
||||
isFixedHeight: false,
|
||||
isFixedWidth: false,
|
||||
id: 'u:d63bc21af893',
|
||||
},
|
||||
{
|
||||
type: 'container',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '当前驱动版本:${gpu.driver}',
|
||||
inline: true,
|
||||
wrapperComponent: '',
|
||||
id: 'u:b66aa4e0241d',
|
||||
},
|
||||
{
|
||||
type: 'cards',
|
||||
columnsCount: 1,
|
||||
card: {
|
||||
type: 'container',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '${name}',
|
||||
inline: true,
|
||||
wrapperComponent: '',
|
||||
id: 'u:702c8fcd1378',
|
||||
},
|
||||
{
|
||||
type: 'progress',
|
||||
value: '$percent',
|
||||
placeholder: '-',
|
||||
progressClassName: '',
|
||||
strokeWidth: 10,
|
||||
map: [
|
||||
{ color: '#28a745', value: 30 },
|
||||
{ color: '#fad733', value: 70 },
|
||||
{ color: '#dc3545', value: 100 },
|
||||
],
|
||||
gapDegree: 75,
|
||||
gapPosition: 'bottom',
|
||||
mode: 'dashboard',
|
||||
id: 'u:376de1b837de',
|
||||
},
|
||||
],
|
||||
wrapperBody: false,
|
||||
style: {
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flexWrap: 'nowrap',
|
||||
inset: 'auto',
|
||||
justifyContent: 'space-evenly',
|
||||
alignItems: 'center',
|
||||
},
|
||||
themeCss: {
|
||||
baseControlClassName: {
|
||||
'radius:default': {
|
||||
'top-left-border-radius': '6px',
|
||||
'top-right-border-radius': '6px',
|
||||
'bottom-left-border-radius': '6px',
|
||||
'bottom-right-border-radius': '6px',
|
||||
},
|
||||
'boxShadow:default':
|
||||
' 0px 0px 10px 0px var(--colors-neutral-line-8)',
|
||||
'border:default': {
|
||||
'top-border-width': 'var(--borders-width-1)',
|
||||
'left-border-width': 'var(--borders-width-1)',
|
||||
'right-border-width': 'var(--borders-width-1)',
|
||||
'bottom-border-width': 'var(--borders-width-1)',
|
||||
'top-border-style': 'var(--borders-style-1)',
|
||||
'left-border-style': 'var(--borders-style-1)',
|
||||
'right-border-style': 'var(--borders-style-1)',
|
||||
'bottom-border-style': 'var(--borders-style-1)',
|
||||
'top-border-color': '#3be157',
|
||||
'left-border-color': '#3be157',
|
||||
'right-border-color': '#3be157',
|
||||
'bottom-border-color': '#3be157',
|
||||
},
|
||||
'padding-and-margin:default': {
|
||||
paddingTop: '10px',
|
||||
paddingRight: '10px',
|
||||
paddingBottom: '10px',
|
||||
paddingLeft: '10px',
|
||||
},
|
||||
},
|
||||
},
|
||||
id: 'u:537200fb743f',
|
||||
isFixedHeight: false,
|
||||
isFixedWidth: false,
|
||||
},
|
||||
placeholder: '',
|
||||
style: { gutterY: 10 },
|
||||
id: 'u:f886cae9f2af',
|
||||
name: 'gpu.list',
|
||||
className: 'mt-5',
|
||||
},
|
||||
],
|
||||
size: 'none',
|
||||
style: {
|
||||
position: 'static',
|
||||
display: 'block',
|
||||
flex: '1 1 auto',
|
||||
flexGrow: 2,
|
||||
flexBasis: 0,
|
||||
},
|
||||
wrapperBody: false,
|
||||
isFixedHeight: false,
|
||||
isFixedWidth: false,
|
||||
id: 'u:6915a8d9638a',
|
||||
},
|
||||
],
|
||||
style: { position: 'relative', rowGap: '10px', columnGap: '10px' },
|
||||
id: 'u:e4d85d1072cf',
|
||||
},
|
||||
],
|
||||
id: 'u:6399a46b9a99',
|
||||
dsType: 'api',
|
||||
ws: { url: 'ws://peiyun.host.platosoft.org:7080/api/nb/ws/metrics' },
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
syncLocation: false,
|
||||
api: {
|
||||
method: 'get',
|
||||
url: '/hub/users',
|
||||
data: {},
|
||||
messages: {},
|
||||
requestAdaptor: '',
|
||||
adaptor: '',
|
||||
dataType: 'json',
|
||||
},
|
||||
bulkActions: [],
|
||||
itemActions: [],
|
||||
id: 'u:bd445262be74',
|
||||
perPageAvailable: [5, 10, 20, 50, 100],
|
||||
messages: {},
|
||||
listItem: {
|
||||
body: [
|
||||
{
|
||||
name: 'name',
|
||||
label: '用户',
|
||||
type: 'text',
|
||||
id: 'u:83674005c420',
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
value: 1729753248,
|
||||
name: 'last_activity',
|
||||
id: 'u:92420a25ff12',
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '停止',
|
||||
onEvent: { click: { actions: [] } },
|
||||
id: 'u:2384629a386e',
|
||||
level: 'danger',
|
||||
icon: 'fa fa-power-off',
|
||||
},
|
||||
],
|
||||
id: 'u:aaf60b5849b5',
|
||||
},
|
||||
mode: 'list',
|
||||
loadDataOnce: true,
|
||||
matchFunc: '',
|
||||
className: 'mt-10',
|
||||
title: '用户列表',
|
||||
showHeader: false,
|
||||
},
|
||||
],
|
||||
id: 'u:4b1560f3b2db',
|
||||
asideResizor: false,
|
||||
pullRefresh: { disabled: true },
|
||||
};
|
||||
|
||||
export { schema };
|
||||
148
src/pages/schema/centre/computing/model.schema.ts
Normal file
148
src/pages/schema/centre/computing/model.schema.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'dataset-renderer',
|
||||
name: 'storage',
|
||||
bucket: 'model',
|
||||
addFolder: {
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
reload: 'storage',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '新建文件夹',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'storage/v1/object/model/$path/$name/.empty',
|
||||
requestAdaptor:
|
||||
"api.url = api.url.replace('//', '/');return api",
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '文件夹名称',
|
||||
name: 'name',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
},
|
||||
moveFile: {
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
reload: 'storage',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '重命名',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'storage/v1/object/move',
|
||||
data: {
|
||||
bucketId: 'model',
|
||||
sourceKey: '$sourceKey',
|
||||
dest: '$dest',
|
||||
path: '$path',
|
||||
},
|
||||
requestAdaptor:
|
||||
"console.log(api)\r\nreturn {\r\n ...api,\r\n data: {\r\n bucketId: api.data.bucketId,\r\n sourceKey: api.data.sourceKey,\r\n destinationKey: api.data.path === '' ? api.data.dest : api.data.path + '/' + api.data.dest\r\n }\r\n}",
|
||||
},
|
||||
body: [
|
||||
{
|
||||
name: 'dest',
|
||||
type: 'input-text',
|
||||
label: '名称',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
},
|
||||
delFile: {
|
||||
type: 'button',
|
||||
actionType: 'ajax',
|
||||
reload: 'storage',
|
||||
label: false,
|
||||
api: {
|
||||
method: 'delete',
|
||||
url: '/storage/v1/object/model',
|
||||
data: {
|
||||
prefixes: '$prefixes',
|
||||
},
|
||||
headers: {
|
||||
post2rest: false,
|
||||
},
|
||||
},
|
||||
confirmText: '确认删除吗?',
|
||||
},
|
||||
delFiles: {
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
reload: 'storage',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '批量删除文件',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'delete',
|
||||
url: 'storage/v1/object/model',
|
||||
data: { prefixes: '$prefixes', path: '$path' },
|
||||
headers: { post2rest: false },
|
||||
requestAdaptor:
|
||||
"return {\r\n ...api,\r\n data: {\r\n prefixes: api.data.prefixes.map(item => api.data.path === '' ? item : api.data.path + '/' + item)\r\n }\r\n}",
|
||||
},
|
||||
body: [
|
||||
{
|
||||
name: 'prefixes',
|
||||
type: 'checkboxes',
|
||||
label: false,
|
||||
inline: false,
|
||||
checkAll: true,
|
||||
joinValues: false,
|
||||
extractValue: true,
|
||||
required: true,
|
||||
source: {
|
||||
method: 'post',
|
||||
url: '/storage/v1/object/list/model',
|
||||
data: {
|
||||
prefix: '$path',
|
||||
sortBy: {
|
||||
column: 'name',
|
||||
order: 'asc',
|
||||
},
|
||||
},
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.filter(item => item.id && item.name.charAt(0) !== ".").map(item => {\r\n return {\r\n label: item.name,\r\n value: item.name\r\n }\r\n })\r\n}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
,
|
||||
],
|
||||
};
|
||||
|
||||
export { schema };
|
||||
657
src/pages/schema/centre/computing/subject.schema.ts
Normal file
657
src/pages/schema/centre/computing/subject.schema.ts
Normal file
@@ -0,0 +1,657 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/subjects?order=name',
|
||||
data: {
|
||||
name: 'like.%${name}%',
|
||||
},
|
||||
requestAdaptor:
|
||||
"if (api.query.name === 'like.%%') {\r\n api.url = api.url.replace('&name=like.%25%25', '')\r\n}\r\n\r\nreturn api;",
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n ...item,\r\n sname: item.name,\r\n temp: item.content.map(item => {\r\n return {\r\n ...item,\r\n mooc: item.session.filter(item => item.split("-")[1] === "mooc"),\r\n notebook: item.session.filter(item => item.split("-")[1] === "notebook")\r\n }\r\n })\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
align: 'right',
|
||||
label: '添加',
|
||||
icon: 'fa fa-plus',
|
||||
level: 'default',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '添加专题',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
body: [
|
||||
{
|
||||
type: 'control',
|
||||
label: '',
|
||||
mode: 'normal',
|
||||
body: [
|
||||
{
|
||||
type: 'grid',
|
||||
columns: [
|
||||
{
|
||||
body: [
|
||||
{
|
||||
label: '名称',
|
||||
name: 'sname',
|
||||
type: 'input-text',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:f72c2d77e3a4',
|
||||
},
|
||||
],
|
||||
id: 'u:8c6f142936c2',
|
||||
},
|
||||
],
|
||||
id: 'u:c60d1c6c17ae',
|
||||
},
|
||||
],
|
||||
id: 'u:1aab6d05c2f2',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '内容管理',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '内容管理',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
reload: 'subject?content=$content',
|
||||
body: [
|
||||
{
|
||||
label: '内容',
|
||||
type: 'combo',
|
||||
name: 'temp',
|
||||
multiple: true,
|
||||
multiLine: true,
|
||||
items: [
|
||||
{
|
||||
label: '描述',
|
||||
type: 'textarea',
|
||||
name: 'intro',
|
||||
mode: 'normal',
|
||||
id: 'u:86fa1674235c',
|
||||
},
|
||||
{
|
||||
type: 'picker',
|
||||
label: '课程',
|
||||
name: 'mooc',
|
||||
valueField: 'value',
|
||||
labelField: 'name',
|
||||
modalClassName: 'app-popover',
|
||||
id: 'u:c8e1e52ea0b1',
|
||||
modalMode: 'dialog',
|
||||
strictMode: false,
|
||||
multiple: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/mooc?name=${name}&order=name',
|
||||
data: {
|
||||
page: '${page}',
|
||||
perPage: '${perPage}',
|
||||
name: 'like.%${name}%',
|
||||
},
|
||||
requestAdaptor:
|
||||
"if (api.query.name === 'like.%%') {\r\n api.url = api.url.replace('&name=like.%25%25', '')\r\n}\r\n\r\nreturn api;",
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n ...item,\r\n value: item.id + "-" + item.type + "-" + item.name\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
joinValues: false,
|
||||
extractValue: true,
|
||||
embed: true,
|
||||
pickerSchema: {
|
||||
mode: 'table',
|
||||
id: 'u:ae755822f652',
|
||||
perPageAvailable: [10],
|
||||
messages: {},
|
||||
syncLocation: false,
|
||||
columns: [
|
||||
{
|
||||
type: 'text',
|
||||
label: '编号',
|
||||
name: 'code',
|
||||
id: 'u:636f3f0a2ed3',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '名称',
|
||||
name: 'name',
|
||||
searchable: true,
|
||||
id: 'u:636f3f0a2ed3',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'picker',
|
||||
label: '文档',
|
||||
name: 'notebook',
|
||||
valueField: 'value',
|
||||
labelField: 'name',
|
||||
modalClassName: 'app-popover',
|
||||
id: 'u:0a92044686ee',
|
||||
modalMode: 'dialog',
|
||||
strictMode: false,
|
||||
multiple: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/notebooks?name=${name}&author=${author}&order=name',
|
||||
data: {
|
||||
page: '${page}',
|
||||
perPage: '${perPage}',
|
||||
name: 'like.%${name}%',
|
||||
author: 'like.%${author}%',
|
||||
},
|
||||
requestAdaptor:
|
||||
"if (api.query.name === 'like.%%') {\r\n api.url = api.url.replace('&name=like.%25%25', '')\r\n}\r\n\r\nif (api.query.author === 'like.%%') {\r\n api.url = api.url.replace('&author=like.%25%25', '')\r\n}\r\n\r\nreturn api;",
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n ...item,\r\n value: item.id + "-" + item.type + "-" + item.name\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
joinValues: false,
|
||||
extractValue: true,
|
||||
embed: true,
|
||||
pickerSchema: {
|
||||
mode: 'table',
|
||||
id: 'u:ae755822f652',
|
||||
perPageAvailable: [10],
|
||||
messages: {},
|
||||
syncLocation: false,
|
||||
columns: [
|
||||
{
|
||||
type: 'text',
|
||||
label: '名称',
|
||||
name: 'name',
|
||||
searchable: true,
|
||||
id: 'u:636f3f0a2ed3',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '作者',
|
||||
name: 'author',
|
||||
searchable: true,
|
||||
id: 'u:636f3f0a2ed3',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'formula',
|
||||
name: 'content',
|
||||
formula:
|
||||
'data.temp.map(item => { return { intro: item.intro, session: item.mooc.concat(item.notebook).filter(item => item) } })',
|
||||
},
|
||||
],
|
||||
id: 'u:d2e6bd28f354',
|
||||
},
|
||||
],
|
||||
showCloseButton: true,
|
||||
showErrorMsg: true,
|
||||
showLoading: true,
|
||||
className: 'app-popover',
|
||||
id: 'u:b6ff11656496',
|
||||
closeOnEsc: true,
|
||||
size: 'lg',
|
||||
withDefaultData: false,
|
||||
dataMapSwitch: true,
|
||||
data: {
|
||||
'&': '$$',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
id: 'u:739469c288af',
|
||||
level: 'primary',
|
||||
},
|
||||
{
|
||||
type: 'input-array',
|
||||
label: '内容',
|
||||
name: 'content',
|
||||
id: 'u:561926ea3025',
|
||||
items: {
|
||||
type: 'combo',
|
||||
label: '',
|
||||
controls: [
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '介绍',
|
||||
tpl: '$intro',
|
||||
},
|
||||
{
|
||||
type: 'input-array',
|
||||
label: '内容',
|
||||
name: 'session',
|
||||
id: 'u:561926ea3025',
|
||||
items: {
|
||||
type: 'tpl',
|
||||
id: 'u:f95cfcfcc0fb',
|
||||
},
|
||||
addable: false,
|
||||
removable: true,
|
||||
draggable: true,
|
||||
isSlot: true,
|
||||
},
|
||||
],
|
||||
id: 'u:f95cfcfcc0fb',
|
||||
multiLine: false,
|
||||
multiple: false,
|
||||
messages: {},
|
||||
},
|
||||
addable: false,
|
||||
removable: false,
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/subjects',
|
||||
data: {
|
||||
name: '$sname',
|
||||
content: '$content',
|
||||
},
|
||||
dataType: 'json',
|
||||
},
|
||||
name: 'subject',
|
||||
id: 'u:6ddf84e7e06c',
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
id: 'u:2f4c08ecc48e',
|
||||
},
|
||||
name: 'semesterSelect',
|
||||
options: [],
|
||||
checkAll: false,
|
||||
submitOnChange: true,
|
||||
autoComplete: '',
|
||||
mode: 'inline',
|
||||
className: '',
|
||||
id: 'u:8eee4a809327',
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
name: 'plan',
|
||||
footerToolbar: [],
|
||||
affixHeader: true,
|
||||
placeholder: '暂无数据',
|
||||
mode: 'cards',
|
||||
card: {
|
||||
type: 'card',
|
||||
header: {
|
||||
title: '$name',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
label: '',
|
||||
type: 'button-group',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '修改专题',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
data: { '&': '$$', name: '__undefined' },
|
||||
body: [
|
||||
{
|
||||
type: 'control',
|
||||
label: '',
|
||||
mode: 'normal',
|
||||
body: [
|
||||
{
|
||||
type: 'grid',
|
||||
columns: [
|
||||
{
|
||||
body: [
|
||||
{
|
||||
label: '名称',
|
||||
name: 'sname',
|
||||
type: 'input-text',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:f72c2d77e3a4',
|
||||
},
|
||||
],
|
||||
id: 'u:8c6f142936c2',
|
||||
},
|
||||
],
|
||||
id: 'u:c60d1c6c17ae',
|
||||
},
|
||||
],
|
||||
id: 'u:1aab6d05c2f2',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '内容管理',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '内容管理',
|
||||
data: { '&': '$$', name: '__undefined' },
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
reload: 'subject?content=$content',
|
||||
body: [
|
||||
{
|
||||
label: '内容',
|
||||
type: 'combo',
|
||||
name: 'temp',
|
||||
multiple: true,
|
||||
multiLine: true,
|
||||
items: [
|
||||
{
|
||||
label: '描述',
|
||||
type: 'textarea',
|
||||
name: 'intro',
|
||||
mode: 'normal',
|
||||
id: 'u:86fa1674235c',
|
||||
},
|
||||
{
|
||||
type: 'picker',
|
||||
label: '课程',
|
||||
name: 'mooc',
|
||||
valueField: 'value',
|
||||
labelField: 'name',
|
||||
modalClassName: 'app-popover',
|
||||
id: 'u:c8e1e52ea0b1',
|
||||
modalMode: 'dialog',
|
||||
strictMode: false,
|
||||
multiple: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/mooc?name=${name}&order=name',
|
||||
data: {
|
||||
page: '${page}',
|
||||
perPage: '${perPage}',
|
||||
name: 'like.%${name}%',
|
||||
},
|
||||
requestAdaptor:
|
||||
"if (api.query.name === 'like.%%') {\r\n api.url = api.url.replace('&name=like.%25%25', '')\r\n}\r\n\r\nreturn api;",
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n ...item,\r\n value: item.id + "-" + item.type + "-" + item.name\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
joinValues: false,
|
||||
extractValue: true,
|
||||
embed: true,
|
||||
pickerSchema: {
|
||||
mode: 'table',
|
||||
id: 'u:ae755822f652',
|
||||
perPageAvailable: [10],
|
||||
messages: {},
|
||||
syncLocation: false,
|
||||
columns: [
|
||||
{
|
||||
type: 'text',
|
||||
label: '编号',
|
||||
name: 'code',
|
||||
id: 'u:636f3f0a2ed3',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '名称',
|
||||
name: 'name',
|
||||
searchable: true,
|
||||
id: 'u:636f3f0a2ed3',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'picker',
|
||||
label: '文档',
|
||||
name: 'notebook',
|
||||
valueField: 'value',
|
||||
labelField: 'name',
|
||||
modalClassName: 'app-popover',
|
||||
id: 'u:0a92044686ee',
|
||||
modalMode: 'dialog',
|
||||
strictMode: false,
|
||||
multiple: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/notebooks?name=${name}&author=${author}&order=name',
|
||||
data: {
|
||||
page: '${page}',
|
||||
perPage: '${perPage}',
|
||||
name: 'like.%${name}%',
|
||||
author: 'like.%${author}%',
|
||||
},
|
||||
requestAdaptor:
|
||||
"if (api.query.name === 'like.%%') {\r\n api.url = api.url.replace('&name=like.%25%25', '')\r\n}\r\n\r\nif (api.query.author === 'like.%%') {\r\n api.url = api.url.replace('&author=like.%25%25', '')\r\n}\r\n\r\nreturn api;",
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n ...item,\r\n value: item.id + "-" + item.type + "-" + item.name\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
joinValues: false,
|
||||
extractValue: true,
|
||||
embed: true,
|
||||
pickerSchema: {
|
||||
mode: 'table',
|
||||
id: 'u:ae755822f652',
|
||||
perPageAvailable: [10],
|
||||
messages: {},
|
||||
syncLocation: false,
|
||||
columns: [
|
||||
{
|
||||
type: 'text',
|
||||
label: '名称',
|
||||
name: 'name',
|
||||
searchable: true,
|
||||
id: 'u:636f3f0a2ed3',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '作者',
|
||||
name: 'author',
|
||||
searchable: true,
|
||||
id: 'u:636f3f0a2ed3',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'formula',
|
||||
name: 'content',
|
||||
formula:
|
||||
'data.temp.map(item => { return { intro: item.intro, session: item.mooc.concat(item.notebook).filter(item => item) } })',
|
||||
},
|
||||
],
|
||||
id: 'u:d2e6bd28f354',
|
||||
},
|
||||
],
|
||||
showCloseButton: true,
|
||||
showErrorMsg: true,
|
||||
showLoading: true,
|
||||
className: 'app-popover',
|
||||
id: 'u:b6ff11656496',
|
||||
closeOnEsc: true,
|
||||
size: 'lg',
|
||||
withDefaultData: false,
|
||||
dataMapSwitch: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
id: 'u:739469c288af',
|
||||
level: 'primary',
|
||||
},
|
||||
{
|
||||
type: 'input-array',
|
||||
label: '内容',
|
||||
name: 'content',
|
||||
id: 'u:561926ea3025',
|
||||
items: {
|
||||
type: 'combo',
|
||||
label: '',
|
||||
controls: [
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '介绍',
|
||||
tpl: '$intro',
|
||||
},
|
||||
{
|
||||
type: 'input-array',
|
||||
label: '内容',
|
||||
name: 'session',
|
||||
id: 'u:561926ea3025',
|
||||
items: {
|
||||
type: 'tpl',
|
||||
id: 'u:f95cfcfcc0fb',
|
||||
},
|
||||
addable: false,
|
||||
removable: true,
|
||||
draggable: true,
|
||||
isSlot: true,
|
||||
},
|
||||
],
|
||||
id: 'u:f95cfcfcc0fb',
|
||||
multiLine: false,
|
||||
multiple: false,
|
||||
messages: {},
|
||||
},
|
||||
addable: false,
|
||||
removable: false,
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/subjects/${id}',
|
||||
data: {
|
||||
name: '$sname',
|
||||
content: '$content',
|
||||
},
|
||||
dataType: 'json',
|
||||
},
|
||||
name: 'subject',
|
||||
id: 'u:6ddf84e7e06c',
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
size: 'md',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
icon: 'fa fa-pencil text-info',
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
target: '',
|
||||
block: false,
|
||||
tooltip: '修改专题',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
actionType: 'ajax',
|
||||
icon: 'fa fa-trash text-danger',
|
||||
iconClassName: 'pull-left',
|
||||
confirmText: '确认删除"${name}"?',
|
||||
api: {
|
||||
method: 'delete',
|
||||
url: 'rest/subjects/${id}',
|
||||
},
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
tooltip: '删除专题',
|
||||
tooltipPlacement: 'top',
|
||||
className: 'p-r-none p-l-none',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
label: '',
|
||||
},
|
||||
{
|
||||
type: 'tabs',
|
||||
label: '',
|
||||
tabs: [
|
||||
{
|
||||
title: '内容',
|
||||
body: [
|
||||
{
|
||||
type: 'list',
|
||||
listItem: {
|
||||
body: [
|
||||
{
|
||||
type: 'html',
|
||||
html: '$intro',
|
||||
wrapperComponent: '',
|
||||
id: 'u:d6c7aa76d24f',
|
||||
},
|
||||
{
|
||||
type: 'input-array',
|
||||
name: 'session',
|
||||
items: {
|
||||
type: 'html',
|
||||
},
|
||||
addable: false,
|
||||
removable: false,
|
||||
},
|
||||
],
|
||||
actions: [],
|
||||
id: 'u:1ddb9863eba8',
|
||||
},
|
||||
id: 'u:6a6d7608b012',
|
||||
source: '$content',
|
||||
placeholder: '没有数据',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
className: 'tab',
|
||||
},
|
||||
],
|
||||
actions: [],
|
||||
actionsCount: 4,
|
||||
},
|
||||
columnsCount: 1,
|
||||
loadDataOnce: true,
|
||||
masonryLayout: false,
|
||||
itemClassName: '',
|
||||
checkAll: false,
|
||||
id: 'u:f2d78d4b8af6',
|
||||
},
|
||||
],
|
||||
cssVars: {
|
||||
'--Form-item-gap': 0,
|
||||
},
|
||||
headerToolbar: [],
|
||||
id: 'u:9891040f8810',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
1453
src/pages/schema/centre/data/index.schema.ts
Normal file
1453
src/pages/schema/centre/data/index.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
850
src/pages/schema/centre/data/task.schema.ts
Normal file
850
src/pages/schema/centre/data/task.schema.ts
Normal file
@@ -0,0 +1,850 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'tabs',
|
||||
className: '',
|
||||
tabs: [
|
||||
{
|
||||
title: '日程一览',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
className: '',
|
||||
submitOnChange: true,
|
||||
submitOnInit: true,
|
||||
reload:
|
||||
'manage_loc?course_id=$course_id&project_id=$project_id&teacher_id=$teacher_id&location_id=$location_id&date=$date&no_tch=$no_tch&public=$public&semesterSelect=$semesterSelect',
|
||||
initApi: {
|
||||
method: 'get',
|
||||
url: 'rest/users?role=eq.teacher&id=eq.${user.id}&order=code',
|
||||
requestAdaptor: '',
|
||||
adaptor:
|
||||
"if (payload.data.items.length !== 0) {\r\n return {\r\n teacher_id: 'eq.'.concat(payload.data.items[0].id)\r\n }\r\n} else {\r\n return {}\r\n}",
|
||||
},
|
||||
wrapWithPanel: false,
|
||||
mode: 'inline',
|
||||
name: 'filter',
|
||||
body: [
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '历史',
|
||||
name: 'semesterSelect',
|
||||
mode: 'inline',
|
||||
clearable: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/semesters?order=is_open.desc,since.desc',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
checkAll: false,
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
options: [],
|
||||
id: 'u:66e672b9a4eb',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '课程',
|
||||
name: 'course_id',
|
||||
mode: 'inline',
|
||||
clearable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/courses?organization_id=eq.$centre_id&semester_id=eq.$semesterSelect&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}(${item.code})`,\r\n value: "eq.".concat(item.id)\r\n }\r\n })\r\n}',
|
||||
},
|
||||
checkAll: false,
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
id: 'u:834c027e3f4a',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
mode: 'inline',
|
||||
label: '项目',
|
||||
name: 'project_id',
|
||||
clearable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/projects?organization_id=eq.$centre_id&semester_id=eq.$semesterSelect&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: "eq.".concat(item.id)\r\n }\r\n })\r\n}',
|
||||
},
|
||||
checkAll: false,
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
className: 'm-l-sm',
|
||||
inputClassName: 'm-l-xs',
|
||||
id: 'u:ce7be11036a7',
|
||||
},
|
||||
],
|
||||
id: 'u:5014fc9e9daf',
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'input-date-range',
|
||||
label: '日期',
|
||||
name: 'date',
|
||||
mode: 'inline',
|
||||
clearable: true,
|
||||
value: '',
|
||||
format: 'YYYY-MM-DD',
|
||||
ranges:
|
||||
'yesterday,today,7daysago,prevweek,thismonth,prevmonth,prevquarter',
|
||||
inputClassName: 'm-l-xs',
|
||||
id: 'u:105a2c1566be',
|
||||
},
|
||||
{
|
||||
label: '地点',
|
||||
type: 'select',
|
||||
name: 'location_id',
|
||||
mode: 'inline',
|
||||
clearable: true,
|
||||
checkAll: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/locations?organization_id=eq.$centre_id&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: "eq.".concat(item.id)\r\n }\r\n })\r\n}',
|
||||
},
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
className: 'm-l-sm',
|
||||
id: 'u:da7fb6289bbf',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'teacher_id',
|
||||
mode: 'inline',
|
||||
label: '教师',
|
||||
clearable: true,
|
||||
checkAll: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/users?role=eq.teacher&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}(${item.code})`,\r\n value: "eq.".concat(item.id)\r\n }\r\n })\r\n}',
|
||||
},
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
className: 'm-l-sm',
|
||||
id: 'u:9945a410d682',
|
||||
},
|
||||
],
|
||||
id: 'u:7ef4cbfbbc4c',
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'switch',
|
||||
name: 'no_tch',
|
||||
option: '显示无教师场次',
|
||||
mode: 'inline',
|
||||
optionAtLeft: false,
|
||||
trueValue: 1,
|
||||
falseValue: 0,
|
||||
value: 0,
|
||||
inputClassName: '',
|
||||
id: 'u:e9b5ab665ddf',
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
name: 'public',
|
||||
option: '显示公共场次',
|
||||
mode: 'inline',
|
||||
optionAtLeft: false,
|
||||
trueValue: 1,
|
||||
falseValue: 0,
|
||||
value: 0,
|
||||
inputClassName: 'm-l-sm',
|
||||
id: 'u:984816e1593e',
|
||||
},
|
||||
],
|
||||
id: 'u:1de38c7bb180',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
lineStyle: 'solid',
|
||||
id: 'u:f99b4d53b5dc',
|
||||
},
|
||||
],
|
||||
id: 'u:ca42a8e96c02',
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/schedule_stats?organization_id=eq.$centre_id&semester_id=eq.$semesterSelect&is_publish=is.true&order=project_name,date,period_name,location_name,teacher_name',
|
||||
data: {
|
||||
page: '$page',
|
||||
perPage: '$perPage',
|
||||
'&': '$$',
|
||||
},
|
||||
adaptor:
|
||||
"let supportDateTimeFormat = typeof(Intl.DateTimeFormat) === 'function';\r\npayload.data.items.forEach(item => {\r\n item.day = supportDateTimeFormat ? `${item.date}(${new Intl.DateTimeFormat('zh-CN', { weekday: 'short'}).format(new Date(item.date))})` : item.date;\r\n let att_count = item.att_y_count\r\n + item.att_y_late_count\r\n + item.att_y_unclean_count\r\n item.att_num = \"\".concat(\r\n att_count, \"/\", item.current_student_number\r\n )\r\n item.data_num = \"\".concat(\r\n item.data_count, '/', att_count\r\n )\r\n item.data_num_status =\r\n item.data_count === att_count\r\n ? 1\r\n : 0\r\n item.stu_title = item.project_name.concat(\r\n ' :: ', item.date, ' ', item.period_name,\r\n ' :: ', item.location_name,\r\n ' :: ', item.teacher_name ? item.teacher_name : '无教师认领'\r\n )\r\n})\r\n\r\nreturn {\r\n ...payload\r\n}",
|
||||
requestAdaptor:
|
||||
"if (api.query.semesterSelect) {\r\n api.url = api.url.replace(/&semesterSelect=\\d*/g, '')\r\n api.url = api.url.replace(/(?<=semester_id=eq\\.)\\d*(?=&)/g, api.query.semesterSelect)\r\n} else {\r\n api.url = api.url.replace(/&semesterSelect=/g, '')\r\n}\r\nif (api.body.course_id === '') {\r\n api.url = api.url.replace(/&course_id=/g, '')\r\n}\r\nif (api.body.project_id === '') {\r\n api.url = api.url.replace(/&project_id=/g, '')\r\n}\r\nif (api.body.date === '') {\r\n api.url = api.url.replace(/&date=/g, '')\r\n}\r\nif (api.body.location_id === '') {\r\n api.url = api.url.replace(/&location_id=/g, '')\r\n}\r\nif (api.body.teacher_id === '') {\r\n api.url = api.url.replace(/&teacher_id=/g, '')\r\n}\r\nif (api.body.no_tch === 0) {\r\n api.url = api.url.replace(/&no_tch=0/g, '')\r\n}\r\nif (api.body.public === 0) {\r\n api.url = api.url.replace(/&public=0/g, '')\r\n}\r\nconst pattern = /(?:date=)(\\d+-\\d+-\\d+)%2C(\\d+-\\d+-\\d+)/g\r\nconst result = pattern.exec(api.url)\r\nif (result && result[1] === result[2]) {\r\n api.url = api.url.replace(result[0], 'date=eq.'.concat(result[1]))\r\n} else if (result) {\r\n api.url = api.url.replace(result[0], 'date=gte.'.concat(result[1], '&date=lte.', result[2]))\r\n}\r\nconst is_no_tch = /no_tch=1/g.test(api.url)\r\nconst has_teacher = /teacher_id=.+?(?=&)/g.test(api.url)\r\nif (is_no_tch && has_teacher) {\r\n const teacher = /teacher_id=.+?(?=&)/g.exec(api.url)\r\n api.url = api.url.replace(teacher, 'or=('.concat(teacher, ',teacher_id.is.null)')).replace('teacher_id=', 'teacher_id.')\r\n api.url = api.url.replace(/no_tch=1/g, '')\r\n} else if (is_no_tch && !has_teacher) {\r\n api.url = api.url.replace(/no_tch=1/g, 'teacher_id=is.null')\r\n}\r\nconst is_public = /public=1/g.test(api.url)\r\nconst has_course = /course_id=.+?(?=&)/g.test(api.url)\r\nif (is_public && has_course) {\r\n const course = /course_id=.+?(?=&)/g.exec(api.url)\r\n api.url = api.url.replace(course, 'or=('.concat(course, ',course_id.is.null)')).replace('course_id=', 'course_id.')\r\n api.url = api.url.replace(/public=1/g, '')\r\n} else if (is_public && !has_course) {\r\n api.url = api.url.replace(/public=1/g, 'course_id=is.null')\r\n}\r\nreturn {\r\n ...api\r\n}",
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '项目',
|
||||
tpl: '<% if(!this.course_id) { %>\n <span class="label label-info">公共</span><span> <%= this.project_name %> <span>\n<% } else {%>\n <span> <%= this.project_name %> <span>\n<% } %>',
|
||||
inline: false,
|
||||
placeholder: '-',
|
||||
id: 'u:a41eff5b6b05',
|
||||
},
|
||||
{
|
||||
name: 'day',
|
||||
label: '日期',
|
||||
type: 'text',
|
||||
placeholder: '-',
|
||||
id: 'u:d4c925b1426d',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '节次',
|
||||
name: 'period_name',
|
||||
placeholder: '-',
|
||||
id: 'u:97591d821213',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '地点',
|
||||
name: 'location_name',
|
||||
placeholder: '-',
|
||||
id: 'u:d15f6350ec5c',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '教师',
|
||||
name: 'teacher_name',
|
||||
placeholder: '-',
|
||||
id: 'u:bb4bb20fe052',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '考勤数',
|
||||
placeholder: '-',
|
||||
name: 'att_num',
|
||||
id: 'u:d25f22581135',
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '查看学生',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '学生信息',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/user2projects?select=*,att_status_text:user2project_att_status_dict_fk(dictvalue),schedule_status:user2project_schedule_status_dict_fk(dictvalue),student:users!user2project_student_id_fkey(*),projects(*)&schedule_id=eq.${id}&schedule_status=neq.canceled&order=id.asc',
|
||||
data: {},
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map((item,index) => {\r\n return {\r\n ...item,\r\n row_number: index+1\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
label: '序号',
|
||||
type: 'plain',
|
||||
inline: false,
|
||||
name: 'row_number',
|
||||
tpl: '',
|
||||
},
|
||||
{
|
||||
name: 'schedule_status.dictvalue',
|
||||
label: '选课状态',
|
||||
type: 'plain',
|
||||
placeholder: '-',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
name: 'student.name',
|
||||
label: '学生',
|
||||
type: 'text',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '学号',
|
||||
name: 'student.code',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '考勤状态',
|
||||
name: 'att_status_text.dictvalue',
|
||||
placeholder: '-',
|
||||
groupName: '',
|
||||
remark: '',
|
||||
},
|
||||
{
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '查看',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '$student.name($student.code)',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
syncLocation: false,
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/tasks?select=*,dict_status:dicts!task_status_fkey(*),dict_type:dicts!task_type_fkey(*)&schedule_id=eq.$schedule_id&user_id=eq.$student.id',
|
||||
messages: {},
|
||||
requestAdaptor: '',
|
||||
adaptor: '',
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
name: 'content',
|
||||
label: '任务',
|
||||
type: 'text',
|
||||
id: 'u:b7848b797bf9',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
name: 'dict_type.dictvalue',
|
||||
label: '类型',
|
||||
type: 'text',
|
||||
id: 'u:f7ed595f5c57',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
wrapperComponent: '',
|
||||
id: 'u:80864ebd90a9',
|
||||
label: '状态',
|
||||
placeholder: '-',
|
||||
name: 'dict_status.dictvalue',
|
||||
},
|
||||
],
|
||||
bulkActions: [],
|
||||
itemActions: [],
|
||||
id: 'u:3458736acc74',
|
||||
perPageAvailable: [10],
|
||||
messages: {},
|
||||
},
|
||||
],
|
||||
size: 'lg',
|
||||
actions: [],
|
||||
closeOnEsc: true,
|
||||
closeOnOutside: false,
|
||||
showCloseButton: true,
|
||||
id: 'u:bc2fcc411e2a',
|
||||
},
|
||||
size: 'xs',
|
||||
level: 'primary',
|
||||
closeOnEsc: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '${stu_title}',
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
affixHeader: false,
|
||||
bodyClassName: '',
|
||||
className: '',
|
||||
perPageAvailable: [10],
|
||||
name: 'schedule_data',
|
||||
silentPolling: true,
|
||||
},
|
||||
],
|
||||
size: 'lg',
|
||||
actions: [],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
closeOnOutside: false,
|
||||
},
|
||||
level: 'primary',
|
||||
size: 'xs',
|
||||
id: 'u:a05b0e0b829a',
|
||||
},
|
||||
],
|
||||
placeholder: '-',
|
||||
id: 'u:8bd7633bd4c7',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
syncLocation: false,
|
||||
pageField: 'page',
|
||||
perPageField: 'perPage',
|
||||
headerToolbar: [],
|
||||
footerToolbar: [
|
||||
{
|
||||
type: 'pagination',
|
||||
},
|
||||
{
|
||||
type: 'switch-per-page',
|
||||
},
|
||||
{
|
||||
type: 'statistics',
|
||||
},
|
||||
],
|
||||
name: 'manage_loc',
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
id: 'u:6488ee7bd91a',
|
||||
},
|
||||
],
|
||||
className: 'p-t-none',
|
||||
id: 'u:66531bc383d6',
|
||||
},
|
||||
{
|
||||
title: '学生一览',
|
||||
body: [
|
||||
{
|
||||
type: 'grid',
|
||||
columns: [
|
||||
{
|
||||
md: 3,
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
reload:
|
||||
'stu?parent=${tree}&tree=${tree}&org_name=${name}&type=${type}&filter=${filter}',
|
||||
data: {
|
||||
parentId: 1,
|
||||
},
|
||||
wrapWithPanel: false,
|
||||
title: '表单',
|
||||
submitOnChange: true,
|
||||
target: '',
|
||||
resetAfterSubmit: false,
|
||||
initApi: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?type=eq.school',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n tree: payload.data.id\r\n }\r\n}',
|
||||
headers: {
|
||||
Accept: 'application/vnd.pgrst.object+json',
|
||||
},
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'service',
|
||||
name: 'service',
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs/${tree}',
|
||||
sendOn: 'this.tree',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '部门类型',
|
||||
name: 'type',
|
||||
visibleOn: 'false',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '部门名称',
|
||||
name: 'name',
|
||||
visibleOn: 'false',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '部门Path',
|
||||
name: 'path',
|
||||
visibleOn: 'false',
|
||||
value: 'root',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '过滤条件',
|
||||
name: 'filter',
|
||||
visibleOn: 'false',
|
||||
},
|
||||
{
|
||||
type: 'formula',
|
||||
name: 'filter',
|
||||
formula:
|
||||
"'org_path.cd.' + data.path + '.' + data.tree + ',org_id.eq.' + data.tree",
|
||||
condition: 'data.tree',
|
||||
},
|
||||
{
|
||||
type: 'formula',
|
||||
name: 'filter',
|
||||
formula: "'org_path.cd.root'",
|
||||
condition: '!data.tree',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'input-tree',
|
||||
label: '',
|
||||
name: 'tree',
|
||||
inputClassName: 'partment-height',
|
||||
creatable: false,
|
||||
removable: false,
|
||||
editable: false,
|
||||
deleteApi: 'rest/orgs/$id',
|
||||
rootCreatable: false,
|
||||
unfoldedLevel: 1,
|
||||
initiallyOpen: false,
|
||||
virtualThreshold: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?select=*&path=cd.root&order=code',
|
||||
adaptor:
|
||||
"const recursive = id => {\r\n let nodes = payload.data.items.filter(x => {\r\n return x.parent === id\r\n })\r\n return nodes.map(item => {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: `${item.id}`,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}\r\nlet school = payload.data.items.find(x => x.type === 'school');\r\nreturn {\r\n ...payload,\r\n data: [{\r\n label: school.name,\r\n value: `${school.id}`,\r\n children: recursive(school.id)\r\n }]\r\n}",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
md: 9,
|
||||
body: [
|
||||
{
|
||||
name: 'header',
|
||||
type: 'wrapper',
|
||||
body: [
|
||||
{
|
||||
type: 'service',
|
||||
name: 'service',
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs/${tree}',
|
||||
sendOn: 'this.tree',
|
||||
adaptor:
|
||||
"let role_items = [];\r\nif(payload.data.type === '')\r\nreturn {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n role_items: payload.data.\r\n }\r\n}",
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '部门类型',
|
||||
name: 'type',
|
||||
visibleOn: 'false',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '部门名称',
|
||||
name: 'name',
|
||||
visibleOn: 'false',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
name: 'stu',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/users_ex?and=(or(${filter}),or(code.like.*${keywords}*,name.like.*${keywords}*),role.eq.student)&org_type=eq.class&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n ...item,\r\n stu_id: item.id\r\n }\r\n })\r\n }\r\n}',
|
||||
data: {
|
||||
page: '$page',
|
||||
perPage: '${perPage}',
|
||||
},
|
||||
requestAdaptor:
|
||||
'api.url = api.url.replace(/and=([^&]*?)&/g, (match, p1) => {\r\n let encodeAnd = window.btoa(p1);\r\n\treturn `encodeAnd=${encodeAnd}&`;\r\n});',
|
||||
sendOn: 'this.filter',
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
name: 'code',
|
||||
type: 'text',
|
||||
label: '账号',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '姓名',
|
||||
name: 'name',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '部门',
|
||||
name: 'org_name',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '查看场次',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '场次信息',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/user2projects?select=*,courses(id,name),projects(id,code,name,free_schedule),att_status_text:user2project_att_status_dict_fk(dictvalue),schedule_status:user2project_schedule_status_dict_fk(dictvalue)&user_id=eq.$id&schedule_status=neq.canceled&semester_id=eq.$currentSemester&order=created_at.asc',
|
||||
data: {
|
||||
page: '$page',
|
||||
perPage: '$perPage',
|
||||
},
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
name: 'schedule_status.dictvalue',
|
||||
label: '选课状态',
|
||||
type: 'plain',
|
||||
placeholder: '-',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
name: 'courses.name',
|
||||
label: '课程',
|
||||
type: 'text',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '项目',
|
||||
name: 'projects.name',
|
||||
placeholder: '-',
|
||||
tpl: '<% if(this.projects && this.projects.free_schedule) { %>\n <span class="label label-info">自由安排</span><span><%= this.projects.name %></span>\n<% } else if(this.projects) { %>\n <span><%= this.projects.name %></span>\n<% } %>',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '考勤状态',
|
||||
name: 'att_status_text.dictvalue',
|
||||
placeholder: '-',
|
||||
groupName: '',
|
||||
remark: '',
|
||||
},
|
||||
{
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '查看',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
syncLocation: false,
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/tasks?select=*,dict_status:dicts!task_status_fkey(*),dict_type:dicts!task_type_fkey(*)&schedule_id=eq.$schedule_id&user_id=eq.$user_id',
|
||||
messages: {},
|
||||
requestAdaptor: '',
|
||||
adaptor: '',
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
name: 'content',
|
||||
label: '任务',
|
||||
type: 'text',
|
||||
id: 'u:b7848b797bf9',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
name: 'dict_type.dictvalue',
|
||||
label: '类型',
|
||||
type: 'text',
|
||||
id: 'u:f7ed595f5c57',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
wrapperComponent: '',
|
||||
id: 'u:80864ebd90a9',
|
||||
label: '状态',
|
||||
placeholder: '-',
|
||||
name: 'dict_status.dictvalue',
|
||||
},
|
||||
],
|
||||
bulkActions: [],
|
||||
itemActions: [],
|
||||
id: 'u:3458736acc74',
|
||||
perPageAvailable: [10],
|
||||
messages: {},
|
||||
},
|
||||
],
|
||||
size: 'lg',
|
||||
actions: [],
|
||||
closeOnEsc: true,
|
||||
closeOnOutside: false,
|
||||
showCloseButton: true,
|
||||
id: 'u:bc2fcc411e2a',
|
||||
},
|
||||
size: 'xs',
|
||||
level: 'primary',
|
||||
closeOnEsc: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
syncLocation: false,
|
||||
affixHeader: false,
|
||||
bodyClassName: '',
|
||||
className: '',
|
||||
perPageAvailable: [10],
|
||||
},
|
||||
],
|
||||
actions: [],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
},
|
||||
level: 'primary',
|
||||
size: 'xs',
|
||||
},
|
||||
],
|
||||
placeholder: '-',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
className: '',
|
||||
headerToolbar: [],
|
||||
md: 9,
|
||||
syncLocation: false,
|
||||
footerToolbar: [
|
||||
{
|
||||
type: 'form',
|
||||
wrapWithPanel: false,
|
||||
reload: 'stu?perPage=${perPage}',
|
||||
submitOnChange: true,
|
||||
submitOnInit: false,
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
name: 'perPage',
|
||||
value: '10',
|
||||
options: [
|
||||
{
|
||||
label: '默认',
|
||||
value: '17',
|
||||
},
|
||||
{
|
||||
label: '10',
|
||||
value: '10',
|
||||
},
|
||||
{
|
||||
label: '20',
|
||||
value: '20',
|
||||
},
|
||||
{
|
||||
label: '50',
|
||||
value: '50',
|
||||
},
|
||||
{
|
||||
label: '100',
|
||||
value: '100',
|
||||
},
|
||||
{
|
||||
label: '500',
|
||||
value: '500',
|
||||
},
|
||||
],
|
||||
label: '每页显示',
|
||||
mode: 'inline',
|
||||
checkAll: false,
|
||||
className: 'm-t-sm',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'pagination',
|
||||
},
|
||||
],
|
||||
perPageAvailable: '',
|
||||
columnsTogglable: 'auto',
|
||||
filter: {
|
||||
title: '',
|
||||
affixFooter: false,
|
||||
actions: [],
|
||||
body: [
|
||||
{
|
||||
type: 'input-group',
|
||||
name: 'keywords',
|
||||
label: '学号或姓名 ',
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
placeholder: '',
|
||||
inputClassName: 'b-r-none p-r-none',
|
||||
name: 'keywords',
|
||||
size: 'md',
|
||||
clearable: true,
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
type: 'control',
|
||||
label: '',
|
||||
body: [
|
||||
{
|
||||
type: 'submit',
|
||||
level: 'primary',
|
||||
actionType: 'submit',
|
||||
label: '搜索',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
bulkActions: [],
|
||||
},
|
||||
],
|
||||
size: 'xs',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
className: 'partment-grid',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
tabsMode: 'line',
|
||||
tabClassName: '',
|
||||
id: 'u:4f5cf5424791',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
bodyClassName: '',
|
||||
title: '',
|
||||
id: 'u:976cfa59becd',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
1193
src/pages/schema/centre/data/template.schema.ts
Normal file
1193
src/pages/schema/centre/data/template.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
92
src/pages/schema/centre/evaluation/params.schema.ts
Normal file
92
src/pages/schema/centre/evaluation/params.schema.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
const schema = {
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/config?key=eq.evaluation&organization_id=eq.$centre_id',
|
||||
data: {
|
||||
evaluationInstruStatus: '$evaluationInstruStatus',
|
||||
evaluationInstru: '$evaluationInstru',
|
||||
evaluationIntroStatus: '$evaluationIntroStatus',
|
||||
evaluationIntro: '$evaluationIntro',
|
||||
},
|
||||
dataType: 'json',
|
||||
requestAdaptor:
|
||||
'return {\r\n ...api,\r\n data: {\r\n value: api.data\r\n }\r\n}',
|
||||
},
|
||||
name: 'form',
|
||||
title: '',
|
||||
wrapWithPanel: true,
|
||||
initApi: {
|
||||
method: 'get',
|
||||
url: 'rest/config?key=eq.evaluation&organization_id=eq.$centre_id',
|
||||
adaptor:
|
||||
'let item = payload.data.items[0]\r\nreturn {\r\n data: {\r\n ...item.value\r\n }\r\n}',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'fieldset',
|
||||
title: '评教指引及评教说明配置',
|
||||
collapsable: true,
|
||||
body: [
|
||||
{
|
||||
type: 'switch',
|
||||
name: 'evaluationInstruStatus',
|
||||
value: true,
|
||||
option: '评教指引',
|
||||
optionAtLeft: false,
|
||||
trueValue: true,
|
||||
falseValue: false,
|
||||
onText: '开启',
|
||||
offText: '关闭',
|
||||
},
|
||||
{
|
||||
type: 'input-rich-text',
|
||||
label: '',
|
||||
name: 'evaluationInstru',
|
||||
mode: 'normal',
|
||||
visibleOn: '',
|
||||
clearValueOnHidden: false,
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
option: '评教说明',
|
||||
name: 'evaluationIntroStatus',
|
||||
optionAtLeft: false,
|
||||
trueValue: true,
|
||||
falseValue: false,
|
||||
value: true,
|
||||
onText: '开启',
|
||||
offText: '关闭',
|
||||
},
|
||||
{
|
||||
type: 'input-rich-text',
|
||||
name: 'evaluationIntro',
|
||||
label: '',
|
||||
mode: 'normal',
|
||||
visibleOn: '',
|
||||
clearValueOnHidden: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
submitText: '保存',
|
||||
submitOnChange: false,
|
||||
submitOnInit: false,
|
||||
reload: '',
|
||||
affixFooter: false,
|
||||
},
|
||||
],
|
||||
type: 'page',
|
||||
messages: {},
|
||||
title: '评教参数设置',
|
||||
toolbar: [
|
||||
{ type: 'submit', label: '保存', level: 'primary', target: 'form' },
|
||||
],
|
||||
bodyClassName: '',
|
||||
style: {},
|
||||
subTitle: '设置评教指引、评教说明等参数',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
789
src/pages/schema/centre/evaluation/settings.schema.ts
Normal file
789
src/pages/schema/centre/evaluation/settings.schema.ts
Normal file
@@ -0,0 +1,789 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
name: 'eval',
|
||||
id: 'u:1eda9b4880ff',
|
||||
columns: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'parent_name',
|
||||
label: '一级指标',
|
||||
id: 'u:6dbfe25ccbea',
|
||||
placeholder: '-',
|
||||
quickEdit: {
|
||||
mode: 'popOver',
|
||||
saveImmediately: {
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/teaching_evaluation_items/$id',
|
||||
data: {
|
||||
name: '$parent_name',
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'input-text',
|
||||
name: 'parent_name',
|
||||
size: 'lg',
|
||||
},
|
||||
quickEditEnabledOn: '!this.parent',
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
label: '二级指标',
|
||||
id: 'u:a42f65cf3947',
|
||||
placeholder: '-',
|
||||
quickEdit: {
|
||||
mode: 'popOver',
|
||||
type: 'input-text',
|
||||
label: '',
|
||||
name: 'name',
|
||||
saveImmediately: {
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/teaching_evaluation_items/$id',
|
||||
data: {
|
||||
name: '$name',
|
||||
},
|
||||
},
|
||||
},
|
||||
size: 'lg',
|
||||
},
|
||||
quickEditEnabledOn: 'this.parent',
|
||||
},
|
||||
{
|
||||
name: 'order',
|
||||
label: '顺序',
|
||||
type: 'tpl',
|
||||
id: 'u:a617718a149f',
|
||||
placeholder: '-',
|
||||
tpl: '${order}',
|
||||
inline: true,
|
||||
quickEdit: {
|
||||
type: 'input-number',
|
||||
name: 'order',
|
||||
label: '数字',
|
||||
min: '1',
|
||||
step: 1,
|
||||
value: 1,
|
||||
saveImmediately: {
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/teaching_evaluation_items?id=eq.${id}',
|
||||
data: {
|
||||
order: '$order',
|
||||
},
|
||||
dataType: 'form',
|
||||
},
|
||||
},
|
||||
mode: 'popOver',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '权重',
|
||||
name: 'weight',
|
||||
id: 'u:956ac49cc933',
|
||||
placeholder: '-',
|
||||
quickEdit: {
|
||||
type: 'input-number',
|
||||
name: 'weight',
|
||||
label: '',
|
||||
step: 0.5,
|
||||
min: '0',
|
||||
saveImmediately: {
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/teaching_evaluation_items/$id',
|
||||
data: {
|
||||
weight: '$weight',
|
||||
},
|
||||
},
|
||||
},
|
||||
mode: 'popOver',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
id: 'u:6a7475988e72',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button-group',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
visibleOn: '!this.parent',
|
||||
icon: 'fa fa-plus text-primary',
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
tooltip: '添加二级指标',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
id: 'u:e0d17fb5cf3e',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '添加二级指标',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/teaching_evaluation_items',
|
||||
data: {
|
||||
name: '$name',
|
||||
weight: '$weight',
|
||||
organization_id: '$centre_id',
|
||||
semester_id: '$currentSemester',
|
||||
parent: '$id',
|
||||
},
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '名称',
|
||||
name: 'name',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:6ba0f199321c',
|
||||
},
|
||||
{
|
||||
type: 'input-number',
|
||||
name: 'weight',
|
||||
label: '权重',
|
||||
mode: 'normal',
|
||||
min: '0',
|
||||
step: 0.5,
|
||||
required: true,
|
||||
id: 'u:fa0cff8dbbc5',
|
||||
},
|
||||
],
|
||||
id: 'u:a7a2df747e8d',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:1eda9b4880ff',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
id: 'u:18243755deb7',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:188cf1c021ad',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:c83910750b73',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
icon: 'fa fa-pencil text-info',
|
||||
visibleOn: '!this.parent',
|
||||
tooltip: '修改一级指标',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
id: 'u:e2ca6634c8a6',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '修改一级指标',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/teaching_evaluation_items/$id',
|
||||
data: {
|
||||
name: '$parent_name',
|
||||
weight: '$weight',
|
||||
},
|
||||
dataType: 'json',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
label: '名称',
|
||||
type: 'input-text',
|
||||
name: 'parent_name',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:e694855bd5a0',
|
||||
},
|
||||
{
|
||||
type: 'input-number',
|
||||
label: '权重',
|
||||
name: 'weight',
|
||||
mode: 'normal',
|
||||
min: '0',
|
||||
step: 0.5,
|
||||
required: true,
|
||||
id: 'u:6ef32f0c3c51',
|
||||
},
|
||||
],
|
||||
id: 'u:0045f9b77be4',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:1eda9b4880ff',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
id: 'u:3e72890a4b8e',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:7472c80f7c68',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:d860e25841e9',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
confirmText: '确认删除"${parent_name}"?',
|
||||
level: 'link',
|
||||
icon: 'fa fa-times text-danger',
|
||||
size: 'md',
|
||||
tooltip: '删除一级指标',
|
||||
tooltipPlacement: 'top',
|
||||
reload: '',
|
||||
visibleOn: '!this.parent && !this.children.length',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
id: 'u:967e36c71712',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
outputVar: 'responseResult',
|
||||
actionType: 'ajax',
|
||||
options: {},
|
||||
api: {
|
||||
url: 'rest/teaching_evaluation_items/$id',
|
||||
method: 'delete',
|
||||
requestAdaptor: '',
|
||||
adaptor: '',
|
||||
messages: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
componentId: 'u:1eda9b4880ff',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
icon: 'fa fa-pencil text-info',
|
||||
visibleOn: 'this.parent',
|
||||
tooltip: '修改二级指标',
|
||||
tooltipPlacement: 'top',
|
||||
className: 'p-r-none p-l-none',
|
||||
iconClassName: 'pull-left',
|
||||
id: 'u:8290e35c33ea',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '修改二级指标',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/teaching_evaluation_items/$id',
|
||||
data: {
|
||||
name: '$name',
|
||||
weight: '$weight',
|
||||
parent: '$parent',
|
||||
},
|
||||
dataType: 'json',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
label: '名称',
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:c2480af665ff',
|
||||
},
|
||||
{
|
||||
type: 'input-number',
|
||||
label: '权重',
|
||||
name: 'weight',
|
||||
mode: 'normal',
|
||||
min: '0',
|
||||
step: 0.5,
|
||||
required: true,
|
||||
id: 'u:b401731a558d',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '一级指标',
|
||||
name: 'parent',
|
||||
mode: 'normal',
|
||||
options: [],
|
||||
checkAll: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/teaching_evaluation_items?parent=is.null&organization_id=eq.$centre_id&semester_id=eq.$currentSemester&order=order',
|
||||
adaptor:
|
||||
'return {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id\r\n }\r\n })\r\n}',
|
||||
},
|
||||
searchable: true,
|
||||
required: true,
|
||||
id: 'u:d1ed468a2aa3',
|
||||
},
|
||||
],
|
||||
id: 'u:d0af5e81cfa8',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:1eda9b4880ff',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
id: 'u:44a6ba40d2f2',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:05a728e1cf94',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:8bed7ea1617c',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
confirmText: '确认删除"${name}"?',
|
||||
level: 'link',
|
||||
icon: 'fa fa-times text-danger',
|
||||
size: 'md',
|
||||
tooltip: '删除二级指标',
|
||||
tooltipPlacement: 'top',
|
||||
reload: '',
|
||||
visibleOn: 'this.parent',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
id: 'u:46e8dd7835fd',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
outputVar: 'responseResult',
|
||||
actionType: 'ajax',
|
||||
options: {},
|
||||
api: {
|
||||
url: 'rest/teaching_evaluation_items/$id',
|
||||
method: 'delete',
|
||||
requestAdaptor: '',
|
||||
adaptor: '',
|
||||
messages: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
componentId: 'u:1eda9b4880ff',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
label: '操作',
|
||||
id: 'u:ae8cc0b046b6',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/teaching_evaluation_items?semester_id=eq.$currentSemester&organization_id=eq.$centre_id&order=order',
|
||||
data: null,
|
||||
requestAdaptor: '',
|
||||
adaptor:
|
||||
'let evaluations = []\r\nlet parents = payload.data.items.filter(item => !item.parent)\r\n\r\nparents.length && parents.forEach((parent, index) => {\r\n evaluations.push({\r\n id: parent.id,\r\n order: parent.order,\r\n parent_name: parent.name,\r\n weight: parent.weight,\r\n parent: parent.parent,\r\n children: payload.data.items\r\n .filter(item => item.parent === parent.id)\r\n })\r\n})\r\n\r\nreturn {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: evaluations\r\n }\r\n}',
|
||||
sendOn: '',
|
||||
},
|
||||
syncLocation: false,
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
align: 'right',
|
||||
icon: '',
|
||||
level: 'primary',
|
||||
label: '添加一级指标',
|
||||
id: 'u:37f0878b29a0',
|
||||
reload: '',
|
||||
tpl: '内容',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '添加一级指标',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/teaching_evaluation_items',
|
||||
data: {
|
||||
name: '$name',
|
||||
weight: '$weight',
|
||||
organization_id: '$centre_id',
|
||||
semester_id: '$currentSemester',
|
||||
},
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '名称',
|
||||
name: 'name',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:53e7e8e5c2f6',
|
||||
},
|
||||
{
|
||||
type: 'input-number',
|
||||
name: 'weight',
|
||||
label: '权重',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
min: '0',
|
||||
step: 0.5,
|
||||
id: 'u:a85fdbfc8915',
|
||||
},
|
||||
],
|
||||
id: 'u:4f356ae00643',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:1eda9b4880ff',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
id: 'u:bdb5abe2835a',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:0b55901a86eb',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:6dbf2b39f297',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '指定学期导入',
|
||||
align: 'right',
|
||||
level: 'primary',
|
||||
visibleOn: '',
|
||||
className: '',
|
||||
id: 'u:595e4f3e93ab',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '指定学期',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/create_evaluation_items_from_history',
|
||||
data: {
|
||||
organization_id: '$centre_id',
|
||||
semester_id: '$semester_id',
|
||||
},
|
||||
},
|
||||
target: '',
|
||||
name: 'form',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'reload',
|
||||
target: 'recognize',
|
||||
},
|
||||
],
|
||||
body: [
|
||||
{
|
||||
label: '学期',
|
||||
type: 'select',
|
||||
name: 'semester_id',
|
||||
mode: 'inline',
|
||||
options: [],
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/semesters?is_open=is.false&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id\r\n }\r\n })\r\n }\r\n}',
|
||||
requestAdaptor: '',
|
||||
},
|
||||
size: 'md',
|
||||
inputClassName: 'm-l-xs',
|
||||
checkAll: false,
|
||||
clearable: false,
|
||||
searchable: true,
|
||||
id: 'u:7ee00833ea34',
|
||||
},
|
||||
],
|
||||
id: 'u:551a790ac5aa',
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:1eda9b4880ff',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'sm',
|
||||
id: 'u:8d46598e4801',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:39cbcc618226',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:cab0fce5d690',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
footerToolbar: [],
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
filter: null,
|
||||
bulkActions: [],
|
||||
className: '',
|
||||
md: 9,
|
||||
columnsTogglable: false,
|
||||
quickSaveItemApi: '',
|
||||
hideQuickSaveBtn: true,
|
||||
visibleOn: '',
|
||||
perPage: null,
|
||||
loadDataOnce: true,
|
||||
},
|
||||
],
|
||||
id: 'u:23e12f269e33',
|
||||
messages: {},
|
||||
title: '',
|
||||
bodyClassName: '',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
690
src/pages/schema/centre/evaluation/stat.schema.ts
Normal file
690
src/pages/schema/centre/evaluation/stat.schema.ts
Normal file
@@ -0,0 +1,690 @@
|
||||
const schema = {
|
||||
body: [
|
||||
{
|
||||
type: 'tabs',
|
||||
className: '',
|
||||
tabs: [
|
||||
{
|
||||
title: '日程一览',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
className: '',
|
||||
submitOnChange: true,
|
||||
submitOnInit: true,
|
||||
reload:
|
||||
'manage_loc?course_id=$course_id&project_id=$project_id&teacher_id=$teacher_id&location_id=$location_id&date=$date&no_tch=$no_tch&public=$public&semesterSelect=$semesterSelect',
|
||||
initApi: {
|
||||
method: 'get',
|
||||
url: 'rest/users?role=eq.teacher&id=eq.${user.id}&order=code',
|
||||
requestAdaptor: '',
|
||||
adaptor:
|
||||
"if (payload.data.items.length !== 0) {\r\n return {\r\n teacher_id: 'eq.'.concat(payload.data.items[0].id)\r\n }\r\n} else {\r\n return {}\r\n}",
|
||||
},
|
||||
wrapWithPanel: false,
|
||||
mode: 'inline',
|
||||
name: 'filter',
|
||||
body: [
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '历史',
|
||||
name: 'semesterSelect',
|
||||
options: [],
|
||||
checkAll: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/semesters?order=is_open.desc,since.desc',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
searchable: true,
|
||||
clearable: false,
|
||||
mode: 'inline',
|
||||
size: 'md',
|
||||
inputClassName: 'm-l-xs',
|
||||
id: 'u:a4b2e9a131fa',
|
||||
},
|
||||
{
|
||||
label: '课程',
|
||||
type: 'select',
|
||||
name: 'course_id',
|
||||
mode: 'inline',
|
||||
clearable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/courses?organization_id=eq.$centre_id&semester_id=eq.$semesterSelect&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}(${item.code})`,\r\n value: "eq.".concat(item.id)\r\n }\r\n })\r\n}',
|
||||
},
|
||||
checkAll: false,
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
className: 'm-l-sm',
|
||||
inputClassName: 'm-l-xs',
|
||||
id: 'u:22532e0d2a66',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '实验',
|
||||
name: 'project_id',
|
||||
mode: 'inline',
|
||||
clearable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/projects?select=*,course2projects(*)&organization_id=eq.$centre_id&semester_id=eq.$semesterSelect&course2projects.course_id=$course_id&order=code',
|
||||
adaptor:
|
||||
"return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.filter(\r\n f => f.course2projects.length > 0\r\n ).map(item => {\r\n return {\r\n label: item.name,\r\n value: 'eq.'.concat(item.id)\r\n }\r\n })\r\n }\r\n}",
|
||||
requestAdaptor:
|
||||
'if (api.query.course2projects.course_id === \'\') {\r\n api.url = api.url.replace("&course2projects[course_id]=", "")\r\n}\r\n\r\nreturn api',
|
||||
},
|
||||
checkAll: false,
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
className: 'm-l-sm',
|
||||
inputClassName: 'm-l-xs',
|
||||
id: 'u:cf571eb6cc1a',
|
||||
},
|
||||
],
|
||||
id: 'u:0cf1ac6473cc',
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'input-date-range',
|
||||
label: '日期',
|
||||
name: 'date',
|
||||
mode: 'inline',
|
||||
clearable: true,
|
||||
value: '',
|
||||
format: 'YYYY-MM-DD',
|
||||
ranges:
|
||||
'yesterday,today,7daysago,prevweek,thismonth,prevmonth,prevquarter',
|
||||
inputClassName: 'm-l-xs',
|
||||
id: 'u:b78bf613761a',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '地点',
|
||||
name: 'location_id',
|
||||
mode: 'inline',
|
||||
clearable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/locations?organization_id=eq.$centre_id&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: "eq.".concat(item.id)\r\n }\r\n })\r\n}',
|
||||
},
|
||||
checkAll: false,
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
className: 'm-l-sm',
|
||||
inputClassName: 'm-l-xs',
|
||||
id: 'u:d90502be2317',
|
||||
},
|
||||
{
|
||||
label: '教师',
|
||||
type: 'select',
|
||||
name: 'teacher_id',
|
||||
mode: 'inline',
|
||||
clearable: true,
|
||||
checkAll: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/users?role=eq.teacher&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}(${item.code})`,\r\n value: "eq.".concat(item.id)\r\n }\r\n })\r\n}',
|
||||
},
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
className: 'm-l-sm',
|
||||
id: 'u:2fd7d9613116',
|
||||
},
|
||||
],
|
||||
id: 'u:9df1f6742c55',
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'switch',
|
||||
name: 'no_tch',
|
||||
option: '仅显示无教师场次',
|
||||
mode: 'inline',
|
||||
optionAtLeft: false,
|
||||
trueValue: 1,
|
||||
falseValue: 0,
|
||||
value: 0,
|
||||
disabledOn: 'this.teacher_id',
|
||||
id: 'u:16519e5c1c12',
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
name: 'public',
|
||||
option: '仅显示公共场次',
|
||||
mode: 'inline',
|
||||
optionAtLeft: false,
|
||||
trueValue: 1,
|
||||
falseValue: 0,
|
||||
value: 0,
|
||||
disabledOn: 'this.course_id',
|
||||
inputClassName: 'm-l-sm',
|
||||
id: 'u:0d1445244f47',
|
||||
},
|
||||
],
|
||||
id: 'u:6add1eda4c2a',
|
||||
},
|
||||
],
|
||||
id: 'u:42c847232809',
|
||||
},
|
||||
{ type: 'divider', lineStyle: 'solid', id: 'u:3eb06bbf6fa8' },
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/teaching_evaluation_stats?select=*?organization_id=eq.$centre_id&semester_id=eq.$semesterSelect&is_publish=is.true&order=project_name,date,teacher_name',
|
||||
data: { '&': '$$', page: '$page', perPage: '$perPage' },
|
||||
requestAdaptor:
|
||||
"if (api.query.semesterSelect) {\r\n api.url = api.url.replace(/&semesterSelect=\\d*/g, '')\r\n api.url = api.url.replace(/(?<=semester_id=eq\\.)\\d*(?=&)/g, api.query.semesterSelect)\r\n} else {\r\n api.url = api.url.replace(/&semesterSelect=/g, '')\r\n}\r\nif (api.body.course_id === '') {\r\n api.url = api.url.replace(/&course_id=/g, '')\r\n}\r\nif (api.body.project_id === '') {\r\n api.url = api.url.replace(/&project_id=/g, '')\r\n}\r\nif (api.body.date === '') {\r\n api.url = api.url.replace(/&date=/g, '')\r\n}\r\nif (api.body.location_id === '') {\r\n api.url = api.url.replace(/&location_id=/g, '')\r\n}\r\nif (api.body.teacher_id === '') {\r\n api.url = api.url.replace(/&teacher_id=/g, '')\r\n}\r\nif (api.body.no_tch === 0) {\r\n api.url = api.url.replace(/&no_tch=0/g, '')\r\n}\r\nif (api.body.public === 0) {\r\n api.url = api.url.replace(/&public=0/g, '')\r\n}\r\nconst pattern = /(?:date=)(\\d+-\\d+-\\d+)%2C(\\d+-\\d+-\\d+)/g\r\nconst result = pattern.exec(api.url)\r\nif (result && result[1] === result[2]) {\r\n api.url = api.url.replace(result[0], 'date=eq.'.concat(result[1]))\r\n} else if (result) {\r\n api.url = api.url.replace(result[0], 'date=gte.'.concat(result[1], '&date=lte.', result[2]))\r\n}\r\nconst is_no_tch = /no_tch=1/g.test(api.url)\r\nconst has_teacher = /teacher_id=.+?(?=&)/g.test(api.url)\r\nif (is_no_tch && has_teacher) {\r\n const teacher = /teacher_id=.+?(?=&)/g.exec(api.url)\r\n api.url = api.url.replace(teacher, 'or=('.concat(teacher, ',teacher_id.is.null)')).replace('teacher_id=', 'teacher_id.')\r\n api.url = api.url.replace(/no_tch=1/g, '')\r\n} else if (is_no_tch && !has_teacher) {\r\n api.url = api.url.replace(/no_tch=1/g, 'teacher_id=is.null')\r\n}\r\nconst is_public = /public=1/g.test(api.url)\r\nconst has_course = /course_id=.+?(?=&)/g.test(api.url)\r\nif (is_public && has_course) {\r\n const course = /course_id=.+?(?=&)/g.exec(api.url)\r\n api.url = api.url.replace(course, 'or=('.concat(course, ',course_id.is.null)')).replace('course_id=', 'course_id.')\r\n api.url = api.url.replace(/public=1/g, '')\r\n} else if (is_public && !has_course) {\r\n api.url = api.url.replace(/public=1/g, 'course_id=is.null')\r\n}\r\nreturn {\r\n ...api\r\n}",
|
||||
adaptor:
|
||||
"let supportDateTimeFormat = typeof(Intl.DateTimeFormat) === 'function';\r\npayload.data.items.forEach(item => {\r\n item.day = supportDateTimeFormat ? `${item.date}(${new Intl.DateTimeFormat('zh-CN', { weekday: 'short'}).format(new Date(item.date))})` : item.date;\r\n item.stu_title = item.project_name.concat(\r\n ' :: ', item.date, ' ', item.period_name,\r\n ' :: ', item.location_name,\r\n ' :: ', item.teacher_name ? item.teacher_name : '无教师认领'\r\n )\r\n})\r\n\r\nreturn {\r\n ...payload\r\n}",
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
label: '实验',
|
||||
type: 'tpl',
|
||||
placeholder: '-',
|
||||
tpl: '<% if(!this.course_id) { %>\n <span class="label label-info">公共</span><span> <%= this.project_name %> <span>\n<% } else {%>\n <span> <%= this.project_name %> <span>\n<% } %>',
|
||||
inline: false,
|
||||
id: 'u:821411c19e45',
|
||||
},
|
||||
{
|
||||
name: 'day',
|
||||
label: '日期',
|
||||
type: 'text',
|
||||
placeholder: '-',
|
||||
id: 'u:65690067149b',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '节次',
|
||||
name: 'period_name',
|
||||
placeholder: '-',
|
||||
id: 'u:2aed39988a12',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '地点',
|
||||
name: 'location_name',
|
||||
placeholder: '-',
|
||||
id: 'u:9298ea2c6eb8',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '${teacher_name}',
|
||||
inline: false,
|
||||
label: '教师姓名',
|
||||
name: 'teacher_name',
|
||||
placeholder: '-',
|
||||
id: 'u:1b5d4fe03c4f',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '${teacher_code}',
|
||||
inline: false,
|
||||
label: '教师工号',
|
||||
name: 'teacher_code',
|
||||
placeholder: '-',
|
||||
id: 'u:01a76262c44b',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<% if (data.participation_count<1) { %> \n<span>暂无</span>\n<% } else { %>\n<%= data.evaluation_rating %>\n<% } %>',
|
||||
inline: false,
|
||||
label: '统计总分',
|
||||
name: 'evaluation_rating',
|
||||
placeholder: '-',
|
||||
id: 'u:01b6021e08ff',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '${participation_count}',
|
||||
inline: false,
|
||||
label: '学生评价人数',
|
||||
name: 'participation_count',
|
||||
placeholder: '-',
|
||||
id: 'u:fb9b688b6471',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '${current_student_number}',
|
||||
inline: false,
|
||||
label: '总选课人数',
|
||||
name: 'current_student_number',
|
||||
placeholder: '-',
|
||||
id: 'u:10215551a08f',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '${participation_rating}',
|
||||
inline: false,
|
||||
label: '参评率',
|
||||
name: 'participation_rating',
|
||||
id: 'u:c3abec775099',
|
||||
},
|
||||
{
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
label: '评教明细',
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '评教明细',
|
||||
body: [
|
||||
{
|
||||
type: 'grid',
|
||||
columns: [
|
||||
{
|
||||
xs: 5,
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
wrapWithPanel: false,
|
||||
submitOnChange: true,
|
||||
reload:
|
||||
'evaluation?selected_user=$selected_user',
|
||||
name: 'filter',
|
||||
debug: false,
|
||||
body: [
|
||||
{
|
||||
type: 'switch',
|
||||
name: 'open',
|
||||
option: '显示姓名',
|
||||
optionAtLeft: false,
|
||||
trueValue: true,
|
||||
falseValue: false,
|
||||
value: false,
|
||||
mode: 'normal',
|
||||
submitOnChange: false,
|
||||
id: 'u:ae62dc94f85c',
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
type: 'input-tree',
|
||||
name: 'selected_user',
|
||||
mode: 'inline',
|
||||
size: 'lg',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/user2projects?select=*,users:users!user2project_student_id_fkey(*)&schedule_id=eq.$schedule_id&schedule_status=eq.elected&open=$open',
|
||||
adaptor:
|
||||
"return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map((item, index) => {\r\n let temp = null\r\n\r\n if ((index + 1).toString().length === 1) {\r\n temp = '00' + (index + 1)\r\n } else if ((index + 1).toString().length === 2) {\r\n temp = '0' + (index + 1)\r\n } else {\r\n temp = '' + (index + 1)\r\n }\r\n\r\n return {\r\n label: item.users.name\r\n ? '' + temp + ' - 学号: ' + item.users.code + ' - 姓名: ' + item.users.name\r\n : '' + temp + ' - 学号: xxx - 姓名: xxx',\r\n value: item.users.id\r\n }\r\n })\r\n }\r\n}",
|
||||
data: null,
|
||||
requestAdaptor:
|
||||
"if (api.query.open) {\r\n api.url = api.url.replace('&open=true', '')\r\n} else {\r\n api.url = api.url.replace('&open=false', '')\r\n api.url = api.url.replace('%2Cusers%3Ausers%21user2project_student_id_fkey%28%2A%29', '%2Cusers%3Ausers%21user2project_student_id_fkey%28id%29')\r\n}",
|
||||
sendOn: '',
|
||||
},
|
||||
inputClassName: 'evaluation-height',
|
||||
className: '',
|
||||
labelClassName: '',
|
||||
id: 'u:2234b8e979fc',
|
||||
multiple: false,
|
||||
enableNodePath: false,
|
||||
hideRoot: true,
|
||||
showIcon: true,
|
||||
initiallyOpen: true,
|
||||
virtualThreshold: false,
|
||||
},
|
||||
],
|
||||
id: 'u:f15b97a9e90e',
|
||||
},
|
||||
],
|
||||
md: 4,
|
||||
id: 'u:f5120659ae2f',
|
||||
},
|
||||
{
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/user2projects?select=*,teaching_evaluations(*,teaching_evaluation_items(*)),teaching_evaluation_message(content)&schedule_id=eq.$schedule_id&user_id=eq.$selected_user&schedule_status=eq.elected',
|
||||
sendOn: 'this.selected_user',
|
||||
adaptor:
|
||||
'let evaluations = []\r\n\r\npayload.data.items[0].teaching_evaluations\r\n .filter(item => !item.teaching_evaluation_items.parent)\r\n .sort((a, b) => {\r\n return a.teaching_evaluation_items.order - b.teaching_evaluation_items.order\r\n })\r\n .forEach(parent => {\r\n evaluations.push({\r\n ...parent,\r\n parent_name: parent.teaching_evaluation_items.name\r\n })\r\n payload.data.items[0].teaching_evaluations\r\n .filter(item => item.teaching_evaluation_items.parent\r\n === parent.teaching_evaluation_items.id)\r\n .sort((a, b) => {\r\n return a.teaching_evaluation_items.order - b.teaching_evaluation_items.order\r\n })\r\n .forEach(item => evaluations.push({\r\n ...item,\r\n child_name: item.teaching_evaluation_items.name\r\n }))\r\n })\r\n\r\nconsole.log(payload.data.items[0].teaching_evaluation_message[0]);return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n content:payload.data.items[0].teaching_evaluation_message[0]?payload.data.items[0].teaching_evaluation_message[0].content:null, items: evaluations\r\n }\r\n}\r\n',
|
||||
requestAdaptor: '',
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
name: 'parent_name',
|
||||
label: '一级指标',
|
||||
type: 'text',
|
||||
id: 'u:b0fbb3e1005c',
|
||||
},
|
||||
{
|
||||
name: 'child_name',
|
||||
label: '二级指标',
|
||||
type: 'text',
|
||||
id: 'u:cf4a41c690ef',
|
||||
},
|
||||
{
|
||||
name: 'evaluation_rating',
|
||||
label: '评分',
|
||||
type: 'text',
|
||||
id: 'u:15f856dabafe',
|
||||
},
|
||||
],
|
||||
bulkActions: [],
|
||||
itemActions: [],
|
||||
perPageAvailable: [10],
|
||||
messages: {},
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '${stu_title}',
|
||||
id: 'u:6ee9e9f6eea1',
|
||||
},
|
||||
],
|
||||
footerToolbar: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '评语:${content}',
|
||||
id: 'u:6ee9e9f6eea1',
|
||||
visibleOn: 'this.content'
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
name: 'evaluation',
|
||||
affixHeader: false,
|
||||
id: 'u:9c49baa041b9',
|
||||
},
|
||||
],
|
||||
md: 8,
|
||||
id: 'u:65c044dae897',
|
||||
},
|
||||
],
|
||||
id: 'u:9a86c3778456',
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
actions: [],
|
||||
data: null,
|
||||
size: 'lg',
|
||||
closeOnOutside: false,
|
||||
bodyClassName: '',
|
||||
id: 'u:43821eb65ad8',
|
||||
},
|
||||
level: 'primary',
|
||||
size: 'xs',
|
||||
id: 'u:bda614e0a3f8',
|
||||
},
|
||||
],
|
||||
id: 'u:2b35aaf1e56c',
|
||||
},
|
||||
],
|
||||
bulkActions: [],
|
||||
itemActions: [],
|
||||
syncLocation: false,
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'export-excel',
|
||||
label: '导出',
|
||||
icon: 'fa fa-download',
|
||||
level: 'primary',
|
||||
align: 'right',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/teaching_evaluation_stats?organization_id=eq.$centre_id&semester_id=eq.$semesterSelect&is_publish=is.true&order=project_name,date,teacher_name',
|
||||
dataType: 'form',
|
||||
data: { semesterSelect: '$semesterSelect' },
|
||||
requestAdaptor:
|
||||
"if (api.query.semesterSelect) {\r\n api.url = api.url.replace(/&semesterSelect=\\d*/g, '')\r\n api.url = api.url.replace(/(?<=semester_id=eq\\.)\\d*(?=&)/g, api.query.semesterSelect)\r\n} else {\r\n api.url = api.url.replace(/&semesterSelect=/g, '')\r\n}\r\n\r\nreturn {\r\n ...api\r\n}",
|
||||
adaptor:
|
||||
'let columns = [{\r\n "name": "project_name",\r\n "label": "实验" },\r\n {\r\n "name": "date",\r\n "label": "日期" },\r\n {\r\n "name": "teacher_name",\r\n "label": "教师姓名"\r\n },\r\n {\r\n "name": "teacher_code",\r\n "label": "教师工号"\r\n },\r\n {\r\n "name": "evaluation_rating",\r\n "label": "总计总分"\r\n },\r\n {\r\n "name": "participation_count",\r\n "label": "学生评价人数"\r\n },\r\n {\r\n "name": "current_student_number",\r\n "label": "总选课人数"\r\n },\r\n {\r\n "name": "participation_rating",\r\n "label": "参评率"\r\n }];\r\nreturn {\r\n ...payload,\r\n data: {\r\n items: payload.data.items,\r\n columns: columns\r\n }\r\n}',
|
||||
},
|
||||
filename: '评教统计',
|
||||
id: 'u:1b59142be0d3',
|
||||
},
|
||||
],
|
||||
name: 'manage_loc',
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
messages: {},
|
||||
id: 'u:367dac93b9ef',
|
||||
},
|
||||
],
|
||||
className: 'p-t-none',
|
||||
id: 'u:804603aed3fd',
|
||||
},
|
||||
{
|
||||
title: '自由安排',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
columns: [
|
||||
{ type: 'text', label: '编号', name: 'code' },
|
||||
{ name: 'name', type: 'text', label: '课程' },
|
||||
{
|
||||
type: 'button-group',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '查看项目',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '项目信息',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/course2projects?select=*,projects(*)&course_id=eq.$id',
|
||||
data: { page: '$page', perPage: '$perPage' },
|
||||
adaptor:
|
||||
'let free, not_free\r\nfree = payload.data.items.filter(\r\n item => item.projects.free_schedule\r\n ).sort(\r\n (a, b) => a.projects.code.localeCompare(b.projects.code)\r\n )\r\nnot_free = payload.data.items.filter(\r\n item => !item.projects.free_schedule\r\n ).sort(\r\n (a, b) => a.projects.code.localeCompare(b.projects.code)\r\n )\r\nreturn {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: not_free.concat(free)\r\n }\r\n}',
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
name: 'projects.code',
|
||||
label: '编号',
|
||||
type: 'text',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
name: 'projects.name',
|
||||
label: '项目',
|
||||
type: 'tpl',
|
||||
placeholder: '-',
|
||||
tpl: '<% if(this.projects && this.projects.free_schedule) { %>\n <span class="label label-info">自由安排</span><span><%= this.projects.name %></span>\n<% } else if(this.projects) { %>\n <span><%= this.projects.name %></span>\n<% } %>',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
label: '操作',
|
||||
placeholder: '-',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '评教明细',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '评教明细',
|
||||
body: [
|
||||
{
|
||||
type: 'grid',
|
||||
columns: [
|
||||
{
|
||||
name: 'filter',
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
controls: [
|
||||
{
|
||||
type: 'switch',
|
||||
name: 'open',
|
||||
option: '显示姓名',
|
||||
optionAtLeft: false,
|
||||
trueValue: true,
|
||||
falseValue: false,
|
||||
value: false,
|
||||
mode: 'inline',
|
||||
submitOnChange: false,
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
type: 'tree',
|
||||
name: 'selected_user',
|
||||
options: [],
|
||||
mode: 'inline',
|
||||
size: 'md',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/user2projects?select=*,users:users!user2project_student_id_fkey(*)&schedule_id=eq.$id&schedule_status=eq.elected&open=$open',
|
||||
adaptor:
|
||||
"return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n label: item.users.name ? item.users.name : 'xxx',\r\n value: item.users.id\r\n }\r\n })\r\n }\r\n}",
|
||||
data: null,
|
||||
requestAdaptor:
|
||||
"if (api.query.open) {\r\n api.url = api.url.replace('&open=true', '')\r\n} else {\r\n api.url = api.url.replace('&open=false', '')\r\n api.url = api.url.replace('%2Cusers%3Ausers%21user2project_student_id_fkey%28%2A%29', '%2Cusers%3Ausers%21user2project_student_id_fkey%28id%29')\r\n}",
|
||||
sendOn: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
wrapWithPanel: false,
|
||||
submitOnChange: true,
|
||||
xs: 4,
|
||||
reload:
|
||||
'evaluation?selected_user=$selected_user',
|
||||
debug: false,
|
||||
},
|
||||
{
|
||||
name: 'evaluation',
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/user2projects?select=*,teaching_evaluations(*,teaching_evaluation_items(*)),teaching_evaluation_message(content)&project_id=eq.$id&user_id=eq.$selected_user&schedule_status=eq.free_schedule',
|
||||
sendOn: 'this.selected_user',
|
||||
adaptor:
|
||||
'let evaluations = []\r\n\r\npayload.data.items[0].teaching_evaluations\r\n .filter(item => !item.teaching_evaluation_items.parent)\r\n .forEach(parent => {\r\n evaluations.push({\r\n ...parent,\r\n parent_name: parent.teaching_evaluation_items.name\r\n })\r\n payload.data.items[0].teaching_evaluations\r\n .filter(item => item.teaching_evaluation_items.parent\r\n === parent.teaching_evaluation_items.id)\r\n .forEach(item => evaluations.push({\r\n ...item,\r\n child_name: item.teaching_evaluation_items.name\r\n }))\r\n })\r\n\r\nreturn {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: evaluations\r\n }\r\n}\r\n',
|
||||
requestAdaptor: '',
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
name: 'parent_name',
|
||||
label: '一级指标',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'child_name',
|
||||
label: '二级指标',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'evaluation_rating',
|
||||
label: '评分',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
bulkActions: [],
|
||||
itemActions: [],
|
||||
perPageAvailable: [10],
|
||||
messages: {},
|
||||
syncLocation: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
actions: [],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
data: null,
|
||||
size: 'lg',
|
||||
},
|
||||
size: 'xs',
|
||||
level: 'primary',
|
||||
visibleOn: 'this.projects.free_schedule',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
syncLocation: false,
|
||||
headerToolbar: [],
|
||||
affixHeader: false,
|
||||
name: 'project_view',
|
||||
footerToolbar: [{ type: 'pagination' }],
|
||||
},
|
||||
],
|
||||
size: 'lg',
|
||||
actions: [],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
level: 'primary',
|
||||
size: 'xs',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/courses?organization_id=eq.$centre_id&semester_id=eq.$semesterSelect&order=code',
|
||||
data: { page: '$page', perPage: '$perPage' },
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n ...item,\r\n selected_course_id: item.id\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
messages: {},
|
||||
syncLocation: false,
|
||||
pageField: 'page',
|
||||
perPageField: 'perPage',
|
||||
},
|
||||
],
|
||||
className: 'p-t-none',
|
||||
},
|
||||
],
|
||||
tabsMode: 'line',
|
||||
tabClassName: '',
|
||||
id: 'u:264fc2e5b77b',
|
||||
},
|
||||
],
|
||||
type: 'page',
|
||||
messages: {},
|
||||
bodyClassName: '',
|
||||
title: '',
|
||||
definitions: {
|
||||
options: {
|
||||
type: 'combo',
|
||||
multiple: true,
|
||||
multiLine: true,
|
||||
controls: [
|
||||
{
|
||||
type: 'group',
|
||||
controls: [
|
||||
{ label: '选项', name: 'key', type: 'text' },
|
||||
{ label: '内容', name: 'label', type: 'text' },
|
||||
{
|
||||
type: 'switch',
|
||||
option: '开关',
|
||||
name: 'is_fixed',
|
||||
label: '是否固定',
|
||||
},
|
||||
],
|
||||
label: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
id: 'u:21edf657a725',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
323
src/pages/schema/centre/exam/assessment.schema.ts
Normal file
323
src/pages/schema/centre/exam/assessment.schema.ts
Normal file
@@ -0,0 +1,323 @@
|
||||
const schema = {
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
name: 'custom_tp',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/project_process_templates?select=*,exam_paper:exam_paper!project_preparation_exam_exam_paper_id_fk(*)&organization_id=eq.$centre_id&semester_id=eq.${currentSemester}&order=code',
|
||||
adaptor: '',
|
||||
data: { page: '$page', perPage: '$perPage' },
|
||||
requestAdaptor: '',
|
||||
},
|
||||
columns: [
|
||||
{ name: 'code', label: '编号', type: 'text', placeholder: '-' },
|
||||
{ name: 'name', label: '名称', type: 'text', placeholder: '-' },
|
||||
{
|
||||
type: 'text',
|
||||
label: '考试试卷',
|
||||
name: 'exam_paper.name',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
inline: false,
|
||||
name: 'exam_start_fix',
|
||||
label: '上课多久允许答题',
|
||||
style: {},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '持续时间',
|
||||
placeholder: '-',
|
||||
name: 'exam_duration',
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '设置考试',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '设置实验考试',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/project_preparation_exam',
|
||||
data: {
|
||||
name: '$name',
|
||||
exam_paper_id: '$exam_paper_id',
|
||||
type: '$type',
|
||||
exam_start_time: '$exam_start_time',
|
||||
exam_start_fix: '$exam_start_fix',
|
||||
exam_duration: '$exam_duration',
|
||||
organization_id: '${centre_id}',
|
||||
project_code: '$code',
|
||||
exam_type: 'process_assessment',
|
||||
},
|
||||
requestAdaptor:
|
||||
'if(!api.data.exam_start_fix) {\r\n api.data.exam_start_fix=0;\r\n}\r\nif(!api.data.exam_duration) {\r\n api.data.exam_duration=10;\r\n}\r\nreturn api;',
|
||||
headers: { Prefer: 'resolution=merge-duplicates' },
|
||||
},
|
||||
initApi: {
|
||||
method: 'get',
|
||||
url: 'rest/project_preparation_exam/$template_id',
|
||||
sendOn: 'this.template_id !== null',
|
||||
data: null,
|
||||
adaptor:
|
||||
'if(payload.data.exam_start_time){payload.data.exam_start_time=payload.data.exam_start_time.substring(0,19).replace("T", " ");}\r\nreturn payload;',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
label: '名称',
|
||||
name: 'name',
|
||||
mode: 'normal',
|
||||
type: 'input-text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '选择实验考试试卷',
|
||||
name: 'exam_paper_id',
|
||||
options: [],
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
checkAll: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/exam_paper?select=label:name,value:id&type=eq.exam_template_preparation',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'formula',
|
||||
name: 'type',
|
||||
condition: 'data.free_schedule',
|
||||
formula: "'exam_time_fixed'",
|
||||
},
|
||||
{
|
||||
type: 'list-select',
|
||||
label: '',
|
||||
name: 'type',
|
||||
mode: 'normal',
|
||||
options: [
|
||||
{
|
||||
label: '以上课时间为参考',
|
||||
value: 'exam_time_offset',
|
||||
},
|
||||
{ label: '固定开始时间', value: 'exam_time_fixed' },
|
||||
],
|
||||
value: 'exam_time_offset',
|
||||
disabledOn: 'this.free_schedule',
|
||||
},
|
||||
{
|
||||
type: 'input-datetime',
|
||||
label: '考试时间',
|
||||
name: 'exam_start_time',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
visibleOn: "this.type === 'exam_time_fixed'",
|
||||
placeholder: '请选择考试日期时间',
|
||||
value: '',
|
||||
minDate: '',
|
||||
maxDate: '',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
inputFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '上课多久允许答题',
|
||||
name: 'exam_start_fix',
|
||||
mode: 'normal',
|
||||
description:
|
||||
'使用d/h/m或D/H/M表示天/小时/分钟,默认为分钟,负值为提前',
|
||||
validations: {
|
||||
matchRegexp1:
|
||||
/^(\d+|\d+[Dd]|\d+[Hh]|\d+[Mm])$|^-(\d+|\d+[Dd]|\d+[Hh]|\d+[Mm])$/,
|
||||
},
|
||||
validationErrors: {
|
||||
matchRegexp1:
|
||||
'请输入正确的格式,使用d/h/m或D/H/M表示天/小时/分钟',
|
||||
},
|
||||
validateOnChange: true,
|
||||
step: 1,
|
||||
required: true,
|
||||
visibleOn: "this.type === 'exam_time_offset'",
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '持续时间',
|
||||
name: 'exam_duration',
|
||||
mode: 'normal',
|
||||
description:
|
||||
'使用d/h/m或D/H/M表示天/小时/分钟,默认为分钟',
|
||||
validations: {
|
||||
matchRegexp1: /^(\d+|\d+[Dd]|\d+[Hh]|\d+[Mm])$/,
|
||||
},
|
||||
validationErrors: {
|
||||
matchRegexp1:
|
||||
'请输入正确的格式,使用d/h/m或D/H/M表示天/小时/分钟',
|
||||
},
|
||||
validateOnChange: true,
|
||||
step: 1,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
closeOnOutside: false,
|
||||
},
|
||||
level: 'primary',
|
||||
icon: 'fa fa-cog',
|
||||
size: 'sm',
|
||||
className: 'pull-right',
|
||||
},
|
||||
],
|
||||
label: '操作',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
syncLocation: false,
|
||||
headerToolbar: [],
|
||||
footerToolbar: [
|
||||
{ type: 'pagination' },
|
||||
{ type: 'switch-per-page' },
|
||||
{ type: 'statistics' },
|
||||
],
|
||||
bodyClassName: 'rpt-tp-height',
|
||||
affixHeader: false,
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
perPageField: 'perPage',
|
||||
},
|
||||
],
|
||||
type: 'page',
|
||||
title: '',
|
||||
messages: {},
|
||||
bodyClassName: '',
|
||||
definitions: {
|
||||
variable: {
|
||||
type: 'combo',
|
||||
label: '组合输入',
|
||||
name: 'combo',
|
||||
multiple: true,
|
||||
multiLine: false,
|
||||
joinValues: true,
|
||||
messages: {},
|
||||
mode: 'normal',
|
||||
conditions: [
|
||||
{
|
||||
label: '变量',
|
||||
test: 'this.type === "text"',
|
||||
controls: [{ type: 'text', label: '变量', name: 'string' }],
|
||||
scaffold: { type: 'text', label: '变量', name: '' },
|
||||
},
|
||||
{
|
||||
label: '对象',
|
||||
test: 'this.type === "object"',
|
||||
controls: [
|
||||
{ type: 'text', label: '变量', name: 'string' },
|
||||
{ $ref: 'variable' },
|
||||
],
|
||||
scaffold: { type: 'object', label: '对象', name: '' },
|
||||
},
|
||||
{
|
||||
label: '数组',
|
||||
test: 'this.type === "array"',
|
||||
controls: [{ $ref: 'variable' }],
|
||||
scaffold: { type: 'array', label: '数组', name: '' },
|
||||
},
|
||||
],
|
||||
},
|
||||
textItem: {
|
||||
type: 'group',
|
||||
controls: [
|
||||
{
|
||||
name: 'type',
|
||||
value: 'text',
|
||||
type: 'select',
|
||||
clearable: false,
|
||||
size: 'sm',
|
||||
options: [
|
||||
{ label: '变量', value: 'string' },
|
||||
{ label: '对象', value: 'object' },
|
||||
{ label: '数组', value: 'array' },
|
||||
],
|
||||
},
|
||||
{ name: 'name', type: 'text', placeholder: '名称', required: true },
|
||||
{ name: 'title', type: 'text', placeholder: '说明', required: true },
|
||||
{
|
||||
type: 'text',
|
||||
name: 'default',
|
||||
placeholder: '例值',
|
||||
required: true,
|
||||
visibleOn: "this.type==='text'",
|
||||
},
|
||||
],
|
||||
},
|
||||
objectItem: {
|
||||
type: 'combo',
|
||||
multiLine: true,
|
||||
controls: [{ name: 'data', value: {}, $ref: 'fieldItem', minLength: 1 }],
|
||||
multiple: false,
|
||||
},
|
||||
arrayItem: {
|
||||
type: 'combo',
|
||||
multiLine: true,
|
||||
controls: [
|
||||
{ name: 'data', value: {}, $ref: 'elementItem', minLength: 1 },
|
||||
],
|
||||
multiple: false,
|
||||
},
|
||||
fieldItem: {
|
||||
type: 'combo',
|
||||
multiple: true,
|
||||
multiLine: true,
|
||||
typeSwitchable: false,
|
||||
controls: [
|
||||
{ $ref: 'textItem', minLength: 0 },
|
||||
{
|
||||
$ref: 'objectItem',
|
||||
minLength: 0,
|
||||
visibleOn: "this.type==='object'",
|
||||
name: 'children',
|
||||
},
|
||||
{
|
||||
$ref: 'arrayItem',
|
||||
minLength: 0,
|
||||
visibleOn: "this.type==='array'",
|
||||
name: 'children',
|
||||
},
|
||||
],
|
||||
},
|
||||
elementItem: {
|
||||
type: 'combo',
|
||||
multiple: false,
|
||||
multiLine: true,
|
||||
typeSwitchable: false,
|
||||
controls: [
|
||||
{ $ref: 'textItem', minLength: 0, unique: true },
|
||||
{
|
||||
$ref: 'objectItem',
|
||||
minLength: 0,
|
||||
visibleOn: "this.type==='object'",
|
||||
name: 'children',
|
||||
},
|
||||
{
|
||||
$ref: 'arrayItem',
|
||||
minLength: 0,
|
||||
visibleOn: "this.type==='array'",
|
||||
name: 'children',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export { schema };
|
||||
2000
src/pages/schema/centre/exam/index.schema.ts
Normal file
2000
src/pages/schema/centre/exam/index.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
2944
src/pages/schema/centre/exam/paper.schema.ts
Normal file
2944
src/pages/schema/centre/exam/paper.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
322
src/pages/schema/centre/exam/preparation.schema.ts
Normal file
322
src/pages/schema/centre/exam/preparation.schema.ts
Normal file
@@ -0,0 +1,322 @@
|
||||
const schema = {
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
name: 'custom_tp',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/project_exam_templates?select=*,exam_paper:exam_paper!project_preparation_exam_exam_paper_id_fk(*)&organization_id=eq.$centre_id&semester_id=eq.${currentSemester}&order=code',
|
||||
adaptor: '',
|
||||
data: { page: '$page', perPage: '$perPage' },
|
||||
requestAdaptor: '',
|
||||
},
|
||||
columns: [
|
||||
{ name: 'code', label: '编号', type: 'text', placeholder: '-' },
|
||||
{ name: 'name', label: '名称', type: 'text', placeholder: '-' },
|
||||
{
|
||||
type: 'text',
|
||||
label: '考试试卷',
|
||||
name: 'exam_paper.name',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
inline: false,
|
||||
name: 'exam_start_fix',
|
||||
label: '上课多久允许答题',
|
||||
style: {},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '持续时间',
|
||||
placeholder: '-',
|
||||
name: 'exam_duration',
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '设置考试',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '设置实验考试',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/project_preparation_exam',
|
||||
data: {
|
||||
name: '$name',
|
||||
exam_paper_id: '$exam_paper_id',
|
||||
type: '$type',
|
||||
exam_start_time: '$exam_start_time',
|
||||
exam_start_fix: '$exam_start_fix',
|
||||
exam_duration: '$exam_duration',
|
||||
organization_id: '${centre_id}',
|
||||
project_code: '$code',
|
||||
},
|
||||
requestAdaptor:
|
||||
'if(!api.data.exam_start_fix) {\r\n api.data.exam_start_fix=0;\r\n}\r\nif(!api.data.exam_duration) {\r\n api.data.exam_duration=10;\r\n}\r\nreturn api;',
|
||||
headers: { Prefer: 'resolution=merge-duplicates' },
|
||||
},
|
||||
initApi: {
|
||||
method: 'get',
|
||||
url: 'rest/project_preparation_exam/$template_id',
|
||||
sendOn: 'this.template_id !== null',
|
||||
data: null,
|
||||
adaptor:
|
||||
'if(payload.data.exam_start_time){payload.data.exam_start_time=payload.data.exam_start_time.substring(0,19).replace("T", " ");}\r\nreturn payload;',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
label: '名称',
|
||||
name: 'name',
|
||||
mode: 'normal',
|
||||
type: 'input-text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '选择实验考试试卷',
|
||||
name: 'exam_paper_id',
|
||||
options: [],
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
checkAll: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/exam_paper?select=label:name,value:id&type=eq.exam_template_preparation',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'formula',
|
||||
name: 'type',
|
||||
condition: 'data.free_schedule',
|
||||
formula: "'exam_time_fixed'",
|
||||
},
|
||||
{
|
||||
type: 'list-select',
|
||||
label: '',
|
||||
name: 'type',
|
||||
mode: 'normal',
|
||||
options: [
|
||||
{
|
||||
label: '以上课时间为参考',
|
||||
value: 'exam_time_offset',
|
||||
},
|
||||
{ label: '固定开始时间', value: 'exam_time_fixed' },
|
||||
],
|
||||
value: 'exam_time_offset',
|
||||
disabledOn: 'this.free_schedule',
|
||||
},
|
||||
{
|
||||
type: 'input-datetime',
|
||||
label: '考试时间',
|
||||
name: 'exam_start_time',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
visibleOn: "this.type === 'exam_time_fixed'",
|
||||
placeholder: '请选择考试日期时间',
|
||||
value: '',
|
||||
minDate: '',
|
||||
maxDate: '',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
inputFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '上课多久允许答题',
|
||||
name: 'exam_start_fix',
|
||||
mode: 'normal',
|
||||
description:
|
||||
'使用d/h/m或D/H/M表示天/小时/分钟,默认为分钟,负值为提前',
|
||||
validations: {
|
||||
matchRegexp1:
|
||||
/^(\d+|\d+[Dd]|\d+[Hh]|\d+[Mm])$|^-(\d+|\d+[Dd]|\d+[Hh]|\d+[Mm])$/,
|
||||
},
|
||||
validationErrors: {
|
||||
matchRegexp1:
|
||||
'请输入正确的格式,使用d/h/m或D/H/M表示天/小时/分钟',
|
||||
},
|
||||
validateOnChange: true,
|
||||
step: 1,
|
||||
required: true,
|
||||
visibleOn: "this.type === 'exam_time_offset'",
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '持续时间',
|
||||
name: 'exam_duration',
|
||||
mode: 'normal',
|
||||
description:
|
||||
'使用d/h/m或D/H/M表示天/小时/分钟,默认为分钟',
|
||||
validations: {
|
||||
matchRegexp1: /^(\d+|\d+[Dd]|\d+[Hh]|\d+[Mm])$/,
|
||||
},
|
||||
validationErrors: {
|
||||
matchRegexp1:
|
||||
'请输入正确的格式,使用d/h/m或D/H/M表示天/小时/分钟',
|
||||
},
|
||||
validateOnChange: true,
|
||||
step: 1,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
closeOnOutside: false,
|
||||
},
|
||||
level: 'primary',
|
||||
icon: 'fa fa-cog',
|
||||
size: 'sm',
|
||||
className: 'pull-right',
|
||||
},
|
||||
],
|
||||
label: '操作',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
syncLocation: false,
|
||||
headerToolbar: [],
|
||||
footerToolbar: [
|
||||
{ type: 'pagination' },
|
||||
{ type: 'switch-per-page' },
|
||||
{ type: 'statistics' },
|
||||
],
|
||||
bodyClassName: 'rpt-tp-height',
|
||||
affixHeader: false,
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
perPageField: 'perPage',
|
||||
},
|
||||
],
|
||||
type: 'page',
|
||||
title: '',
|
||||
messages: {},
|
||||
bodyClassName: '',
|
||||
definitions: {
|
||||
variable: {
|
||||
type: 'combo',
|
||||
label: '组合输入',
|
||||
name: 'combo',
|
||||
multiple: true,
|
||||
multiLine: false,
|
||||
joinValues: true,
|
||||
messages: {},
|
||||
mode: 'normal',
|
||||
conditions: [
|
||||
{
|
||||
label: '变量',
|
||||
test: 'this.type === "text"',
|
||||
controls: [{ type: 'text', label: '变量', name: 'string' }],
|
||||
scaffold: { type: 'text', label: '变量', name: '' },
|
||||
},
|
||||
{
|
||||
label: '对象',
|
||||
test: 'this.type === "object"',
|
||||
controls: [
|
||||
{ type: 'text', label: '变量', name: 'string' },
|
||||
{ $ref: 'variable' },
|
||||
],
|
||||
scaffold: { type: 'object', label: '对象', name: '' },
|
||||
},
|
||||
{
|
||||
label: '数组',
|
||||
test: 'this.type === "array"',
|
||||
controls: [{ $ref: 'variable' }],
|
||||
scaffold: { type: 'array', label: '数组', name: '' },
|
||||
},
|
||||
],
|
||||
},
|
||||
textItem: {
|
||||
type: 'group',
|
||||
controls: [
|
||||
{
|
||||
name: 'type',
|
||||
value: 'text',
|
||||
type: 'select',
|
||||
clearable: false,
|
||||
size: 'sm',
|
||||
options: [
|
||||
{ label: '变量', value: 'string' },
|
||||
{ label: '对象', value: 'object' },
|
||||
{ label: '数组', value: 'array' },
|
||||
],
|
||||
},
|
||||
{ name: 'name', type: 'text', placeholder: '名称', required: true },
|
||||
{ name: 'title', type: 'text', placeholder: '说明', required: true },
|
||||
{
|
||||
type: 'text',
|
||||
name: 'default',
|
||||
placeholder: '例值',
|
||||
required: true,
|
||||
visibleOn: "this.type==='text'",
|
||||
},
|
||||
],
|
||||
},
|
||||
objectItem: {
|
||||
type: 'combo',
|
||||
multiLine: true,
|
||||
controls: [{ name: 'data', value: {}, $ref: 'fieldItem', minLength: 1 }],
|
||||
multiple: false,
|
||||
},
|
||||
arrayItem: {
|
||||
type: 'combo',
|
||||
multiLine: true,
|
||||
controls: [
|
||||
{ name: 'data', value: {}, $ref: 'elementItem', minLength: 1 },
|
||||
],
|
||||
multiple: false,
|
||||
},
|
||||
fieldItem: {
|
||||
type: 'combo',
|
||||
multiple: true,
|
||||
multiLine: true,
|
||||
typeSwitchable: false,
|
||||
controls: [
|
||||
{ $ref: 'textItem', minLength: 0 },
|
||||
{
|
||||
$ref: 'objectItem',
|
||||
minLength: 0,
|
||||
visibleOn: "this.type==='object'",
|
||||
name: 'children',
|
||||
},
|
||||
{
|
||||
$ref: 'arrayItem',
|
||||
minLength: 0,
|
||||
visibleOn: "this.type==='array'",
|
||||
name: 'children',
|
||||
},
|
||||
],
|
||||
},
|
||||
elementItem: {
|
||||
type: 'combo',
|
||||
multiple: false,
|
||||
multiLine: true,
|
||||
typeSwitchable: false,
|
||||
controls: [
|
||||
{ $ref: 'textItem', minLength: 0, unique: true },
|
||||
{
|
||||
$ref: 'objectItem',
|
||||
minLength: 0,
|
||||
visibleOn: "this.type==='object'",
|
||||
name: 'children',
|
||||
},
|
||||
{
|
||||
$ref: 'arrayItem',
|
||||
minLength: 0,
|
||||
visibleOn: "this.type==='array'",
|
||||
name: 'children',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export { schema };
|
||||
1642
src/pages/schema/centre/exam/question.schema.ts
Normal file
1642
src/pages/schema/centre/exam/question.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
1178
src/pages/schema/centre/grade/data.schema.ts
Normal file
1178
src/pages/schema/centre/grade/data.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
1184
src/pages/schema/centre/grade/operation.schema.ts
Normal file
1184
src/pages/schema/centre/grade/operation.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
1203
src/pages/schema/centre/grade/overview.schema.ts
Normal file
1203
src/pages/schema/centre/grade/overview.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
1185
src/pages/schema/centre/grade/preparation.schema.ts
Normal file
1185
src/pages/schema/centre/grade/preparation.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
1177
src/pages/schema/centre/grade/report.schema.ts
Normal file
1177
src/pages/schema/centre/grade/report.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
1178
src/pages/schema/centre/grade/studyonline.schema.ts
Normal file
1178
src/pages/schema/centre/grade/studyonline.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
104
src/pages/schema/centre/mooc/accessory.schema.ts
Normal file
104
src/pages/schema/centre/mooc/accessory.schema.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
const schema = {
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/projects?select=*&organization_id=eq.${centre_id}&semester_id=eq.${currentSemester}&order=code',
|
||||
data: { page: '$page', perPage: '$perPage' },
|
||||
},
|
||||
headerToolbar: [],
|
||||
syncLocation: false,
|
||||
pageField: 'page',
|
||||
perPageField: 'perPage',
|
||||
name: 'score',
|
||||
footerToolbar: [
|
||||
{ type: 'pagination' },
|
||||
{ type: 'switch-per-page' },
|
||||
{ type: 'statistics' },
|
||||
],
|
||||
mode: 'table',
|
||||
affixHeader: true,
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
columns: [
|
||||
{ name: 'code', type: 'text', label: '编号' },
|
||||
{ type: 'text', name: 'name', label: '名称' },
|
||||
{
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '附件管理',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/projects?id=eq.${id}',
|
||||
data: {
|
||||
files: '${files}',
|
||||
},
|
||||
dataType: 'json',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'combo',
|
||||
label: '',
|
||||
name: 'files',
|
||||
mode: 'inline',
|
||||
inputClassName: 'm-l-xs',
|
||||
multiple: true,
|
||||
controls: [
|
||||
{
|
||||
type: 'dataset-picker',
|
||||
bucket: 'mooc',
|
||||
name: 'path',
|
||||
required: true,
|
||||
},
|
||||
{ type: 'tpl', tpl: '$path' },
|
||||
],
|
||||
multiLine: false,
|
||||
joinValues: false,
|
||||
draggable: false,
|
||||
strictMode: false,
|
||||
delimiter: ',',
|
||||
scaffold: '',
|
||||
disabled: false,
|
||||
disabledOn: '',
|
||||
removable: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
},
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
icon: 'fa fa-cog text-warning',
|
||||
tooltip: '附件管理',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'm-r-none m-l-none',
|
||||
},
|
||||
],
|
||||
placeholder: '-',
|
||||
},
|
||||
],
|
||||
bodyClassName: '',
|
||||
initApi: '',
|
||||
initFetch: '',
|
||||
label: '名称',
|
||||
},
|
||||
],
|
||||
type: 'page',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
1042
src/pages/schema/centre/mooc/course.schema.ts
Normal file
1042
src/pages/schema/centre/mooc/course.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
18
src/pages/schema/centre/mooc/resource.schema.ts
Normal file
18
src/pages/schema/centre/mooc/resource.schema.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: {
|
||||
type: 'iframe',
|
||||
src: '/res',
|
||||
height: window.innerHeight - 80,
|
||||
},
|
||||
};
|
||||
window.parent.postMessage(
|
||||
{
|
||||
type: 'amis:resize',
|
||||
data: {
|
||||
height: window.innerHeight - 80
|
||||
}
|
||||
},
|
||||
'*'
|
||||
);
|
||||
export { schema };
|
||||
423
src/pages/schema/centre/notification.schema.ts
Normal file
423
src/pages/schema/centre/notification.schema.ts
Normal file
@@ -0,0 +1,423 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
id: 'u:d0a463529569',
|
||||
columns: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'title',
|
||||
label: '标题',
|
||||
id: 'u:068708870a6a',
|
||||
},
|
||||
{
|
||||
name: 'creator.name',
|
||||
type: 'text',
|
||||
label: '发送人',
|
||||
id: 'u:118fc9ffbf89',
|
||||
},
|
||||
{
|
||||
name: 'created_at',
|
||||
label: '发送时间',
|
||||
type: 'date',
|
||||
id: 'u:9f0439c43297',
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
label: '操作',
|
||||
id: 'u:537781aa8eec',
|
||||
placeholder: '-',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
level: 'link',
|
||||
icon: 'fa fa-eye',
|
||||
size: 'md',
|
||||
tooltip: '删除',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
id: 'u:9731651193a3',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '${title}',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '${data}',
|
||||
wrapperComponent: 'p',
|
||||
inline: false,
|
||||
id: 'u:2f15ac0923a7',
|
||||
},
|
||||
],
|
||||
showCloseButton: true,
|
||||
showErrorMsg: true,
|
||||
showLoading: true,
|
||||
className: 'app-popover',
|
||||
id: 'u:93d802668933',
|
||||
closeOnEsc: true,
|
||||
actions: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
confirmText: '确认删除?',
|
||||
level: 'link',
|
||||
icon: 'fa fa-times text-danger',
|
||||
size: 'md',
|
||||
tooltip: '删除',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
id: 'u:9731651193a3',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
outputVar: 'responseResult',
|
||||
actionType: 'ajax',
|
||||
options: {},
|
||||
api: {
|
||||
url: 'rest/notifications/$id',
|
||||
method: 'delete',
|
||||
requestAdaptor: '',
|
||||
adaptor: '',
|
||||
messages: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
componentId: 'u:d0a463529569',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
width: 200,
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/notifications?select=*,creator:users!notification_creator_id_fkey(id,name),user_notifications!user_notification_notification_id_fkey(users(name))&order=created_at.desc&organization_id=eq.${centre_id}&type=eq.announcement',
|
||||
data: null,
|
||||
},
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
align: 'right',
|
||||
icon: 'fa fa-plus text-primary',
|
||||
level: 'link',
|
||||
label: '',
|
||||
id: 'u:6adf561275c3',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '发送消息',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/notifications',
|
||||
data: {
|
||||
'&': '$$',
|
||||
organization_id: '$centre_id',
|
||||
},
|
||||
dataType: 'json',
|
||||
requestAdaptor:
|
||||
"let notification = {\r\n title: api.data.title,\r\n data: api.data.data,\r\n push2wechat: api.data.push2wechat,\r\n organization_id: api.data.organization_id\r\n};\r\nconsole.log(api)\r\nif (!api.data.push2wechat && api.data.target === 'toorg') {\r\n notification.org_list = `{${api.data.org_list}}`\r\n} else if (!api.data.push2wechat && api.data.target === 'touser') {\r\n notification.user_id_list = `{${api.data.user_id_list}}`\r\n}\r\nreturn {\r\n ...api,\r\n data: notification\r\n}",
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'group',
|
||||
label: false,
|
||||
name: 'title',
|
||||
id: 'u:5cb966ef8b55',
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '',
|
||||
name: 'title',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
columnRatio: 9,
|
||||
placeholder: '消息标题',
|
||||
id: 'u:2e6661386694',
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
name: 'push2wechat',
|
||||
option: '是否推送微信消息',
|
||||
mode: 'normal',
|
||||
label: '',
|
||||
optionAtLeft: false,
|
||||
trueValue: true,
|
||||
falseValue: false,
|
||||
value: false,
|
||||
id: 'u:90d1abf14f6c',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'control',
|
||||
label: '发送给谁',
|
||||
mode: 'normal',
|
||||
id: 'u:9064f08b91d6',
|
||||
className: 'm-t-sm',
|
||||
visibleOn: '!this.push2wechat',
|
||||
body: [
|
||||
{
|
||||
type: 'grid',
|
||||
columns: [
|
||||
{
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '',
|
||||
name: 'target',
|
||||
options: [
|
||||
{
|
||||
label: '所有人',
|
||||
value: 'toall',
|
||||
},
|
||||
{
|
||||
label: '指定班级',
|
||||
value: 'toorg',
|
||||
},
|
||||
{
|
||||
label: '指定人',
|
||||
value: 'touser',
|
||||
},
|
||||
],
|
||||
checkAll: false,
|
||||
required: true,
|
||||
value: 'touser',
|
||||
visibleOn: '',
|
||||
className: '1css',
|
||||
labelClassName: '2css',
|
||||
inputClassName: '3css',
|
||||
id: 'u:150a4240cd56',
|
||||
},
|
||||
],
|
||||
id: 'u:65ffd686a587',
|
||||
},
|
||||
{
|
||||
body: [
|
||||
{
|
||||
label: '',
|
||||
name: 'org_list',
|
||||
type: 'picker',
|
||||
visibleOn: "this.target === 'toorg'",
|
||||
required: true,
|
||||
pickerSchema: {
|
||||
mode: 'list',
|
||||
listItem: {
|
||||
title: '${label}',
|
||||
},
|
||||
messages: {},
|
||||
syncLocation: false,
|
||||
defaultParams: {
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
},
|
||||
},
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?type=eq.class',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
multiple: true,
|
||||
mode: 'normal',
|
||||
modalMode: 'dialog',
|
||||
joinValues: true,
|
||||
id: 'u:8cb5375b4bfb',
|
||||
},
|
||||
{
|
||||
type: 'picker',
|
||||
name: 'user_id_list',
|
||||
visibleOn: "this.target === 'touser'",
|
||||
required: true,
|
||||
pickerSchema: {
|
||||
mode: 'list',
|
||||
listItem: {
|
||||
title: '${label}',
|
||||
},
|
||||
messages: {},
|
||||
footerToolbar: [
|
||||
{
|
||||
type: 'pagination',
|
||||
tpl: '内容',
|
||||
},
|
||||
],
|
||||
defaultParams: {
|
||||
perPage: 10,
|
||||
page: 1,
|
||||
},
|
||||
syncLocation: false,
|
||||
filter: {
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
wrapWithPanel: false,
|
||||
submitOnInit: false,
|
||||
submitOnChange: false,
|
||||
target: '',
|
||||
resetAfterSubmit: false,
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'keywords',
|
||||
clearable: true,
|
||||
size: 'md',
|
||||
label: '学号',
|
||||
id: 'u:8a6776c917cd',
|
||||
mode: 'inline',
|
||||
},
|
||||
{
|
||||
type: 'control',
|
||||
id: 'u:fa7b6a89a71b',
|
||||
label: '',
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
level: 'primary',
|
||||
actionType: 'submit',
|
||||
label: '搜索',
|
||||
id: 'u:544f3de716b9',
|
||||
reload:
|
||||
'userlist?code=like.*${keywords}*',
|
||||
},
|
||||
],
|
||||
mode: 'inline',
|
||||
},
|
||||
],
|
||||
id: 'u:e95d6e701a18',
|
||||
},
|
||||
name: 'userlist',
|
||||
},
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/users?role=eq.student&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n label: `${item.code}-${item.name}`,\r\n value: item.id\r\n }\r\n })\r\n }\r\n}',
|
||||
data: {
|
||||
'&': '$$',
|
||||
keywords: '__undefined',
|
||||
},
|
||||
},
|
||||
modalMode: 'dialog',
|
||||
multiple: true,
|
||||
joinValues: false,
|
||||
mode: 'normal',
|
||||
label: '',
|
||||
extractValue: true,
|
||||
id: 'u:eff885fa85ae',
|
||||
},
|
||||
],
|
||||
id: 'u:5352be84ebdb',
|
||||
},
|
||||
],
|
||||
className: '',
|
||||
id: 'u:7a1bff6ac12a',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'input-rich-text',
|
||||
label: '内容',
|
||||
name: 'data',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
inputClassName: 'm-t-sm',
|
||||
options: {
|
||||
menubar: false,
|
||||
},
|
||||
id: 'u:1ff80f85808d',
|
||||
},
|
||||
],
|
||||
id: 'u:b2bb8e58c569',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:d0a463529569',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'md',
|
||||
id: 'u:50bb302aecfa',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:2a8cd9b988ff',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:2e2b83a9fcd0',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
size: 'lg',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
id: 'u:cd729a982c31',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
3665
src/pages/schema/centre/plan/course.schema.ts
Normal file
3665
src/pages/schema/centre/plan/course.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
929
src/pages/schema/centre/plan/group.schema.ts
Normal file
929
src/pages/schema/centre/plan/group.schema.ts
Normal file
@@ -0,0 +1,929 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
id: 'u:90c6fa536042',
|
||||
mode: 'inline',
|
||||
name: 'filter',
|
||||
title: '表单',
|
||||
className: 'm-b-sm',
|
||||
submitOnChange: true,
|
||||
submitOnInit: true,
|
||||
reload:
|
||||
'group?course_id=$course_id&class_ids=$class_ids&studentgroup_ids=$studentgroup_ids',
|
||||
wrapWithPanel: false,
|
||||
body: [
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
mode: 'inline',
|
||||
label: '课程',
|
||||
name: 'course_id',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/courses?organization_id=eq.$centre_id&semester_id=eq.$currentSemester&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}(${item.code})`,\r\n value: item.id\r\n }\r\n })\r\n}',
|
||||
},
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
className: 'm-l-sm',
|
||||
inputClassName: 'm-l-xs',
|
||||
id: 'u:23277f1965f7',
|
||||
},
|
||||
],
|
||||
id: 'u:8575c6f7cefc',
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'tree-select',
|
||||
label: '班级',
|
||||
name: 'class_ids',
|
||||
mode: 'horizontal',
|
||||
multiple: true,
|
||||
joinValues: false,
|
||||
extractValue: true,
|
||||
onlyChildren: true,
|
||||
clearable: true,
|
||||
searchable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?select=*,dicts:dicts!organization_type_fkey(*),course2orgs(*)&path=cd.root&course2orgs.course_id=eq.${course_id}&order=name',
|
||||
adaptor:
|
||||
"const recursive = id => {\r\n const nodes = payload.data.items.filter(item => {\r\n return item.parent === id && (\r\n (item.type === 'class' ||\r\n item.type === 'reelectclass') &&\r\n item.course2orgs.length ||\r\n item.type !== 'class' &&\r\n item.type !== 'reelectclass' &&\r\n item.type !== 'studentgroup'\r\n )\r\n })\r\n const result = []\r\n\r\n if (nodes.length) {\r\n nodes.forEach(item => {\r\n const children = recursive(item.id)\r\n\r\n if (children)\r\n result.push({\r\n ...item,\r\n label: item.name,\r\n children,\r\n })\r\n else if (\r\n item.type === 'class' ||\r\n item.type === 'reelectclass'\r\n ) result.push({\r\n ...item,\r\n label: item.name,\r\n value: item.id,\r\n })\r\n })\r\n\r\n if (result.length) return result\r\n } else return null\r\n}\r\n\r\nlet school = payload.data.items.find(x => x.type === 'school')\r\n\r\nreturn {\r\n ...payload,\r\n data: recursive(school.id)\r\n}\r\n",
|
||||
requestAdaptor:
|
||||
'if (api.query.course2orgs.course_id === "eq.") {\r\n \n api.url = api.url.replace("&course2orgs[course_id]=eq.", "")\r\n}\r\nif (api.query.course2orgs.course_id === "eq.undefined") {\r\n api.url = api.url.replace("&course2orgs[course_id]=eq.undefined", "")\r\n}',
|
||||
sendOn: 'course_id',
|
||||
messages: {},
|
||||
},
|
||||
withChildren: true,
|
||||
initiallyOpen: false,
|
||||
id: 'u:42ede681efa8',
|
||||
autoCheckChildren: true,
|
||||
enableNodePath: false,
|
||||
showIcon: true,
|
||||
heightAuto: true,
|
||||
virtualThreshold: false,
|
||||
},
|
||||
],
|
||||
id: 'u:f2885926e84d',
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'tree-select',
|
||||
label: '分组',
|
||||
name: 'studentgroup_ids',
|
||||
mode: 'horizontal',
|
||||
multiple: true,
|
||||
joinValues: false,
|
||||
extractValue: true,
|
||||
onlyChildren: true,
|
||||
clearable: true,
|
||||
searchable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?select=*,dicts:dicts!organization_type_fkey(*),course2orgs(*)&path=cd.root&course2orgs.course_id=eq.${course_id}&order=name',
|
||||
adaptor:
|
||||
"const recursive = id => {\r\n const nodes = payload.data.items.filter(item => {\r\n return item.parent === id && (\r\n item.type === 'studentgroup' &&\r\n item.course2orgs.length ||\r\n item.type !== 'class' &&\r\n item.type !== 'reelectclass' &&\r\n item.type !== 'studentgroup'\r\n )\r\n })\r\n const result = []\r\n\r\n if (nodes.length) {\r\n nodes.forEach(item => {\r\n const children = recursive(item.id)\r\n\r\n if (children)\r\n result.push({\r\n ...item,\r\n label: item.name,\r\n children,\r\n })\r\n else if (item.type === 'studentgroup')\r\n result.push({\r\n ...item,\r\n label: item.name,\r\n value: item.id,\r\n })\r\n })\r\n\r\n if (result.length) return result\r\n } else return null\r\n}\r\n\r\nlet school = payload.data.items.find(x => x.type === 'school')\r\n\r\nreturn {\r\n ...payload,\r\n data: recursive(school.id)\r\n}\r\n",
|
||||
requestAdaptor:
|
||||
'if (api.query.course2orgs.course_id === "eq.") {\r\n \n api.url = api.url.replace("&course2orgs[course_id]=eq.", "")\r\n}\r\nif (api.query.course2orgs.course_id === "eq.undefined") {\r\n api.url = api.url.replace("&course2orgs[course_id]=eq.undefined", "")\r\n}',
|
||||
sendOn: 'course_id',
|
||||
messages: {},
|
||||
},
|
||||
withChildren: true,
|
||||
initiallyOpen: false,
|
||||
id: 'u:42ede681efa8',
|
||||
autoCheckChildren: true,
|
||||
enableNodePath: false,
|
||||
showIcon: true,
|
||||
heightAuto: true,
|
||||
virtualThreshold: false,
|
||||
},
|
||||
],
|
||||
id: 'u:bfa2b4c7ed01',
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
lineStyle: 'solid',
|
||||
id: 'u:e807ff502b0a',
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/groupusers?semester_id=eq.$currentSemester&course_id=eq.$course_id&or=(class_id.in.($class_ids),studentgroup_id.in.($studentgroup_ids))&order=student_code',
|
||||
sendOn: 'this.course_id && (this.class_ids || this.studentgroup_ids)',
|
||||
data: {
|
||||
orderBy: '$orderBy',
|
||||
orderDir: '$orderDir',
|
||||
},
|
||||
requestAdaptor:
|
||||
"if (api.query.orderDir) {\r\n api.url = api.url.replace('&order=student_code', '');\r\n api.url = api.url.replace('&orderBy=' + api.query.orderBy + '&orderDir=' + api.query.orderDir, '&order=' + api.query.orderBy + '.' + api.query.orderDir);\r\n} else if (api.query.orderBy) {\r\n api.url = api.url.replace('&order=student_code', '');\r\n api.url = api.url.replace('&orderBy=' + api.query.orderBy + '&orderDir=', '&order=' + api.query.orderBy);\r\n} else {\r\n api.url = api.url.replace('&orderBy=&orderDir=', '');\r\n}\r\n\r\nreturn api;",
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map((item, index) => {\r\n return {\r\n ...item,\r\n n: index + 1\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
align: 'right',
|
||||
label: '导入分组',
|
||||
icon: 'fa fa-upload',
|
||||
level: 'warning',
|
||||
id: 'u:ce034375ceff',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '分组导入',
|
||||
size: 'xl',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
mode: 'inline',
|
||||
label: '课程',
|
||||
name: 'course_id',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/courses?organization_id=eq.$centre_id&semester_id=eq.$currentSemester&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}(${item.code})`,\r\n value: item.id\r\n }\r\n })\r\n}',
|
||||
},
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
className: 'm-l-sm',
|
||||
inputClassName: 'm-l-xs',
|
||||
id: 'u:7cfa89be2243',
|
||||
},
|
||||
{
|
||||
type: 'tree-select',
|
||||
label: '上级部门',
|
||||
name: 'parent_id',
|
||||
mode: 'inline',
|
||||
extractValue: true,
|
||||
onlyChildren: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?select=*&or=(type.eq.school,type.eq.faculty,type.eq.centre)&path=cd.root&order=code',
|
||||
adaptor:
|
||||
"const recursive = id => {\r\n let nodes = payload.data.items.filter(x => {\r\n return x.parent === id\r\n })\r\n\r\n return nodes.map(item => {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: `${item.id}`,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}\r\n\r\nlet school = payload.data.items.find(x => x.type === 'school')\r\n\r\nreturn {\r\n ...payload,\r\n data: recursive(school.id)\r\n}\r\n",
|
||||
sendOn: '',
|
||||
},
|
||||
withChildren: true,
|
||||
initiallyOpen: false,
|
||||
searchable: true,
|
||||
clearable: false,
|
||||
heightAuto: true,
|
||||
virtualThreshold: false,
|
||||
id: 'u:37ca2401871c',
|
||||
},
|
||||
{
|
||||
type: 'excel-import-group',
|
||||
name: 'groups',
|
||||
label: '',
|
||||
mode: 'normal',
|
||||
hiddenOn: '${!course_id || !parent_id}',
|
||||
id: 'u:a8c9dbf3d073',
|
||||
},
|
||||
{
|
||||
type: 'input-table',
|
||||
name: 'groups',
|
||||
label: '',
|
||||
columns: [
|
||||
{
|
||||
label: 'Excel 行号',
|
||||
name: 'no',
|
||||
id: 'u:a31efd3393b0',
|
||||
},
|
||||
{
|
||||
name: 'user_code',
|
||||
label: '*学号',
|
||||
classNameExpr:
|
||||
"<%= data.no && !data.user_id ? 'bg-danger' : '' %>",
|
||||
id: 'u:8d68435bf70e',
|
||||
},
|
||||
{
|
||||
label: '姓名',
|
||||
name: 'user_name',
|
||||
id: 'u:fc9cb5a03b8f',
|
||||
},
|
||||
{
|
||||
label: '分组',
|
||||
name: 'group',
|
||||
classNameExpr:
|
||||
"<%= !data.no ? 'bg-secondary' : !data.group ? 'bg-danger' : '' %>",
|
||||
id: 'u:ca487d89fd7c',
|
||||
},
|
||||
],
|
||||
mode: 'normal',
|
||||
visibleOn: 'this.groups',
|
||||
strictMode: true,
|
||||
removable: true,
|
||||
editable: false,
|
||||
clearValueOnHidden: true,
|
||||
id: 'u:625db3dac0ec',
|
||||
},
|
||||
],
|
||||
reload: 'grp',
|
||||
initApi: {
|
||||
method: 'get',
|
||||
url: 'rest/report_check_items?organization_id=eq.$centre_id&order=type_order,type,display_order,comment',
|
||||
},
|
||||
id: 'u:ccd0d1410b18',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'View',
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
closeOnOutside: false,
|
||||
showCloseButton: true,
|
||||
actions: [],
|
||||
id: 'u:d85ab95adb25',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'export-excel',
|
||||
align: 'right',
|
||||
label: '导出',
|
||||
icon: 'fa fa-download',
|
||||
level: 'primary',
|
||||
hiddenOn: '${!class_ids && !studentgroup_ids}',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/groupusers?semester_id=eq.$currentSemester&course_id=eq.$course_id&or=(class_id.in.($class_ids),studentgroup_id.in.($studentgroup_ids))&order=student_code',
|
||||
data: {
|
||||
orderBy: '$orderBy',
|
||||
orderDir: '$orderDir',
|
||||
},
|
||||
requestAdaptor:
|
||||
"if (api.query.orderDir) {\r\n api.url = api.url.replace('&order=student_code', '');\r\n api.url = api.url.replace('&orderBy=' + api.query.orderBy + '&orderDir=' + api.query.orderDir, '&order=' + api.query.orderBy + '.' + api.query.orderDir);\r\n} else if (api.query.orderBy) {\r\n api.url = api.url.replace('&order=student_code', '');\r\n api.url = api.url.replace('&orderBy=' + api.query.orderBy + '&orderDir=', '&order=' + api.query.orderBy);\r\n} else {\r\n api.url = api.url.replace('&orderBy=&orderDir=', '');\r\n}\r\n\r\nreturn api;",
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map((item, index) => {\r\n return {\r\n ...item,\r\n n: index + 1\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
filename: '分组',
|
||||
exportColumns: [
|
||||
{
|
||||
label: '*学号',
|
||||
name: 'student_code',
|
||||
},
|
||||
{
|
||||
label: '姓名',
|
||||
name: 'student_name',
|
||||
},
|
||||
{
|
||||
label: '*分组',
|
||||
name: 'studentgroup_name',
|
||||
},
|
||||
],
|
||||
id: 'u:a44ab3164746',
|
||||
},
|
||||
{
|
||||
type: 'bulk-actions',
|
||||
},
|
||||
],
|
||||
bulkActions: [
|
||||
{
|
||||
label: '批量设置',
|
||||
type: 'button',
|
||||
id: 'u:99fa372537dc',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '批量设置',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/batch_group',
|
||||
data: {
|
||||
org_id: '$org_id',
|
||||
selectedItems: '$selectedItems',
|
||||
},
|
||||
heades: {
|
||||
Prefer: 'params=single-object',
|
||||
},
|
||||
requestAdaptor:
|
||||
'const user2org = api.data.selectedItems.map(item => ({\r\n user_id: item.student_id,\r\n old_org_id: item.studentgroup_id,\r\n new_org_id: api.data.org_id,\r\n}))\r\n\r\napi.data = { user2org }\r\n\r\nreturn api',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '分组',
|
||||
name: 'org_id',
|
||||
mode: 'normal',
|
||||
cascade: true,
|
||||
searchable: true,
|
||||
required: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?select=*,course2orgs!inner(*),dicts:dicts!organization_type_fkey(*)&type=eq.studentgroup&path=cd.root.1&course2orgs.course_id=eq.${course_id}&order=name',
|
||||
requestAdaptor:
|
||||
'if (api.query.course2orgs.course_id === "eq.") {\r\n api.url = api.url.replace("&course2orgs[course_id]=eq.", "")\r\n}\r\nif (api.query.course2orgs.course_id === "eq.undefined") {\r\n api.url = api.url.replace("&course2orgs[course_id]=eq.undefined", "")\r\n}',
|
||||
adaptor:
|
||||
'return {\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id\r\n }\r\n })\r\n}',
|
||||
sendOn: 'course_id',
|
||||
},
|
||||
extractValue: true,
|
||||
id: 'u:8527f8d399e6',
|
||||
},
|
||||
],
|
||||
id: 'u:e9215f23110f',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:148f700a4963',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:ce48989de1f9',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:f8c9f57fa665',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:8cccdee1e21b',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '批量清除',
|
||||
confirmText: '确认清除分组吗?',
|
||||
type: 'button',
|
||||
id: 'u:5eb51d3e6692',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
outputVar: 'responseResult',
|
||||
actionType: 'ajax',
|
||||
options: {},
|
||||
api: {
|
||||
url: 'rest/user_orgs',
|
||||
method: 'delete',
|
||||
requestAdaptor:
|
||||
'api.url = api.url + `?id=in.(${api.data.selectedItems.filter(item => item.user_studentgroup_id).map(item => item.user_studentgroup_id).toString()})`\r\ndelete api.data.selectedItems\r\n\r\nreturn api',
|
||||
adaptor: '',
|
||||
messages: {},
|
||||
data: {
|
||||
selectedItems: '$selectedItems',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
componentId: 'u:148f700a4963',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '批量分组',
|
||||
type: 'button',
|
||||
id: 'u:0371266b981c',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '批量分组',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
id: 'u:f39e60c373bc',
|
||||
body: [
|
||||
{
|
||||
type: 'tree-select',
|
||||
label: '上级部门',
|
||||
name: 'parent',
|
||||
mode: 'normal',
|
||||
extractValue: true,
|
||||
onlyChildren: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?select=*&or=(type.eq.school,type.eq.faculty,type.eq.centre)&path=cd.root&order=code',
|
||||
adaptor:
|
||||
"const recursive = id => {\r\n let nodes = payload.data.items.filter(x => {\r\n return x.parent === id\r\n })\r\n\r\n return nodes.map(item => {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: `${item.id}`,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}\r\n\r\nlet school = payload.data.items.find(x => x.type === 'school')\r\n\r\nreturn {\r\n ...payload,\r\n data: recursive(school.id)\r\n}\r\n",
|
||||
sendOn: '',
|
||||
},
|
||||
withChildren: true,
|
||||
required: true,
|
||||
initiallyOpen: false,
|
||||
searchable: true,
|
||||
clearable: false,
|
||||
heightAuto: true,
|
||||
virtualThreshold: false,
|
||||
id: 'u:1efc22d916f7',
|
||||
},
|
||||
{
|
||||
name: 'groups',
|
||||
label: '新分组',
|
||||
type: 'input-array',
|
||||
items: {
|
||||
type: 'input-text',
|
||||
},
|
||||
removable: true,
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:0a41abbda787',
|
||||
},
|
||||
{
|
||||
label: '选择分组方式',
|
||||
type: 'select',
|
||||
name: 'way',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
options: [
|
||||
{
|
||||
label: '顺序分组',
|
||||
value: 'order',
|
||||
},
|
||||
{
|
||||
label: '随机分组',
|
||||
value: 'random',
|
||||
},
|
||||
],
|
||||
id: 'u:436b4893fdfc',
|
||||
},
|
||||
{
|
||||
type: 'batch-group',
|
||||
label: false,
|
||||
id: 'u:04fbf3b1b31c',
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:148f700a4963',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '关闭',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:148f700a4963',
|
||||
actionType: 'reload',
|
||||
data: null,
|
||||
},
|
||||
{
|
||||
actionType: 'closeDialog',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
id: 'u:f7831022039a',
|
||||
},
|
||||
],
|
||||
id: 'u:3ead95907fbd',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
footerToolbar: [],
|
||||
columns: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'n',
|
||||
label: '序号',
|
||||
id: 'u:dc3b48ab2a2a',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'student_code',
|
||||
sortable: true,
|
||||
label: '学号',
|
||||
id: 'u:9233f140d314',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'student_name',
|
||||
sortable: true,
|
||||
label: '姓名',
|
||||
id: 'u:cb913014ed05',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
sortable: true,
|
||||
label: '班级',
|
||||
name: 'class_name',
|
||||
id: 'u:d0de67b68472',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '课程',
|
||||
name: 'course_name',
|
||||
id: 'u:ecd0d94b6c58',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
sortable: true,
|
||||
label: '分组',
|
||||
name: 'studentgroup_name',
|
||||
id: 'u:de1610df96e6',
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
level: 'link',
|
||||
icon: 'fa fa-cog text-info',
|
||||
size: 'md',
|
||||
tooltip: '设置分组',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
hiddenOn: '${studentgroup_id}',
|
||||
id: 'u:ccec6786dabf',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '设置分组',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/user_orgs',
|
||||
data: {
|
||||
user_id: '$student_id',
|
||||
organization_id: '$group_id',
|
||||
},
|
||||
dataType: 'json',
|
||||
},
|
||||
id: 'u:a20070f8b13c',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '分组',
|
||||
name: 'group_id',
|
||||
mode: 'normal',
|
||||
cascade: true,
|
||||
searchable: true,
|
||||
required: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?select=*,course2orgs!inner(*),dicts:dicts!organization_type_fkey(*)&type=eq.studentgroup&path=cd.root.1&course2orgs.course_id=eq.${course_id}&order=name',
|
||||
requestAdaptor:
|
||||
'if (api.query.course2orgs.course_id === "eq.") {\r\n api.url = api.url.replace("&course2orgs[course_id]=eq.", "")\r\n}\r\nif (api.query.course2orgs.course_id === "eq.undefined") {\r\n api.url = api.url.replace("&course2orgs[course_id]=eq.undefined", "")\r\n}',
|
||||
adaptor:
|
||||
'return {\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id\r\n }\r\n })\r\n}',
|
||||
sendOn: 'course_id',
|
||||
},
|
||||
extractValue: true,
|
||||
id: 'u:75a5e1ef2a35',
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:148f700a4963',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:05921ba2d106',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:5a3bed9137d5',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:9982eb0126f2',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
level: 'link',
|
||||
icon: 'fa fa-pencil text-warning',
|
||||
size: 'md',
|
||||
tooltip: '修改分组',
|
||||
tooltipPlacement: 'top',
|
||||
hiddenOn: '${!studentgroup_id}',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
id: 'u:b1bda5ad847a',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '修改分组',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/user_orgs?user_id=eq.$student_id&organization_id=eq.$studentgroup_id',
|
||||
data: {
|
||||
organization_id: '$group_id',
|
||||
},
|
||||
dataType: 'json',
|
||||
},
|
||||
id: 'u:2b0b7a8f5877',
|
||||
body: [
|
||||
{
|
||||
type: 'tree-select',
|
||||
label: '原分组',
|
||||
name: 'studentgroup_id',
|
||||
searchable: true,
|
||||
readOnly: true,
|
||||
onlyChildren: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?select=*&or=(type.eq.school,type.eq.faculty,type.eq.centre,type.eq.studentgroup)&path=cd.root&order=code',
|
||||
adaptor:
|
||||
"const recursive = id => {\r\n let nodes = payload.data.items.filter(x => {\r\n return x.parent === id\r\n })\r\n\r\n return nodes.map(item => {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: item.id,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}\r\n\r\nlet school = payload.data.items.find(x => x.type === 'school')\r\n\r\nreturn {\r\n ...payload,\r\n data: recursive(school.id)\r\n}\r\n",
|
||||
},
|
||||
withChildren: true,
|
||||
initiallyOpen: false,
|
||||
heightAuto: true,
|
||||
virtualThreshold: false,
|
||||
id: 'u:9b803b7fffbe',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '新分组',
|
||||
name: 'group_id',
|
||||
mode: 'normal',
|
||||
cascade: true,
|
||||
searchable: true,
|
||||
required: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?select=*,course2orgs!inner(*),dicts:dicts!organization_type_fkey(*)&type=eq.studentgroup&path=cd.root.1&course2orgs.course_id=eq.${course_id}&order=name',
|
||||
requestAdaptor:
|
||||
'if (api.query.course2orgs.course_id === "eq.") {\r\n api.url = api.url.replace("&course2orgs[course_id]=eq.", "")\r\n}\r\nif (api.query.course2orgs.course_id === "eq.undefined") {\r\n api.url = api.url.replace("&course2orgs[course_id]=eq.undefined", "")\r\n}',
|
||||
adaptor:
|
||||
'return {\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id\r\n }\r\n })\r\n}',
|
||||
sendOn: 'course_id',
|
||||
},
|
||||
extractValue: true,
|
||||
id: 'u:fd14fbccb8fc',
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:148f700a4963',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:94b2f1f09462',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:0c74d636f491',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:2e60b730d920',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
confirmText: '确认清除分组吗?',
|
||||
level: 'link',
|
||||
icon: 'fa fa-trash text-danger',
|
||||
size: 'md',
|
||||
tooltip: '清除分组',
|
||||
tooltipPlacement: 'top',
|
||||
hiddenOn: '${!studentgroup_id}',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
id: 'u:e719d85aa542',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
outputVar: 'responseResult',
|
||||
actionType: 'ajax',
|
||||
options: {},
|
||||
api: {
|
||||
url: 'rest/user_orgs?user_id=eq.$student_id&organization_id=eq.$studentgroup_id',
|
||||
method: 'delete',
|
||||
requestAdaptor: '',
|
||||
adaptor: '',
|
||||
messages: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
componentId: 'u:148f700a4963',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: '-',
|
||||
width: 200,
|
||||
id: 'u:b59ecbce78a6',
|
||||
},
|
||||
],
|
||||
name: 'grp',
|
||||
bodyClassName: '',
|
||||
title: '',
|
||||
initFetch: true,
|
||||
columnsTogglable: true,
|
||||
id: 'u:148f700a4963',
|
||||
},
|
||||
],
|
||||
title: '',
|
||||
messages: {},
|
||||
id: 'u:9e76719da6ac',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
525
src/pages/schema/centre/plan/project.schema.ts
Normal file
525
src/pages/schema/centre/plan/project.schema.ts
Normal file
@@ -0,0 +1,525 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/projects?select=*&organization_id=eq.${centre_id}&semester_id=eq.${currentSemester}&order=code',
|
||||
data: {
|
||||
page: '$page',
|
||||
perPage: '$perPage',
|
||||
},
|
||||
},
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
id: 'u:b88c442cf1c9',
|
||||
align: 'right',
|
||||
label: '添加',
|
||||
icon: 'fa fa-plus',
|
||||
level: 'default',
|
||||
className: 'm-l-xs',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '添加实验',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/projects',
|
||||
data: {
|
||||
code: '${project_code}',
|
||||
name: '${project_name}',
|
||||
intro: '${project_intro}',
|
||||
organization_id: '${centre_id}',
|
||||
type: '${type}',
|
||||
free_schedule: '${free_schedule}',
|
||||
},
|
||||
dataType: 'json',
|
||||
requestAdaptor:
|
||||
'if(api.data.type===\'examination\') {\r\n api.data.score_item=[{"item": "exp_preparation", "label": "实验预习", "weight": 1}];\r\n}\r\nreturn api;',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '编号',
|
||||
name: 'project_code',
|
||||
value: '',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:4a4471a8f6fd',
|
||||
},
|
||||
{
|
||||
label: '名称',
|
||||
type: 'input-text',
|
||||
name: 'project_name',
|
||||
value: '',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:5d11604bb560',
|
||||
},
|
||||
{
|
||||
label: '类型',
|
||||
type: 'select',
|
||||
name: 'type',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
checkAll: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/dicts?select=dictkey,dictvalue&typecode=eq.012',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n label: item.dictvalue,\r\n value: item.dictkey\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
value: 'experiment',
|
||||
options: [
|
||||
{
|
||||
label: '实验课',
|
||||
value: 'experiment',
|
||||
},
|
||||
],
|
||||
id: 'u:4f93120dc92a',
|
||||
},
|
||||
{
|
||||
label: '学生自主安排时间',
|
||||
type: 'checkbox',
|
||||
name: 'free_schedule',
|
||||
mode: 'inline',
|
||||
value: false,
|
||||
id: 'u:d8de66ea2a6b',
|
||||
},
|
||||
{
|
||||
label: '描述',
|
||||
type: 'input-rich-text',
|
||||
name: 'project_intro',
|
||||
mode: 'normal',
|
||||
options: {
|
||||
menubar: false,
|
||||
},
|
||||
id: 'u:41a4ab0a0f14',
|
||||
},
|
||||
],
|
||||
id: 'u:87d3612f0e3f',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:faff85507083',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:d6cc5749dac1',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:25b4e52d9f7a',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:6f9f855502d6',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'export-excel',
|
||||
align: 'right',
|
||||
label: '实验导出',
|
||||
icon: 'fa fa-download',
|
||||
level: 'primary',
|
||||
id: 'u:d57d6b7f1525',
|
||||
api: 'rest/projects?select=*,dicts:dicts!project_type_fk(dictkey,dictvalue)&organization_id=eq.$centre_id&semester_id=eq.$currentSemester&order=code',
|
||||
filename: '实验',
|
||||
exportColumns: [
|
||||
{
|
||||
label: '*编号',
|
||||
name: 'code',
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: '*名称',
|
||||
},
|
||||
{
|
||||
name: 'dicts.dictvalue',
|
||||
label: '*类型',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
footerToolbar: [
|
||||
{
|
||||
type: 'pagination',
|
||||
},
|
||||
{
|
||||
type: 'switch-per-page',
|
||||
},
|
||||
{
|
||||
type: 'statistics',
|
||||
},
|
||||
],
|
||||
perPageField: 'perPage',
|
||||
pageField: 'page',
|
||||
mode: 'table',
|
||||
affixHeader: true,
|
||||
id: 'u:faff85507083',
|
||||
name: 'score',
|
||||
columns: [
|
||||
{
|
||||
name: 'code',
|
||||
type: 'text',
|
||||
label: '编号',
|
||||
id: 'u:bad42a88a366',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
label: '名称',
|
||||
id: 'u:d9f55c020ac5',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '评分项',
|
||||
name: 'score_item',
|
||||
tpl: "<% if (this.score_item.length > 0) {\n this.score_item.forEach(score => { %>\n <% if (score.item === 'exp_report') { %>\n <span class='label label-info' style='margin-right: 1px; font-size: smaller;'> <%= score.label %> (<%= score.weight %>)\n <% if (score.isRecorded) { %>\n <i class='fa fa-pencil'></i>\n <% } %>\n </span>\n <% } else if (score.item === 'exp_data') { %>\n <span class='label label-success' style='margin-right: 1px; font-size: smaller;'> <%= score.label %> (<%= score.weight %>)\n <% if (score.isRecorded) { %>\n <i class='fa fa-pencil'></i>\n <% } %>\n </span>\n <% } else if (score.item === 'exp_preparation') { %>\n <span class='label label-danger' style='margin-right: 1px; font-size: smaller;'> <%= score.label %> (<%= score.weight %>)\n <% if (score.isRecorded) { %>\n <i class='fa fa-pencil'></i>\n <% } %>\n </span>\n <% } else if (score.item === 'exp_operation') { %>\n <span class='label label-warning' style='margin-right: 1px; font-size: smaller;'> <%= score.label %> (<%= score.weight %>)\n <% if (score.isRecorded) { %>\n <i class='fa fa-pencil'></i>\n <% } %>\n </span>\n <% } else if (score.item === 'exp_notebook') { %>\n <span class='label' style='margin-right: 1px; background: purple; font-size: smaller;'> <%= score.label %> (<%= score.weight %>)\n <% if (score.isRecorded) { %>\n <i class='fa fa-pencil'></i>\n <% } %>\n </span>\n <% } else if (score.item === 'exp_process_assessment') { %>\n <span class='label' style='margin-right: 1px; background: orange; font-size: smaller;'> <%= score.label %> (<%= score.weight %>)\n <% if (score.isRecorded) { %>\n <i class='fa fa-pencil'></i>\n <% } %>\n </span>\n <% } else if (score.item === 'exp_team') { %>\n <span class='label' style='margin-right: 1px; background: pink; font-size: smaller;'> <%= score.label %> (<%= score.weight %>)\n <% if (score.isRecorded) { %>\n <i class='fa fa-pencil'></i>\n <% } %>\n </span>\n <% } else { %>\n <span class='label' style=\"margin-right: 1px; background: #374151; font-size: smaller;\"> <%= score.label %> (<%= score.weight %>)\n <% if (score.isRecorded) { %>\n <i class='fa fa-pencil'></i>\n <% } %>\n </span>\n <% } %>\n <% }) %>\n <% } %>",
|
||||
inline: false,
|
||||
id: 'u:6fb5baeecfab',
|
||||
},
|
||||
{
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
icon: 'fa fa-pencil text-info',
|
||||
tooltip: '修改',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'm-r-none m-l-none',
|
||||
id: 'u:74e446e57e2b',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '修改实验',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/projects?id=eq.${id}',
|
||||
data: {
|
||||
code: '${code}',
|
||||
name: '${name}',
|
||||
intro: '${intro}',
|
||||
type: '${type}',
|
||||
free_schedule: '${free_schedule}',
|
||||
},
|
||||
dataType: 'json',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '编号',
|
||||
name: 'code',
|
||||
value: '',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:45350664b507',
|
||||
},
|
||||
{
|
||||
label: '名称',
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
value: '',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:8e70cec48464',
|
||||
},
|
||||
{
|
||||
label: '类型',
|
||||
type: 'select',
|
||||
name: 'type',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
options: [],
|
||||
checkAll: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/dicts?select=dictkey,dictvalue&typecode=eq.012',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n label: item.dictvalue,\r\n value: item.dictkey\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
id: 'u:425ae91adc74',
|
||||
},
|
||||
{
|
||||
label: '学生自主安排时间',
|
||||
type: 'checkbox',
|
||||
name: 'free_schedule',
|
||||
mode: 'inline',
|
||||
id: 'u:b43e9dc2a55d',
|
||||
},
|
||||
{
|
||||
label: '描述',
|
||||
type: 'input-rich-text',
|
||||
name: 'intro',
|
||||
value: '',
|
||||
mode: 'normal',
|
||||
id: 'u:84e41b04cf8c',
|
||||
},
|
||||
],
|
||||
id: 'u:3c25e724c688',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:faff85507083',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:98e4cae66fe9',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:8dddacfd03ef',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:9a7c3432ab97',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
confirmText: '确认删除"${name}(${code})"?',
|
||||
level: 'link',
|
||||
icon: 'fa fa-times text-danger',
|
||||
size: 'md',
|
||||
tooltip: '删除',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'm-r-none m-l-none',
|
||||
id: 'u:58e504e36911',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
outputVar: 'responseResult',
|
||||
actionType: 'ajax',
|
||||
options: {},
|
||||
api: {
|
||||
url: 'rest/projects/$id',
|
||||
method: 'delete',
|
||||
requestAdaptor: '',
|
||||
adaptor: '',
|
||||
messages: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
componentId: 'u:faff85507083',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
icon: 'fa fa-cog text-warning',
|
||||
level: 'link',
|
||||
messages: {},
|
||||
title: '报告模板',
|
||||
size: 'md',
|
||||
confirmText: '',
|
||||
tooltip: '修改实验评分项',
|
||||
tooltipPlacement: 'top',
|
||||
className: 'm-r-none m-l-none',
|
||||
iconClassName: 'pull-left',
|
||||
id: 'u:a5fd11fac042',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '修改实验评分项',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/projects/${id}',
|
||||
data: {
|
||||
score_item: '$score_item',
|
||||
},
|
||||
requestAdaptor: '',
|
||||
dataType: 'json',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'projectscoreitem',
|
||||
name: 'score_item',
|
||||
api: {
|
||||
url: 'rest/dicts?typecode=eq.007',
|
||||
},
|
||||
mode: 'normal',
|
||||
id: 'u:e3a860ff8f80',
|
||||
},
|
||||
],
|
||||
id: 'u:3510789a5776',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:faff85507083',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
closeOnOutside: false,
|
||||
showCloseButton: true,
|
||||
size: 'md',
|
||||
id: 'u:34a7904cefc0',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:6fd9b88723bf',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:457223ebeaee',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: '-',
|
||||
id: 'u:33cc39b76ad4',
|
||||
},
|
||||
],
|
||||
bodyClassName: '',
|
||||
initApi: '',
|
||||
initFetch: '',
|
||||
label: '名称',
|
||||
},
|
||||
],
|
||||
id: 'u:b7c1c11acda6',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
69
src/pages/schema/centre/plan/textbook.schema.ts
Normal file
69
src/pages/schema/centre/plan/textbook.schema.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '',
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '数字化教材名称',
|
||||
mode: 'horizontal',
|
||||
required: true,
|
||||
name: 'name',
|
||||
id: 'u:1e67e22855ce',
|
||||
},
|
||||
{
|
||||
type: 'combo',
|
||||
label: false,
|
||||
name: 'cells',
|
||||
required: true,
|
||||
multiple: true,
|
||||
addable: true,
|
||||
removable: true,
|
||||
removableMode: 'icon',
|
||||
addBtn: {
|
||||
label: '新增',
|
||||
level: 'primary',
|
||||
size: 'sm',
|
||||
id: 'u:3300178a20b6',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'vditor',
|
||||
name: 'source',
|
||||
id: 'u:48cbfec9c64d',
|
||||
},
|
||||
],
|
||||
id: 'u:1e67e22855be',
|
||||
},
|
||||
],
|
||||
id: 'u:ccd0d1410b18',
|
||||
feat: 'View',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/test',
|
||||
data: {
|
||||
cells: '$cells',
|
||||
},
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
type: 'textbook-submit',
|
||||
label: '提交',
|
||||
},
|
||||
],
|
||||
wrapWithPanel: true,
|
||||
dsType: 'api',
|
||||
},
|
||||
],
|
||||
title: '',
|
||||
id: 'u:9e76719da6ac',
|
||||
messages: {},
|
||||
asideResizor: false,
|
||||
pullRefresh: {
|
||||
disabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
export { schema };
|
||||
1143
src/pages/schema/centre/report/deduction.schema.ts
Normal file
1143
src/pages/schema/centre/report/deduction.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
1411
src/pages/schema/centre/report/index.schema.ts
Normal file
1411
src/pages/schema/centre/report/index.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
49
src/pages/schema/centre/report/instructions.schema.ts
Normal file
49
src/pages/schema/centre/report/instructions.schema.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '使用说明',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
args: {
|
||||
url: 'https://www.platosoft.org/doc/olms/plugin/mark.html',
|
||||
},
|
||||
actionType: 'url',
|
||||
},
|
||||
],
|
||||
weight: 0,
|
||||
},
|
||||
},
|
||||
size: 'md',
|
||||
level: 'primary',
|
||||
block: false,
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '进入批阅',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
args: {
|
||||
url: '/check',
|
||||
},
|
||||
actionType: 'url',
|
||||
},
|
||||
],
|
||||
weight: 0,
|
||||
},
|
||||
},
|
||||
level: 'primary',
|
||||
blank: true,
|
||||
className: 'm-l',
|
||||
},
|
||||
],
|
||||
title: '报告上传与在线批阅',
|
||||
messages: {},
|
||||
};
|
||||
|
||||
export { schema };
|
||||
1113
src/pages/schema/centre/report/template.schema.ts
Normal file
1113
src/pages/schema/centre/report/template.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
513
src/pages/schema/centre/schedule/auto.schema.ts
Normal file
513
src/pages/schema/centre/schedule/auto.schema.ts
Normal file
@@ -0,0 +1,513 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'scheduler-renderer',
|
||||
name: 'scheduler',
|
||||
body: [],
|
||||
plan: {
|
||||
type: 'button',
|
||||
label: '按钮',
|
||||
actionType: 'drawer',
|
||||
dialog: { title: '系统提示', body: '对你点击了' },
|
||||
drawer: {
|
||||
type: 'drawer',
|
||||
title: '弹框标题',
|
||||
body: [
|
||||
{ type: 'tpl', tpl: '<p>对,你刚刚点击了</p>', inline: false },
|
||||
],
|
||||
},
|
||||
},
|
||||
planlistbutton: {
|
||||
type: 'button',
|
||||
label: '',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '排课计划',
|
||||
body: [
|
||||
{
|
||||
type: 'service',
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '添加计划',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '添加计划',
|
||||
body: [
|
||||
{
|
||||
type: 'wizard',
|
||||
reload: 'plancrud',
|
||||
steps: [
|
||||
{
|
||||
title: '计划设置',
|
||||
mode: 'normal',
|
||||
controls: [
|
||||
{
|
||||
type: 'text',
|
||||
label: '计划名称',
|
||||
name: 'name',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'date-range',
|
||||
label: '日期范围',
|
||||
name: 'date',
|
||||
required: true,
|
||||
format: 'YYYY-MM-DD',
|
||||
minDate: '${semester.since}',
|
||||
maxDate: '${semester.to}',
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
label: '老师每周最多上课数量',
|
||||
name: 'teacherFreqWeekly',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
min: '1',
|
||||
step: 1,
|
||||
value: 10,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
label: '老师每天最多上课数量',
|
||||
name: 'teacherFreqDaily',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
min: '1',
|
||||
step: 1,
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
label: '老师同时上课数量',
|
||||
name: 'maxConcurrentOfTeacher',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
min: '1',
|
||||
step: 1,
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'maxConcurrent',
|
||||
mode: 'normal',
|
||||
label: '同一时段最多排课数量',
|
||||
required: true,
|
||||
value: 4,
|
||||
min: '1',
|
||||
step: 1,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'maxFreqWeekly',
|
||||
mode: 'normal',
|
||||
label: '学生每周上课数',
|
||||
required: true,
|
||||
value: 1,
|
||||
min: '1',
|
||||
step: 1,
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
name: 'fixTeacherMode',
|
||||
mode: 'normal',
|
||||
label: '',
|
||||
required: true,
|
||||
value: false,
|
||||
option: ' 教师跟班模式',
|
||||
optionAtLeft: false,
|
||||
trueValue: true,
|
||||
falseValue: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '资源设置',
|
||||
controls: [
|
||||
{
|
||||
type: 'course-list',
|
||||
label: '课程',
|
||||
name: 'courselist',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/courses?select=id,name,course2projects(projects(id,name,validRoomList,preSubjectList)),course2orgs(orgs(id,name, validTimeslotList:timeslot_class_id_fkey(id,date,period:periods(id,name,startTime:start_time,endTime:end_time))))&organization_id=eq.${centre_id}&semester_id=eq.${currentSemester}&order=code',
|
||||
},
|
||||
required: true,
|
||||
multiple: true,
|
||||
},
|
||||
],
|
||||
mode: 'normal',
|
||||
},
|
||||
{
|
||||
mode: 'normal',
|
||||
title: '工作量设置',
|
||||
controls: [
|
||||
{
|
||||
type: 'workload',
|
||||
name: 'workload',
|
||||
source:
|
||||
'rest/users?select=id,name:name,role,subjectList,studentGroupList,user_orgs(organization_id),timeslotList:timeslot_teacher_id_fkey(id,date,period:periods(id,name,startTime:start_time,endTime:end_time)),secondChoiceTimeslotList:timeslot_teacher_secondary_id_fkey(id,date,period:periods(id,name,startTime:start_time,endTime:end_time))&role=eq.teacher&user_orgs.organization_id=eq.${centre_id}&order=code',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
mode: 'horizontal',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/schedule_plans',
|
||||
requestAdaptor:
|
||||
"return {\r\n ...api,\r\n data: {\r\n name: api.data.name,\r\n date: api.data.date,\r\n status: '待求解',\r\n organization_id: api.data.organization_id,\r\n problem: {\r\n problemConfig: {\r\n fixTeacherMode: api.data.fixTeacherMode,\r\n maxConcurrent: api.data.maxConcurrent,\r\n maxConcurrentOfTeacher: api.data.maxConcurrentOfTeacher,\r\n maxFreqWeekly: api.data.maxFreqWeekly,\r\n teacherFreqWeekly: api.data.teacherFreqWeekly,\r\n teacherFreqDaily: api.data.teacherFreqDaily,\r\n excludeRoomTime: api.data.excludeRoomTime,\r\n excludeTeacherStudentGroup: api.data.excludeTeacherStudentGroup,\r\n excludeTeacherSubject: api.data.excludeTeacherSubject,\r\n excludeTeacherTime: api.data.excludeTeacherTime,\r\n },\r\n courseList: api.data.courselist.map((item) => {\r\n return {\r\n id: item.id,\r\n name: item.name,\r\n subjectList: item.subjectList.map((e) => {\r\n return {\r\n id: e.id,\r\n name: e.name,\r\n abbreviation: item.name,\r\n validRoomList: e.validRoomList,\r\n preSubjectList: e.preSubjectList\r\n };\r\n }),\r\n studentGroupList: item.studentGroupList.map((e) => {\r\n return {\r\n id: e.id,\r\n name: e.name,\r\n validTimeslotList: e.validTimeslotList,\r\n };\r\n }),\r\n };\r\n }),\r\n validTeacherList: api.data.workload.filter(item => item.workload > 0).map((item) => {\r\n return {\r\n id: item.id,\r\n name: item.name,\r\n workload: item.workload,\r\n timeslotList: item.timeslotList,\r\n secondChoiceTimeslotList: item.secondChoiceTimeslotList,\r\n subjectList: item.subjectList,\r\n studentGroupList: item.studentGroupList,\r\n };\r\n }),\r\n },\r\n },\r\n};\r\n",
|
||||
data: {
|
||||
'&': '$$',
|
||||
organization_id: '${centre_id}',
|
||||
excludeRoomTime: '${excludeRoomTime}',
|
||||
excludeTeacherStudentGroup:
|
||||
'${excludeTeacherStudentGroup}',
|
||||
excludeTeacherSubject: '${excludeTeacherSubject}',
|
||||
excludeTeacherTime: '${excludeTeacherTime}',
|
||||
},
|
||||
},
|
||||
name: 'stepper',
|
||||
actionNextSaveLabel: '下一步',
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
actions: [],
|
||||
data: {
|
||||
currentSemester: '${currentSemester}',
|
||||
centre_id: '${centre_id}',
|
||||
semester: '${semester}',
|
||||
excludeRoomTime: '${excludeRoomTime}',
|
||||
excludeTeacherStudentGroup:
|
||||
'${excludeTeacherStudentGroup}',
|
||||
excludeTeacherSubject: '${excludeTeacherSubject}',
|
||||
excludeTeacherTime: '${excludeTeacherTime}',
|
||||
},
|
||||
},
|
||||
className: 'm-b-sm ',
|
||||
block: false,
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/schedule_plans?semester_id=eq.${currentSemester}&organization_id=eq.${centre_id}&order=updated_at.desc',
|
||||
data: { '&': '$$', page: '${page}', perPage: '${perPage}' },
|
||||
},
|
||||
columns: [
|
||||
{ name: 'name', label: '排课计划', type: 'text' },
|
||||
{ type: 'text', label: '状态', name: 'status' },
|
||||
{
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
label: '选择',
|
||||
type: 'button',
|
||||
visibleOn: '!this.is_current',
|
||||
actionType: 'ajax',
|
||||
confirmText: '确认切换排课计划?',
|
||||
className: '',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/schedule_plans/${id}',
|
||||
},
|
||||
size: 'sm',
|
||||
level: 'primary',
|
||||
reload: 'scheduler?plan=${id}',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
visibleOn: '!this.is_current',
|
||||
label: '编辑',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '修改计划',
|
||||
body: [
|
||||
{
|
||||
type: 'wizard',
|
||||
reload: 'plancrud',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/schedule_plans?id=eq.${id}',
|
||||
requestAdaptor:
|
||||
"return {\r\n ...api,\r\n data: {\r\n name: api.data.name,\r\n date: api.data.date,\r\n status: '待求解',\r\n organization_id: api.data.organization_id,\r\n problem: {\r\n problemConfig: {\r\n fixTeacherMode: api.data.fixTeacherMode,\r\n maxConcurrent: api.data.maxConcurrent,\r\n maxConcurrentOfTeacher: api.data.maxConcurrentOfTeacher,\r\n maxFreqWeekly: api.data.maxFreqWeekly,\r\n teacherFreqWeekly: api.data.teacherFreqWeekly,\r\n teacherFreqDaily: api.data.teacherFreqDaily,\r\n excludeRoomTime: api.data.excludeRoomTime,\r\n excludeTeacherStudentGroup: api.data.excludeTeacherStudentGroup,\r\n excludeTeacherSubject: api.data.excludeTeacherSubject,\r\n excludeTeacherTime: api.data.excludeTeacherTime,\r\n },\r\n courseList: api.data.courselist.map((item) => {\r\n return {\r\n id: item.id,\r\n name: item.name,\r\n subjectList: item.subjectList.map((e) => {\r\n return {\r\n id: e.id,\r\n name: e.name,\r\n abbreviation: item.name,\r\n validRoomList: e.validRoomList,\r\n preSubjectList: e.preSubjectList\r\n };\r\n }),\r\n studentGroupList: item.studentGroupList.map((e) => {\r\n return {\r\n id: e.id,\r\n name: e.name,\r\n validTimeslotList: e.validTimeslotList\r\n\r\n };\r\n }),\r\n };\r\n }),\r\n validTeacherList: api.data.workload.filter(item => item.workload > 0).map((item) => {\r\n return {\r\n id: item.id,\r\n name: item.name,\r\n workload: item.workload,\r\n timeslotList: item.timeslotList,\r\n secondChoiceTimeslotList: item.secondChoiceTimeslotList,\r\n subjectList: item.subjectList,\r\n studentGroupList: item.studentGroupList,\r\n };\r\n }),\r\n },\r\n },\r\n};\r\n",
|
||||
data: {
|
||||
'&': '$$',
|
||||
organization_id: '${centre_id}',
|
||||
excludeRoomTime: '${excludeRoomTime}',
|
||||
excludeTeacherStudentGroup:
|
||||
'${excludeTeacherStudentGroup}',
|
||||
excludeTeacherSubject:
|
||||
'${excludeTeacherSubject}',
|
||||
excludeTeacherTime: '${excludeTeacherTime}',
|
||||
},
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
title: '计划设置',
|
||||
mode: 'normal',
|
||||
controls: [
|
||||
{
|
||||
type: 'text',
|
||||
label: '计划名称',
|
||||
name: 'name',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'date-range',
|
||||
label: '日期范围',
|
||||
name: 'date',
|
||||
required: true,
|
||||
format: 'YYYY-MM-DD',
|
||||
minDate: '${semester.since}',
|
||||
maxDate: '${semester.to}',
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
label: '老师每周最多上课数量',
|
||||
name: 'teacherFreqWeekly',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
min: '1',
|
||||
step: 1,
|
||||
value: 10,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
label: '老师每天最多上课数量',
|
||||
name: 'teacherFreqDaily',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
min: '1',
|
||||
step: 1,
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
label: '老师同时上课数量',
|
||||
name: 'maxConcurrentOfTeacher',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
min: '1',
|
||||
step: 1,
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'maxConcurrent',
|
||||
mode: 'normal',
|
||||
label: '同一时段最多排课数量',
|
||||
required: true,
|
||||
value: 4,
|
||||
min: '1',
|
||||
step: 1,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'maxFreqWeekly',
|
||||
mode: 'normal',
|
||||
label: '学生每周上课数',
|
||||
required: true,
|
||||
value: 1,
|
||||
min: '1',
|
||||
step: 1,
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
name: 'fixTeacherMode',
|
||||
mode: 'normal',
|
||||
label: '',
|
||||
required: true,
|
||||
value: false,
|
||||
option: ' 教师跟班模式',
|
||||
optionAtLeft: false,
|
||||
trueValue: true,
|
||||
falseValue: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '资源设置',
|
||||
controls: [
|
||||
{
|
||||
type: 'course-list',
|
||||
label: '课程',
|
||||
name: 'courselist',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/courses?select=id,name,course2projects(projects(id,name,validRoomList,preSubjectList)),course2orgs(orgs(id,name, validTimeslotList:timeslot_class_id_fkey(id,date,period:periods(id,name,startTime:start_time,endTime:end_time))))&organization_id=eq.${centre_id}&semester_id=eq.${currentSemester}&order=code',
|
||||
},
|
||||
required: true,
|
||||
multiple: true,
|
||||
},
|
||||
],
|
||||
mode: 'normal',
|
||||
},
|
||||
{
|
||||
mode: 'normal',
|
||||
title: '工作量设置',
|
||||
controls: [
|
||||
{
|
||||
type: 'workload',
|
||||
name: 'workload',
|
||||
source:
|
||||
'rest/users?select=id,name:name,role,subjectList,studentGroupList,user_orgs(organization_id),timeslotList:timeslot_teacher_id_fkey(id,date,period:periods(id,name,startTime:start_time,endTime:end_time)),secondChoiceTimeslotList:timeslot_teacher_secondary_id_fkey(id,date,period:periods(id,name,startTime:start_time,endTime:end_time))&role=eq.teacher&user_orgs.organization_id=eq.${centre_id}&order=code',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
mode: 'horizontal',
|
||||
name: 'stepper',
|
||||
actionNextSaveLabel: '下一步',
|
||||
initApi: {
|
||||
method: 'get',
|
||||
url: 'rest/schedule_plans?id=eq.${id}',
|
||||
adaptor:
|
||||
'const data = payload.data.items[0]\r\nreturn {\r\n data: {\r\n ...data.problem,\r\n ...data.problem.problemConfig,\r\n courselist: data.problem.courseList,\r\n workload: data.problem.validTeacherList,\r\n id: data.id,\r\n name: data.name,\r\n date: data.date\r\n }\r\n}',
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
actions: [],
|
||||
data: {
|
||||
'&': '$$',
|
||||
name: '${name}',
|
||||
maxConcurrent:
|
||||
'${config.problemConfig.maxConcurrent}',
|
||||
teacherFreqDaily:
|
||||
'${config.problemConfig.teacherFreqDaily}',
|
||||
date: '${config.date}',
|
||||
maxConcurrentOfTeacher:
|
||||
'${config.problemConfig.maxConcurrentOfTeacher}',
|
||||
maxFreqWeekly:
|
||||
'${config.problemConfig.maxFreqWeekly}',
|
||||
fixTeacherMode:
|
||||
'${config.problemConfig.fixTeacherMode}',
|
||||
courselist: '${config.courseList}',
|
||||
workload: '${config.validTeacherList}',
|
||||
centre_id: '${centre_id}',
|
||||
id: '${id}',
|
||||
currentSemester: '${currentSemester}',
|
||||
semester: '${semester}',
|
||||
teacherFreqWeekly:
|
||||
'${config.problemConfig.teacherFreqWeekly}',
|
||||
},
|
||||
},
|
||||
size: 'sm',
|
||||
icon: 'fa fa-pencil',
|
||||
tooltip: '修改计划配置',
|
||||
tooltipPlacement: 'bottom',
|
||||
className: 'no-border',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
visibleOn: '!this.is_current',
|
||||
label: '删除',
|
||||
actionType: 'ajax',
|
||||
size: 'sm',
|
||||
confirmText: '确认删除该计划?',
|
||||
api: {
|
||||
method: 'delete',
|
||||
url: 'rest/schedule_plans?id=eq.${id}',
|
||||
},
|
||||
reload: 'plancrud',
|
||||
level: 'danger',
|
||||
icon: 'fa fa-trash',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '当前计划',
|
||||
inline: true,
|
||||
visibleOn: 'this.is_current',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
name: 'plancrud',
|
||||
syncLocation: false,
|
||||
defaultParams: {},
|
||||
filter: null,
|
||||
headerToolbar: [{ type: 'bulk-actions' }],
|
||||
perPageAvailable: '',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/config?key=eq.system',
|
||||
adaptor:
|
||||
'let config = payload.data.items && payload.data.items.length > 0 ? payload.data.items[0].value : null\r\nreturn {\r\n data: {\r\n excludeRoomTime: config ? config.excludeRoomTime : true,\r\n excludeTeacherStudentGroup: config ? config.excludeTeacherStudentGroup : true,\r\n excludeTeacherSubject: config ? config.excludeTeacherSubject : true,\r\n excludeTeacherTime: config ? config.excludeTeacherTime : true,\r\n }\r\n}',
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
actions: [],
|
||||
},
|
||||
size: 'xs',
|
||||
icon: 'fa fa-list',
|
||||
tooltip: '查看计划列表',
|
||||
tooltipPlacement: 'bottom',
|
||||
className: 'no-border',
|
||||
},
|
||||
planstartbutton: {
|
||||
type: 'button',
|
||||
actionType: 'ajax',
|
||||
reload: 'window',
|
||||
target: '',
|
||||
blank: true,
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/timetable/${id}/solve',
|
||||
dataType: 'json',
|
||||
},
|
||||
},
|
||||
planstopbutton: {
|
||||
type: 'button',
|
||||
actionType: 'ajax',
|
||||
reload: 'window',
|
||||
target: '',
|
||||
confirmText: '确认停止计算?',
|
||||
blank: true,
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/timetable/${id}/stopSolving',
|
||||
dataType: 'json',
|
||||
},
|
||||
},
|
||||
plansavebutton: {
|
||||
type: 'button',
|
||||
actionType: 'ajax',
|
||||
reload: '',
|
||||
target: '',
|
||||
confirmText: '确认生效该计划安排的场次?',
|
||||
blank: true,
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/schedules',
|
||||
dataType: 'json',
|
||||
requestAdaptor:
|
||||
'return {\r\n ...api,\r\n data: api.data.schedules\r\n}',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
title: '',
|
||||
messages: {},
|
||||
bodyClassName: 'p-none flex',
|
||||
className: 'bg-white h-full',
|
||||
toolbar: [],
|
||||
aside: [],
|
||||
};
|
||||
|
||||
export { schema };
|
||||
280
src/pages/schema/centre/schedule/class_settings.schema.ts
Normal file
280
src/pages/schema/centre/schedule/class_settings.schema.ts
Normal file
@@ -0,0 +1,280 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
submitOnChange: true,
|
||||
reload: 'class_set?id=$orgSelect&org=$org',
|
||||
wrapWithPanel: false,
|
||||
body: [
|
||||
{
|
||||
type: 'tree-select',
|
||||
name: 'orgSelect',
|
||||
label: '查询',
|
||||
mode: 'inline',
|
||||
size: 'lg',
|
||||
clearable: true,
|
||||
searchable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?select=*,course2orgs(*,courses(*))&path=cd.root&course2orgs.courses.semester_id=eq.${currentSemester}&type=in.(school,centre,faculty)&order=name,code',
|
||||
adaptor:
|
||||
"const recursive = id => {\r\n let nodes = payload.data.items.filter(x => {\r\n return x.parent === id\r\n })\r\n return nodes.map(item => {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: `${item.id}`,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}\r\nlet school = payload.data.items.find(x => x.type === 'school');\r\nreturn {\r\n ...payload,\r\n data: [{\r\n label: school.name,\r\n value: `${school.id}`,\r\n children: recursive(school.id)\r\n }]\r\n}\r\n",
|
||||
},
|
||||
inputClassName: 'm-l-xs',
|
||||
options: [],
|
||||
initiallyOpen: false,
|
||||
unfoldedLevel: 2,
|
||||
multiple: true,
|
||||
joinValues: true,
|
||||
onlyChildren: true,
|
||||
withChildren: true,
|
||||
extractValue: false,
|
||||
heightAuto: true,
|
||||
virtualThreshold: false,
|
||||
id: 'u:3224d44c5c5d',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '组织或组织编号',
|
||||
placeholder: '',
|
||||
name: 'org',
|
||||
size: 'md',
|
||||
clearable: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
id: 'u:89966cee9c0b',
|
||||
},
|
||||
],
|
||||
mode: 'inline',
|
||||
debug: false,
|
||||
id: 'u:254f488290d5',
|
||||
feat: 'Insert',
|
||||
},
|
||||
{ type: 'divider', lineStyle: 'solid', id: 'u:c6f2f106da08' },
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?select=*,course2orgs!inner(*,courses!inner(*))&or=(name.like.*${org}*,code.like.*${org}*)&path=cd.root&type=in.(class,studentgroup,reelecclass)&course2orgs.courses.semester_id=eq.$currentSemester&order=name,code',
|
||||
data: { page: '${page}', perPage: '${perPage}', parent: 'in.(${id})' },
|
||||
adaptor: '',
|
||||
requestAdaptor:
|
||||
"if (api.query.parent === 'in.()') {\r\n api.url = api.url.replace('&parent=in.%28%29', '')\r\n}\r\n\r\nreturn {\r\n ...api\r\n}",
|
||||
},
|
||||
messages: {},
|
||||
mode: 'cards',
|
||||
card: {
|
||||
type: 'card',
|
||||
header: { title: '$name', subTitle: '$code' },
|
||||
body: [],
|
||||
toolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '可用时间',
|
||||
icon: 'fa fa-group',
|
||||
iconClassName: '',
|
||||
reload: '',
|
||||
id: 'u:2681ac5e5b28',
|
||||
editorState: 'default',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
body: [
|
||||
{
|
||||
type: 'timeslot-renderer',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/timeslots?select=id,date,organization_id,class_id,periods(id,start_time,end_time)&class_id=eq.${id}&organization_id=eq.${centre_id}',
|
||||
adaptor:
|
||||
'return {\r\n data: {\r\n timeslots: payload.data.items.map(item => {\r\n return {\r\n id: item.id,\r\n date: item.date,\r\n semester_id: item.semester_id,\r\n organization_id: item.organization_id,\r\n class_id: item.class_id,\r\n period: item.periods\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
copy: {
|
||||
type: 'button',
|
||||
label: '按钮',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '复制本周设置到其他周',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/timeslots',
|
||||
dataType: 'json',
|
||||
headers: { Prefer: 'params=single-object' },
|
||||
requestAdaptor:
|
||||
'return {\r\n ...api,\r\n data: api.data.timeslotcopy\r\n}',
|
||||
},
|
||||
id: 'u:f4b826ee7ac0',
|
||||
body: [
|
||||
{
|
||||
type: 'timeslot-copy',
|
||||
name: 'timeslotcopy',
|
||||
mode: 'normal',
|
||||
target: 'class',
|
||||
id: 'u:ff2c4be14afe',
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{ type: 'submit', label: '提交', primary: true },
|
||||
],
|
||||
feat: 'Insert',
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:c7de31e65b52',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:9451198740aa',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:f874015df55f',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
target: 'class',
|
||||
id: 'u:8467642842c2',
|
||||
},
|
||||
],
|
||||
title: '班级时间设置',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'md',
|
||||
actions: [],
|
||||
id: 'u:5ddbcf531f34',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: ' 预览',
|
||||
icon: 'fa fa-eye',
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
iconClassName: '',
|
||||
id: 'u:f44e5cfab0c3',
|
||||
editorState: 'default',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '班级可用时间',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<span class="label" style="background: #e5e7eb;">X</span>\n<span style="vertical-align: middle;">不可用时间</span>\n<span class="label label-info">X</span>\n<span style="vertical-align: middle;">可用时间</span>',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/periods?select=*,timeslots(*)×lots.organization_id=eq.$centre_id×lots.date=gte.${semester.since}×lots.date=lte.${semester.to}×lots.class_id=eq.$id&order=code',
|
||||
adaptor:
|
||||
"let items = []\r\nconst week_list = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']\r\nconst oneday = 24 * 60 * 60 * 1000\r\nconst get_week_num = (start, end) => {\r\n let s = new Date(start)\r\n let s_ms = s.getTime()\r\n let s_day = s.getDay() ? s.getDay() : 7\r\n s.setTime(s_ms - (s_day - 1) * oneday)\r\n s_ms = s.getTime()\r\n let e = new Date(end)\r\n let e_ms = e.getTime()\r\n let e_day = e.getDay() ? e.getDay() : 7\r\n return {\r\n sem_start: s,\r\n day: e_day,\r\n week_num: Math.ceil(((e_ms - s_ms) / oneday + 1) / 7)\r\n }\r\n}\r\nlet start = /(?<=\\[date\\]\\[0\\]=gte\\.)\\d+-\\d+-\\d+/g.exec(api.url)[0]\r\nlet end = /(?<=\\[date\\]\\[1\\]=lte\\.)\\d+-\\d+-\\d+/g.exec(api.url)[0]\r\nconst { sem_start, week_num } = get_week_num(start, end)\r\nfor (let i = 0; i < 7; i++) {\r\n let date_temp = new Date(sem_start)\r\n let date_temp_ms = date_temp.getTime()\r\n payload.data.items.map(x => {\r\n const [h, m, s] = x.start_time.split(':')\r\n return {\r\n ...x,\r\n cmp_time: new Date(2020, 0, 1, h, m, s)\r\n }\r\n }).sort((a, b) => {\r\n return a.cmp_time - b.cmp_time\r\n }).forEach(item => {\r\n let week_num_list = []\r\n for (let j = 0; j < week_num; j++) {\r\n date_temp.setTime(date_temp_ms + j * 7 * oneday + i * oneday)\r\n week_num_list.push({\r\n week: j + 1,\r\n date: null,\r\n tpl: \"<span title='\".concat(date_temp.toISOString().split('T')[0], \"' class='label' style='background: #e5e7eb;'>\", j + 1, \"</span>\")\r\n })\r\n }\r\n items.push({\r\n day: {\r\n lab: week_list[i],\r\n val: i + 1\r\n },\r\n period: {\r\n lab: item.name,\r\n val: item.id\r\n },\r\n weeks: week_num_list\r\n })\r\n })\r\n}\r\nconst row_mem = payload.data.items.length\r\npayload.data.items.map(x => {\r\n const [h, m, s] = x.start_time.split(':')\r\n return {\r\n ...x,\r\n cmp_time: new Date(2020, 0, 1, h, m, s)\r\n }\r\n}).sort((a, b) => {\r\n return a.cmp_time - b.cmp_time\r\n}).forEach((item, index) => {\r\n item.timeslots && item.timeslots.forEach(i => {\r\n const { day, week_num } = get_week_num(sem_start, i.date)\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].date = i.date\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].id = i.id\r\n const week = items[(day - 1) * row_mem + index].weeks[week_num - 1].week\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].tpl = \"<span title='\".concat(i.date, \"' class='label label-primary'>\", week, \"</span>\")\r\n })\r\n})\r\nreturn {\r\n ...payload,\r\n data: {\r\n items: items\r\n }\r\n}",
|
||||
requestAdaptor: '',
|
||||
},
|
||||
messages: {},
|
||||
columns: [
|
||||
{
|
||||
name: 'day.lab',
|
||||
label: '',
|
||||
type: 'text',
|
||||
inline: true,
|
||||
className: 'common-padding',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '',
|
||||
name: 'period.lab',
|
||||
placeholder: '-',
|
||||
className: 'common-padding',
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: 'weeks',
|
||||
label: '',
|
||||
type: 'each',
|
||||
items: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<%= data.tpl %>',
|
||||
inline: true,
|
||||
className: 'm-r-xs',
|
||||
},
|
||||
],
|
||||
placeholder: '-',
|
||||
className: 'common-padding',
|
||||
},
|
||||
],
|
||||
combineNum: 1,
|
||||
columnsTogglable: false,
|
||||
syncLocation: false,
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
data: null,
|
||||
bodyClassName: 'p-t-none',
|
||||
actions: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
actionsCount: 3,
|
||||
id: 'u:140b3d90b196',
|
||||
},
|
||||
syncLocation: false,
|
||||
placeholder: '暂无数据',
|
||||
columnsCount: 4,
|
||||
name: 'class_set',
|
||||
headerToolbar: [],
|
||||
footerToolbar: [
|
||||
{ type: 'pagination' },
|
||||
{ type: 'switch-per-page' },
|
||||
{ type: 'statistics' },
|
||||
],
|
||||
perPageAvailable: [20, 40, 80],
|
||||
defaultParams: { perPage: 20 },
|
||||
bulkActions: [],
|
||||
itemActions: [],
|
||||
loadDataOnce: false,
|
||||
id: 'u:06cd3a22ee0c',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
title: '',
|
||||
cssVars: { '--Checkbox-onDisabled-color': 'transparent' },
|
||||
id: 'u:1cbc663fae2b',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
4068
src/pages/schema/centre/schedule/index.schema.ts
Normal file
4068
src/pages/schema/centre/schedule/index.schema.ts
Normal file
File diff suppressed because one or more lines are too long
289
src/pages/schema/centre/schedule/lists.schema.ts
Normal file
289
src/pages/schema/centre/schedule/lists.schema.ts
Normal file
@@ -0,0 +1,289 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
className: 'm-b-sm',
|
||||
submitOnChange: true,
|
||||
submitOnInit: true,
|
||||
reload:
|
||||
'manage_list?project_id=$project_id&teacher_id=$teacher_id&location_id=$location_id&date=$date&period_id=$period_id',
|
||||
wrapWithPanel: false,
|
||||
mode: 'inline',
|
||||
name: 'filter',
|
||||
body: [
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '项目',
|
||||
name: 'project_id',
|
||||
mode: 'inline',
|
||||
clearable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/projects?organization_id=eq.$centre_id&semester_id=eq.$currentSemester&order=name',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: "eq.".concat(item.id)\r\n }\r\n })\r\n}',
|
||||
},
|
||||
checkAll: false,
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
className: 'm-l-sm',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'input-date-range',
|
||||
name: 'date',
|
||||
mode: 'inline',
|
||||
label: '日期',
|
||||
clearable: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
className: 'm-l-sm',
|
||||
value: '',
|
||||
format: 'YYYY-MM-DD',
|
||||
ranges:
|
||||
'yesterday,today,7daysago,prevweek,thismonth,prevmonth,prevquarter',
|
||||
},
|
||||
{
|
||||
label: '节次',
|
||||
type: 'select',
|
||||
name: 'period_id',
|
||||
mode: 'inline',
|
||||
clearable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/periods?order=name',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}`,\r\n value: "eq.".concat(item.id)\r\n }\r\n })\r\n}',
|
||||
},
|
||||
checkAll: false,
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
labelClassName: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
name: 'location_id',
|
||||
mode: 'inline',
|
||||
label: '地点',
|
||||
clearable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/locations?organization_id=eq.$centre_id&order=name',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: "eq.".concat(item.id)\r\n }\r\n })\r\n}',
|
||||
},
|
||||
checkAll: false,
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
className: 'm-l-sm',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'teacher_id',
|
||||
mode: 'inline',
|
||||
className: 'm-l-sm',
|
||||
label: '教师',
|
||||
clearable: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
size: 'md',
|
||||
checkAll: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/users?role=eq.teacher&order=name',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: `${item.name}(${item.code})`,\r\n value: "eq.".concat(item.id)\r\n }\r\n })\r\n}',
|
||||
},
|
||||
searchable: true,
|
||||
},
|
||||
],
|
||||
mode: '',
|
||||
subFormMode: '',
|
||||
gap: '',
|
||||
className: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
lineStyle: 'solid',
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/user2projects?select=*,att_status_text:user2project_att_status_dict_fk(dictvalue),schedule_status_text:user2project_schedule_status_dict_fk(dictvalue),users_ex:users_ex!user2project_student_id_fkey!inner(*),schedule_stats!inner(*)&semester_id=eq.${currentSemester}&schedule_status=neq.canceled&users_ex.role=eq.student&users_ex.org_type=eq.class&schedule_stats.organization_id=eq.${centre_id}',
|
||||
data: {
|
||||
page: '${page}',
|
||||
perPage: '${perPage}',
|
||||
'&': '$$',
|
||||
},
|
||||
adaptor:
|
||||
"let supportDateTimeFormat = typeof(Intl.DateTimeFormat) === 'function'\r\n\r\npayload.data.items.forEach(item => {\r\n item.schedule_stats.day = supportDateTimeFormat ? `${item.schedule_stats.date}(${new Intl.DateTimeFormat('zh-CN', { weekday: 'short'}).format(new Date(item.schedule_stats.date))})` : item.schedule_stats.date;\r\n})\r\n\r\nreturn {\r\n ...payload\r\n}",
|
||||
requestAdaptor:
|
||||
"if (api.body.project_id === '') {\r\n api.url = api.url.replace('&project_id=', '')\r\n}\r\n\r\nif (api.body.date === '') {\r\n api.url = api.url.replace('&date=', '')\r\n} else {\r\n let date_former = api.body.date && api.body.date.split(',')[0]\r\n let date_latter = api.body.date && api.body.date.split(',')[1]\r\n\r\n if (date_former === date_latter) {\r\n api.url = api.url.replace(\r\n '&date=' + date_former + '%2C' + date_latter,\r\n '&schedule_stats.date=eq.' + date_former\r\n )\r\n } else {\r\n api.url = api.url.replace(\r\n '&date=' + date_former + '%2C' + date_latter,\r\n '&schedule_stats.date=gte.' + date_former + '&schedule_stats.date=lte.' + date_latter\r\n )\r\n }\r\n}\r\n\r\nif (api.body.period_id === '') {\r\n api.url = api.url.replace('&period_id=', '')\r\n} else {\r\n api.url = api.url.replace(\r\n '&period_id=' + api.body.period_id,\r\n '&schedule_stats.period_id=' + api.body.period_id\r\n )\r\n}\r\n\r\nif (api.body.location_id === '') {\r\n api.url = api.url.replace('&location_id=', '')\r\n} else {\r\n api.url = api.url.replace(\r\n '&location_id=' + api.body.location_id,\r\n '&schedule_stats.location_id=' + api.body.location_id\r\n )\r\n}\r\n\r\nif (api.body.teacher_id === '') {\r\n api.url = api.url.replace('&teacher_id=', '')\r\n} else {\r\n api.url = api.url.replace(\r\n '&teacher_id=' + api.body.teacher_id,\r\n '&schedule_stats.teacher_id=' + api.body.teacher_id\r\n )\r\n}\r\n\r\nreturn api;",
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
name: 'schedule_stats.project_name',
|
||||
label: '项目',
|
||||
type: 'text',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
name: 'schedule_stats.day',
|
||||
label: '日期',
|
||||
type: 'text',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '节次',
|
||||
name: 'schedule_stats.period_name',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '地点',
|
||||
name: 'schedule_stats.location_name',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '教师',
|
||||
name: 'schedule_stats.teacher_name',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '学号',
|
||||
name: 'users_ex.code',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '姓名',
|
||||
name: 'users_ex.name',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '班级',
|
||||
name: 'users_ex.org_name',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '选课状态',
|
||||
name: 'schedule_status_text.dictvalue',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '考勤状态',
|
||||
name: 'att_status_text.dictvalue',
|
||||
placeholder: '-',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
syncLocation: false,
|
||||
pageField: 'page',
|
||||
perPageField: 'perPage',
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'export-excel',
|
||||
align: 'right',
|
||||
label: '导出',
|
||||
icon: 'fa fa-download',
|
||||
level: 'primary',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/user2projects?select=*,att_status_text:user2project_att_status_dict_fk(dictvalue),schedule_status_text:user2project_schedule_status_dict_fk(dictvalue),users_ex:users_ex!user2project_student_id_fkey!inner(*),schedule_stats!inner(*)&semester_id=eq.${currentSemester}&schedule_status=neq.canceled&users_ex.role=eq.student&users_ex.org_type=eq.class&schedule_stats.organization_id=eq.${centre_id}',
|
||||
data: {
|
||||
project_id: '$project_id',
|
||||
date: '$date',
|
||||
period_id: '$period_id',
|
||||
location_id: '$location_id',
|
||||
teacher_id: '$teacher_id',
|
||||
},
|
||||
requestAdaptor:
|
||||
"if (api.body.project_id === '') {\r\n api.url = api.url.replace('&project_id=', '')\r\n}\r\n\r\nif (api.body.date === '') {\r\n api.url = api.url.replace('&date=', '')\r\n} else {\r\n let date_former = api.body.date && api.body.date.split(',')[0]\r\n let date_latter = api.body.date && api.body.date.split(',')[1]\r\n\r\n if (date_former === date_latter) {\r\n api.url = api.url.replace(\r\n '&date=' + date_former + '%2C' + date_latter,\r\n '&schedule_stats.date=eq.' + date_former\r\n )\r\n } else {\r\n api.url = api.url.replace(\r\n '&date=' + date_former + '%2C' + date_latter,\r\n '&schedule_stats.date=gte.' + date_former + '&schedule_stats.date=lte.' + date_latter\r\n )\r\n }\r\n}\r\n\r\nif (api.body.period_id === '') {\r\n api.url = api.url.replace('&period_id=', '')\r\n} else {\r\n api.url = api.url.replace(\r\n '&period_id=' + api.body.period_id,\r\n '&schedule_stats.period_id=' + api.body.period_id\r\n )\r\n}\r\n\r\nif (api.body.location_id === '') {\r\n api.url = api.url.replace('&location_id=', '')\r\n} else {\r\n api.url = api.url.replace(\r\n '&location_id=' + api.body.location_id,\r\n '&schedule_stats.location_id=' + api.body.location_id\r\n )\r\n}\r\n\r\nif (api.body.teacher_id === '') {\r\n api.url = api.url.replace('&teacher_id=', '')\r\n} else {\r\n api.url = api.url.replace(\r\n '&teacher_id=' + api.body.teacher_id,\r\n '&schedule_stats.teacher_id=' + api.body.teacher_id\r\n )\r\n}\r\n\r\nreturn api;",
|
||||
},
|
||||
exportColumns: [
|
||||
{
|
||||
label: '项目',
|
||||
name: 'schedule_stats.project_name',
|
||||
},
|
||||
{
|
||||
name: 'schedule_stats.date',
|
||||
label: '日期',
|
||||
},
|
||||
{
|
||||
name: 'schedule_stats.period_name',
|
||||
label: '节次',
|
||||
},
|
||||
{
|
||||
name: 'schedule_stats.location_name',
|
||||
label: '地点',
|
||||
},
|
||||
{
|
||||
name: 'schedule_stats.teacher_name',
|
||||
label: '教师',
|
||||
},
|
||||
{
|
||||
name: 'users_ex.code',
|
||||
label: '学号',
|
||||
},
|
||||
{
|
||||
name: 'users_ex.name',
|
||||
label: '姓名',
|
||||
},
|
||||
{
|
||||
name: 'users_ex.org_name',
|
||||
label: '班级',
|
||||
},
|
||||
{
|
||||
name: 'schedule_status_text.dictvalue',
|
||||
label: '选课状态',
|
||||
},
|
||||
{
|
||||
name: 'att_status_text.dictvalue',
|
||||
label: '考勤状态',
|
||||
},
|
||||
],
|
||||
filename: '选课名单',
|
||||
},
|
||||
],
|
||||
footerToolbar: [
|
||||
{ type: 'pagination' },
|
||||
{ type: 'switch-per-page' },
|
||||
{ type: 'statistics' },
|
||||
],
|
||||
name: 'manage_list',
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
placeholder: '-',
|
||||
},
|
||||
],
|
||||
title: '',
|
||||
messages: {},
|
||||
bodyClassName: '',
|
||||
className: '',
|
||||
headerClassName: '',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
332
src/pages/schema/centre/schedule/location_settings.schema.ts
Normal file
332
src/pages/schema/centre/schedule/location_settings.schema.ts
Normal file
@@ -0,0 +1,332 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
className: '',
|
||||
submitOnChange: true,
|
||||
reload: 'loc_set?locationSelect=$locationSelect',
|
||||
wrapWithPanel: false,
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
name: 'locationSelect',
|
||||
label: '查询',
|
||||
mode: 'inline',
|
||||
size: 'md',
|
||||
clearable: true,
|
||||
searchable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/locations?organization_id=eq.$centre_id&order=code',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
inputClassName: 'm-l-xs',
|
||||
checkAll: false,
|
||||
perPageAvailable: [10],
|
||||
options: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ type: 'divider', lineStyle: 'solid' },
|
||||
{
|
||||
type: 'tpl',
|
||||
className: 'm-b-sm',
|
||||
tpl: '<% if (data.excludeRoomTime) { %>\n <span class="label label-warning">选择不可用时间<span>\n<% } else { %>\n <span class="label label-info">选择可用时间<span>\n<% } %>',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
syncLocation: false,
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/locations?organization_id=eq.$centre_id&order=code',
|
||||
data: { page: '$page', perPage: '$perPage', id: 'eq.$locationSelect' },
|
||||
requestAdaptor:
|
||||
"if (api.query.id === 'eq.') {\r\n api.url = api.url.replace('&id=eq.', '')\r\n}\r\n\r\nreturn {\r\n ...api\r\n}",
|
||||
adaptor: '',
|
||||
replaceData: false,
|
||||
},
|
||||
perPageAvailable: [20, 40, 80],
|
||||
messages: {},
|
||||
name: 'loc_set',
|
||||
defaultParams: { perPage: 20 },
|
||||
card: {
|
||||
type: 'card',
|
||||
header: { title: '$name', subTitle: '$code' },
|
||||
body: [{ type: 'tpl', tpl: '${intro|raw}' }],
|
||||
toolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '时间选择',
|
||||
icon: 'fa fa-group',
|
||||
iconClassName: '',
|
||||
reload: '',
|
||||
id: 'u:f67c49dcfe29',
|
||||
editorState: 'default',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
body: [
|
||||
{
|
||||
type: 'timeslot-renderer',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/timeslots?select=id,date,organization_id,location_id,periods(id,start_time,end_time)&location_id=eq.${id}&organization_id=eq.${centre_id}',
|
||||
adaptor:
|
||||
'return {\r\n data: {\r\n timeslots: payload.data.items.map(item => {\r\n return {\r\n id: item.id,\r\n date: item.date,\r\n semester_id: item.semester_id,\r\n organization_id: item.organization_id,\r\n location_id: item.location_id,\r\n period: item.periods\r\n }\r\n })\r\n }\r\n}',
|
||||
},
|
||||
copy: {
|
||||
type: 'button',
|
||||
label: '按钮',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '复制本周设置到其他周',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
controls: [
|
||||
{
|
||||
type: 'timeslot-copy',
|
||||
name: 'timeslotcopy',
|
||||
mode: 'normal',
|
||||
target: 'location',
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/timeslots',
|
||||
dataType: 'json',
|
||||
headers: { Prefer: 'params=single-object' },
|
||||
requestAdaptor:
|
||||
'return {\r\n ...api,\r\n data: api.data.timeslotcopy\r\n}',
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: false,
|
||||
showCloseButton: true,
|
||||
},
|
||||
},
|
||||
target: 'location',
|
||||
},
|
||||
],
|
||||
title: '场地时间设置',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'md',
|
||||
actions: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '预览',
|
||||
icon: 'fa fa-eye',
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
iconClassName: '',
|
||||
visibleOn: '!this.excludeRoomTime',
|
||||
id: 'u:c8b5eb1749e3',
|
||||
editorState: 'default',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '场地可用时间',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<span class="label" style="background: #e5e7eb;">X</span>\n<span style="vertical-align: middle;">不可用时间</span>\n<span class="label label-info">X</span>\n<span style="vertical-align: middle;">可用时间</span>',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/periods?select=*,timeslots(*)×lots.organization_id=eq.$centre_id×lots.date=gte.${semester.since}×lots.date=lte.${semester.to}×lots.location_id=eq.$id&order=code',
|
||||
adaptor:
|
||||
"let items = []\r\nconst week_list = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']\r\nconst oneday = 24 * 60 * 60 * 1000\r\nconst get_week_num = (start, end) => {\r\n let s = new Date(start)\r\n let s_ms = s.getTime()\r\n let s_day = s.getDay() ? s.getDay() : 7\r\n s.setTime(s_ms - (s_day - 1) * oneday)\r\n s_ms = s.getTime()\r\n let e = new Date(end)\r\n let e_ms = e.getTime()\r\n let e_day = e.getDay() ? e.getDay() : 7\r\n return {\r\n sem_start: s,\r\n day: e_day,\r\n week_num: Math.ceil(((e_ms - s_ms) / oneday + 1) / 7)\r\n }\r\n}\r\nlet start = /(?<=\\[date\\]\\[0\\]=gte\\.)\\d+-\\d+-\\d+/g.exec(api.url)[0]\r\nlet end = /(?<=\\[date\\]\\[1\\]=lte\\.)\\d+-\\d+-\\d+/g.exec(api.url)[0]\r\nconst { sem_start, week_num } = get_week_num(start, end)\r\nfor (let i = 0; i < 7; i++) {\r\n let date_temp = new Date(sem_start)\r\n let date_temp_ms = date_temp.getTime()\r\n payload.data.items.map(x => {\r\n const [h, m, s] = x.start_time.split(':')\r\n return {\r\n ...x,\r\n cmp_time: new Date(2020, 0, 1, h, m, s)\r\n }\r\n }).sort((a, b) => {\r\n return a.cmp_time - b.cmp_time\r\n }).forEach(item => {\r\n let week_num_list = []\r\n for (let j = 0; j < week_num; j++) {\r\n date_temp.setTime(date_temp_ms + j * 7 * oneday + i * oneday)\r\n week_num_list.push({\r\n week: j + 1,\r\n date: null,\r\n tpl: \"<span title='\".concat(date_temp.toISOString().split('T')[0], \"' class='label' style='background: #e5e7eb;'>\", j + 1, \"</span>\")\r\n })\r\n }\r\n items.push({\r\n day: {\r\n lab: week_list[i],\r\n val: i + 1\r\n },\r\n period: {\r\n lab: item.name,\r\n val: item.id\r\n },\r\n weeks: week_num_list\r\n })\r\n })\r\n}\r\nconst row_mem = payload.data.items.length\r\npayload.data.items.map(x => {\r\n const [h, m, s] = x.start_time.split(':')\r\n return {\r\n ...x,\r\n cmp_time: new Date(2020, 0, 1, h, m, s)\r\n }\r\n}).sort((a, b) => {\r\n return a.cmp_time - b.cmp_time\r\n}).forEach((item, index) => {\r\n item.timeslots && item.timeslots.forEach(i => {\r\n const { day, week_num } = get_week_num(sem_start, i.date)\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].date = i.date\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].id = i.id\r\n const week = items[(day - 1) * row_mem + index].weeks[week_num - 1].week\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].tpl = \"<span title='\".concat(i.date, \"' class='label label-primary'>\", week, \"</span>\")\r\n })\r\n})\r\nreturn {\r\n ...payload,\r\n data: {\r\n items: items\r\n }\r\n}\r\n",
|
||||
requestAdaptor: '',
|
||||
},
|
||||
messages: {},
|
||||
columns: [
|
||||
{
|
||||
name: 'day.lab',
|
||||
label: '',
|
||||
type: 'text',
|
||||
inline: true,
|
||||
className: 'common-padding',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '',
|
||||
name: 'period.lab',
|
||||
placeholder: '-',
|
||||
className: 'common-padding',
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: 'weeks',
|
||||
label: '',
|
||||
type: 'each',
|
||||
items: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<%= data.tpl %>',
|
||||
inline: true,
|
||||
className: 'm-r-xs',
|
||||
},
|
||||
],
|
||||
placeholder: '-',
|
||||
className: 'common-padding',
|
||||
},
|
||||
],
|
||||
combineNum: 1,
|
||||
columnsTogglable: false,
|
||||
syncLocation: false,
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
data: null,
|
||||
bodyClassName: 'p-t-none',
|
||||
actions: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '预览',
|
||||
icon: 'fa fa-eye',
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
iconClassName: '',
|
||||
visibleOn: 'this.excludeRoomTime',
|
||||
id: 'u:fb5fb80671b4',
|
||||
editorState: 'default',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '场地可用时间',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<span class="label" style="background: #e5e7eb;">X</span>\n<span style="vertical-align: middle;">不可用时间</span>\n<span class="label label-info">X</span>\n<span style="vertical-align: middle;">可用时间</span>',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/periods?select=*,timeslots(*)×lots.organization_id=eq.$centre_id×lots.date=gte.${semester.since}×lots.date=lte.${semester.to}×lots.location_id=eq.$id&order=code',
|
||||
adaptor:
|
||||
"let items = []\r\nconst week_list = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']\r\nconst oneday = 24 * 60 * 60 * 1000\r\nconst get_week_num = (start, end) => {\r\n let s = new Date(start)\r\n let s_ms = s.getTime()\r\n let s_day = s.getDay() ? s.getDay() : 7\r\n s.setTime(s_ms - (s_day - 1) * oneday)\r\n s_ms = s.getTime()\r\n let e = new Date(end)\r\n let e_ms = e.getTime()\r\n let e_day = e.getDay() ? e.getDay() : 7\r\n return {\r\n sem_start: s,\r\n day: e_day,\r\n week_num: Math.ceil(((e_ms - s_ms) / oneday + 1) / 7)\r\n }\r\n}\r\nlet start = /(?<=\\[date\\]\\[0\\]=gte\\.)\\d+-\\d+-\\d+/g.exec(api.url)[0]\r\nlet end = /(?<=\\[date\\]\\[1\\]=lte\\.)\\d+-\\d+-\\d+/g.exec(api.url)[0]\r\nconst { sem_start, week_num } = get_week_num(start, end)\r\nfor (let i = 0; i < 7; i++) {\r\n let date_temp = new Date(sem_start)\r\n let date_temp_ms = date_temp.getTime()\r\n payload.data.items.map(x => {\r\n const [h, m, s] = x.start_time.split(':')\r\n return {\r\n ...x,\r\n cmp_time: new Date(2020, 0, 1, h, m, s)\r\n }\r\n }).sort((a, b) => {\r\n return a.cmp_time - b.cmp_time\r\n }).forEach(item => {\r\n let week_num_list = []\r\n for (let j = 0; j < week_num; j++) {\r\n date_temp.setTime(date_temp_ms + j * 7 * oneday + i * oneday)\r\n week_num_list.push({\r\n week: j + 1,\r\n date: null,\r\n tpl: \"<span title='\".concat(date_temp.toISOString().split('T')[0], \"' class='label label-info'>\", j + 1, \"</span>\")\r\n })\r\n }\r\n items.push({\r\n day: {\r\n lab: week_list[i],\r\n val: i + 1\r\n },\r\n period: {\r\n lab: item.name,\r\n val: item.id\r\n },\r\n weeks: week_num_list\r\n })\r\n })\r\n}\r\nconst row_mem = payload.data.items.length\r\npayload.data.items.map(x => {\r\n const [h, m, s] = x.start_time.split(':')\r\n return {\r\n ...x,\r\n cmp_time: new Date(2020, 0, 1, h, m, s)\r\n }\r\n}).sort((a, b) => {\r\n return a.cmp_time - b.cmp_time\r\n}).forEach((item, index) => {\r\n item.timeslots && item.timeslots.forEach(i => {\r\n const { day, week_num } = get_week_num(sem_start, i.date)\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].date = i.date\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].id = i.id\r\n const week = items[(day - 1) * row_mem + index].weeks[week_num - 1].week\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].tpl = \"<span class='label' style='background: #e5e7eb;'>\".concat(week, \"</span>\")\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].tpl = \"<span title='\".concat(i.date, \"' class='label' style='background: #e5e7eb;'>\", week, \"</span>\")\r\n })\r\n})\r\nreturn {\r\n ...payload,\r\n data: {\r\n items: items\r\n }\r\n}",
|
||||
requestAdaptor: '',
|
||||
},
|
||||
messages: {},
|
||||
columns: [
|
||||
{
|
||||
name: 'day.lab',
|
||||
label: '',
|
||||
type: 'text',
|
||||
inline: true,
|
||||
className: 'common-padding',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '',
|
||||
name: 'period.lab',
|
||||
placeholder: '-',
|
||||
className: 'common-padding',
|
||||
},
|
||||
{
|
||||
name: 'weeks',
|
||||
label: '',
|
||||
type: 'each',
|
||||
items: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<%= data.tpl %>',
|
||||
inline: true,
|
||||
className: 'm-r-xs',
|
||||
},
|
||||
],
|
||||
placeholder: '-',
|
||||
className: 'common-padding',
|
||||
},
|
||||
],
|
||||
combineNum: 1,
|
||||
columnsTogglable: false,
|
||||
syncLocation: false,
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
data: null,
|
||||
bodyClassName: 'p-t-none',
|
||||
actions: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
actionsCount: 3,
|
||||
},
|
||||
mode: 'cards',
|
||||
headerToolbar: [],
|
||||
footerToolbar: [
|
||||
{ type: 'pagination' },
|
||||
{ type: 'switch-per-page' },
|
||||
{ type: 'statistics' },
|
||||
],
|
||||
columnsCount: 4,
|
||||
placeholder: '暂无数据',
|
||||
pageField: 'page',
|
||||
perPageField: 'perPage',
|
||||
initApi: '',
|
||||
initFetch: '',
|
||||
checkAll: false,
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
initApi: {
|
||||
method: 'get',
|
||||
url: 'rest/config?key=eq.system&organization_id=eq.${centre_id}',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n excludeRoomTime: payload.data.items[0].value.excludeRoomTime\r\n }\r\n}',
|
||||
},
|
||||
title: '',
|
||||
cssVars: { '--Checkbox-onDisabled-color': 'transparent' },
|
||||
};
|
||||
|
||||
export { schema };
|
||||
2143
src/pages/schema/centre/schedule/open.schema.ts
Normal file
2143
src/pages/schema/centre/schedule/open.schema.ts
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user