20241113-a
This commit is contained in:
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
373
src/pages/schema/centre/schedule/project_settings.schema.ts
Normal file
373
src/pages/schema/centre/schedule/project_settings.schema.ts
Normal file
@@ -0,0 +1,373 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
id: 'u:a2be39c9a42e',
|
||||
className: '',
|
||||
submitOnChange: true,
|
||||
reload: 'pro_set?projectSelect=$projectSelect',
|
||||
wrapWithPanel: false,
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
id: 'u:fb8743dd5368',
|
||||
name: 'projectSelect',
|
||||
label: '查询',
|
||||
mode: 'inline',
|
||||
size: 'md',
|
||||
clearable: true,
|
||||
searchable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/projects?select=*,course2projects!inner(*)&organization_id=eq.$centre_id&semester_id=eq.$currentSemester&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],
|
||||
multiple: false,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
lineStyle: 'solid',
|
||||
id: 'u:44b9b52eb026',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
id: 'u:627706c838e0',
|
||||
className: 'm-b-sm',
|
||||
tpl: '<span class="label label-warning">* 开头的项目为“自由安排”项目</span>',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
syncLocation: false,
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/projects?select=*,course2projects!inner(*)&semester_id=eq.$currentSemester&organization_id=eq.$centre_id&order=code',
|
||||
data: {
|
||||
page: '${page}',
|
||||
perPage: '${perPage}',
|
||||
id: 'eq.${projectSelect}',
|
||||
},
|
||||
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:
|
||||
"return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n if (item.free_schedule) {\r\n return {\r\n ...item,\r\n name: '* '.concat(item.name),\r\n select_pre: item.pre_list.map(item => {\r\n return ''.concat(item)\r\n }),\r\n select_loc: item.location_list.map(item => {\r\n return ''.concat(item)\r\n }),\r\n select_team: item.team_list.map(item => {\r\n return ''.concat(item)\r\n }),\r\n select_tch: item.teacher_list.map(item => {\r\n return ''.concat(item)\r\n })\r\n }\r\n } else {\r\n return {\r\n ...item,\r\n select_pre: item.pre_list.map(item => {\r\n return ''.concat(item)\r\n }),\r\n select_loc: item.location_list.map(item => {\r\n return ''.concat(item)\r\n }),\r\n select_team: item.team_list.map(item => {\r\n return ''.concat(item)\r\n }),\r\n select_tch: item.teacher_list.map(item => {\r\n return ''.concat(item)\r\n })\r\n }\r\n }\r\n })\r\n }\r\n}\r\n",
|
||||
},
|
||||
bulkActions: [],
|
||||
itemActions: [],
|
||||
id: 'u:31c2c09e9a0c',
|
||||
perPageAvailable: [6, 18, 36, 54],
|
||||
messages: {},
|
||||
name: 'pro_set',
|
||||
defaultParams: {
|
||||
perPage: '6',
|
||||
},
|
||||
card: {
|
||||
type: 'card',
|
||||
header: {
|
||||
title: '$name',
|
||||
subTitle: '$code',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
label: '',
|
||||
type: 'tpl',
|
||||
tpl: '${intro|raw}',
|
||||
inline: false,
|
||||
id: 'u:c86359f007f5',
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
type: 'button',
|
||||
icon: 'fa fa-pencil text-info',
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
tooltip: '修改项目',
|
||||
tooltipPlacement: 'top',
|
||||
id: 'u:6a42142de051',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '修改项目',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/projects/$id',
|
||||
data: {
|
||||
code: '$code',
|
||||
name: '$name',
|
||||
intro: '$intro',
|
||||
type: '$type',
|
||||
free_schedule: '$free_schedule',
|
||||
},
|
||||
dataType: 'json',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '编号',
|
||||
name: 'code',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:bcc3e879483c',
|
||||
},
|
||||
{
|
||||
label: '实验',
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:c2c3f1827843',
|
||||
},
|
||||
{
|
||||
label: '类型',
|
||||
type: 'select',
|
||||
name: 'type',
|
||||
mode: 'normal',
|
||||
options: [],
|
||||
required: true,
|
||||
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:f01b0c37f8d7',
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'free_schedule',
|
||||
mode: 'normal',
|
||||
option: '学生自主安排时间',
|
||||
id: 'u:d31e5761e95a',
|
||||
},
|
||||
{
|
||||
label: '描述',
|
||||
type: 'input-rich-text',
|
||||
name: 'intro',
|
||||
mode: 'normal',
|
||||
options: {
|
||||
menubar: false,
|
||||
},
|
||||
id: 'u:5cb8ddd6777b',
|
||||
},
|
||||
],
|
||||
id: 'u:63f0013c6e86',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:31c2c09e9a0c',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:b7dec6737fba',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:9aeb3899f407',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:2faa3f63df9d',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
type: 'divider',
|
||||
id: 'u:f6de922f021a',
|
||||
},
|
||||
{
|
||||
type: 'form',
|
||||
label: '',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/projects/$id',
|
||||
requestAdaptor:
|
||||
"return {\r\n ...api,\r\n data: {\r\n pre_list: api.data.select_pre[0] === '' ? [] : api.data.select_pre,\r\n location_list: api.data.select_loc[0] === '' ? [] : api.data.select_loc,\r\n team_list: api.data.select_team[0] === '' ? [] : api.data.select_team,\r\n teacher_list: api.data.select_tch[0] === '' ? [] : api.data.select_tch\r\n }\r\n}",
|
||||
data: {
|
||||
select_pre: '$select_pre',
|
||||
select_loc: '$select_loc',
|
||||
select_team: '$select_team',
|
||||
select_tch: '$select_tch',
|
||||
},
|
||||
adaptor: '',
|
||||
},
|
||||
initApi: '',
|
||||
wrapWithPanel: false,
|
||||
submitOnChange: true,
|
||||
resetAfterSubmit: false,
|
||||
target: '',
|
||||
reload: 'select_tch?select_team=$select_team',
|
||||
messages: {},
|
||||
body: [
|
||||
{
|
||||
label: '前置实验',
|
||||
type: 'select',
|
||||
name: 'select_pre',
|
||||
options: [],
|
||||
checkAll: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/projects?id=neq.$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: item.id\r\n }\r\n })\r\n\r\n}',
|
||||
},
|
||||
clearable: true,
|
||||
creatable: false,
|
||||
multiple: true,
|
||||
joinValues: false,
|
||||
defaultCheckAll: false,
|
||||
checkAllLabel: '全选',
|
||||
extractValue: true,
|
||||
searchable: true,
|
||||
id: 'u:e27e10d364a9',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '可用场地',
|
||||
options: [],
|
||||
checkAll: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/locations?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: item.id\r\n }\r\n })\r\n\r\n}',
|
||||
},
|
||||
clearable: true,
|
||||
creatable: false,
|
||||
multiple: true,
|
||||
joinValues: false,
|
||||
defaultCheckAll: false,
|
||||
checkAllLabel: '全选',
|
||||
extractValue: true,
|
||||
name: 'select_loc',
|
||||
searchable: true,
|
||||
id: 'u:d2c3a600be4c',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '教师团队',
|
||||
options: [],
|
||||
checkAll: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/teams?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: item.id\r\n }\r\n })\r\n\r\n}',
|
||||
},
|
||||
clearable: true,
|
||||
creatable: false,
|
||||
multiple: true,
|
||||
joinValues: false,
|
||||
defaultCheckAll: false,
|
||||
checkAllLabel: '全选',
|
||||
extractValue: true,
|
||||
name: 'select_team',
|
||||
searchable: true,
|
||||
id: 'u:4f1dbbaab280',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '教师',
|
||||
options: [],
|
||||
checkAll: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/team_members?select=users(id,name)&team_id=in.(${select_team})',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: payload.data.items.map(item => {\r\n return {\r\n label: item.name ? item.name : item.users.name,\r\n value: item.id ? item.id : item.users.id\r\n }\r\n })\r\n}',
|
||||
requestAdaptor:
|
||||
"let url = api.url\r\nconst not_use_team = /team_id=in.%28%29/g.test(url)\r\nif (not_use_team) {\r\n url = url.replace(/team_members.+%29/g, 'users?role=eq.teacher')\r\n}\r\nreturn {\r\n ...api,\r\n url: url\r\n}",
|
||||
},
|
||||
clearable: true,
|
||||
creatable: false,
|
||||
multiple: true,
|
||||
joinValues: false,
|
||||
defaultCheckAll: false,
|
||||
checkAllLabel: '全选',
|
||||
extractValue: true,
|
||||
name: 'select_tch',
|
||||
searchable: true,
|
||||
id: 'u:dfd2cad019a7',
|
||||
},
|
||||
],
|
||||
id: 'u:eded6c20659c',
|
||||
feat: 'Edit',
|
||||
},
|
||||
],
|
||||
actions: [],
|
||||
id: 'u:67ee5a75329d',
|
||||
},
|
||||
mode: 'cards',
|
||||
headerToolbar: [],
|
||||
footerToolbar: [
|
||||
{
|
||||
type: 'pagination',
|
||||
},
|
||||
{
|
||||
type: 'switch-per-page',
|
||||
},
|
||||
{
|
||||
type: 'statistics',
|
||||
},
|
||||
],
|
||||
columnsCount: 3,
|
||||
},
|
||||
],
|
||||
id: 'u:414b97a290cd',
|
||||
messages: {},
|
||||
initApi: '',
|
||||
initFetch: '',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
665
src/pages/schema/centre/schedule/teacher_settings.schema.ts
Normal file
665
src/pages/schema/centre/schedule/teacher_settings.schema.ts
Normal file
@@ -0,0 +1,665 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
submitOnChange: true,
|
||||
reload: 'tea_set?tchSelect=$tchSelect',
|
||||
wrapWithPanel: false,
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
name: 'tchSelect',
|
||||
label: '查询',
|
||||
mode: 'inline',
|
||||
size: 'md',
|
||||
clearable: true,
|
||||
searchable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/users_ex?select=id,code,name,project_list,class_list,org_id=eq.$centre_id&role=eq.teacher&order=code',
|
||||
adaptor:
|
||||
"return {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n label: item.name.concat('(', item.code, ')'),\r\n value: item.id\r\n }\r\n })\r\n}",
|
||||
},
|
||||
inputClassName: 'm-l-xs',
|
||||
options: [],
|
||||
inline: true,
|
||||
checkAll: false,
|
||||
id: 'u:0ef31186ce73',
|
||||
},
|
||||
],
|
||||
id: 'u:a09e49581a1e',
|
||||
feat: 'Insert',
|
||||
},
|
||||
{ type: 'divider', lineStyle: 'solid', id: 'u:75cc046fc8ac' },
|
||||
{
|
||||
type: 'tpl',
|
||||
className: 'm-b-sm',
|
||||
tpl: '<% if (data.excludeTeacherTime) { %>\n <span class="label label-warning">选择不可用时间</span>\n<% } else { %>\n <span class="label label-info">选择可用时间</span>\n<% } %>\n<% if (data.excludeTeacherSubject) { %>\n <span class="label label-warning">选择不可任教实验</span>\n<% } else { %>\n <span class="label label-info">选择可任教实验</span>\n<% } %>\n<% if (data.excludeTeacherStudentGroup) { %>\n <span class="label label-warning">选择不可任教班级</span>\n<% } else { %>\n <span class="label label-info">选择可任教班级</span>\n<% } %>',
|
||||
inline: false,
|
||||
id: 'u:83a0a5c3b981',
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/users_ex?select=id,code,name,project_list,class_list,org_id=eq.$centre_id&role=eq.teacher&order=code',
|
||||
data: { page: '$page', perPage: '$perPage', id: 'eq.$tchSelect' },
|
||||
adaptor:
|
||||
"return {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n ...item,\r\n select_project: item.project_list ? item.project_list.map(item => {\r\n return ''.concat(item)\r\n }) : [],\r\n select_class:item.class_list? item.class_list.map(item => {\r\n return ''.concat(item)\r\n }) : [],\r\n }\r\n })\r\n}",
|
||||
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}",
|
||||
replaceData: false,
|
||||
},
|
||||
messages: {},
|
||||
mode: 'cards',
|
||||
card: {
|
||||
type: 'card',
|
||||
header: { title: '$name', subTitle: '$code' },
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
label: '',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/users/${id}',
|
||||
requestAdaptor:
|
||||
"return {\r\n ...api,\r\n data: {\r\n project_list: api.data.select_project[0] === '' ? [] : api.data.select_project,\r\n class_list: api.data.select_class[0] === '' ? [] : api.data.select_class\r\n }\r\n}",
|
||||
data: {
|
||||
select_project: '$select_project',
|
||||
select_class: '$select_class',
|
||||
},
|
||||
},
|
||||
title: '表单',
|
||||
wrapWithPanel: false,
|
||||
submitOnChange: true,
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '实验选择',
|
||||
name: 'select_project',
|
||||
options: [],
|
||||
checkAll: true,
|
||||
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: item.id\r\n }\r\n })\r\n}',
|
||||
},
|
||||
clearable: true,
|
||||
creatable: false,
|
||||
multiple: true,
|
||||
joinValues: false,
|
||||
defaultCheckAll: false,
|
||||
checkAllLabel: '全选',
|
||||
extractValue: true,
|
||||
visibleOn: '',
|
||||
disabledOn:
|
||||
"this.user.role !== 'admin' && this.user.role !== 'centreadmin' ",
|
||||
searchable: true,
|
||||
id: 'u:65fb5a416142',
|
||||
},
|
||||
{
|
||||
type: 'tree-select',
|
||||
label: '班级选择',
|
||||
name: 'select_class',
|
||||
options: [],
|
||||
checkAll: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/orgs?select=*,course2orgs(*,courses(*))&course2orgs.courses.semester_id=eq.$currentSemester&order=code',
|
||||
adaptor:
|
||||
"let school = payload.data.items.find(\r\n item => item.type === 'school'\r\n)\r\nconst recursive = id => {\r\n let nodes = payload.data.items.filter(item => {\r\n return item.parent === id\r\n })\r\n let validNodes = []\r\n nodes.forEach(item => {\r\n let isLeaf = !item.org_children && ((item.type === 'class' || item.type === 'reelectclass' || item.type === 'studentgroup') && item.course2orgs && item.course2orgs.find(item => item.courses !== null))\r\n if (isLeaf) {\r\n validNodes.push({\r\n ...item,\r\n label: item.name,\r\n value: `${item.id}`\r\n })\r\n } else {\r\n let childNodes = recursive(item.id)\r\n if (childNodes.length > 0) {\r\n validNodes.push({\r\n ...item,\r\n label: item.name,\r\n value: `${item.id}`,\r\n children: childNodes\r\n })\r\n }\r\n }\r\n })\r\n return validNodes\r\n}\r\nreturn {\r\n ...payload,\r\n data: recursive(school.id)\r\n}",
|
||||
sendOn: '',
|
||||
},
|
||||
multiple: true,
|
||||
joinValues: false,
|
||||
extractValue: true,
|
||||
mode: 'normal',
|
||||
required: false,
|
||||
onlyChildren: true,
|
||||
searchable: true,
|
||||
cascade: false,
|
||||
withChildren: true,
|
||||
clearable: true,
|
||||
heightAuto: true,
|
||||
virtualThreshold: false,
|
||||
id: 'u:04d5da3d1cd2',
|
||||
},
|
||||
],
|
||||
id: 'u:32d166e0597b',
|
||||
feat: 'Insert',
|
||||
},
|
||||
],
|
||||
toolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '时间选择',
|
||||
icon: 'fa fa-group',
|
||||
iconClassName: '',
|
||||
reload: '',
|
||||
visibleOn:
|
||||
"this.user.role === 'admin' || this.user.role === 'centreadmin' ",
|
||||
id: 'u:a1c14e8c36bc',
|
||||
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,teacher_id,periods(id,start_time,end_time)&teacher_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, semester_id:item.semester_id,organization_id: item.organization_id,teacher_id:item.teacher_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: 'teacher',
|
||||
},
|
||||
],
|
||||
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: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
},
|
||||
target: 'teacher',
|
||||
},
|
||||
],
|
||||
title: '教师时间设置',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'md',
|
||||
actions: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '预览',
|
||||
icon: 'fa fa-eye',
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
iconClassName: '',
|
||||
visibleOn: '!this.excludeTeacherTime',
|
||||
id: 'u:6e637dc227f1',
|
||||
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>\n<span class="label label-warning">X</span>\n<span style="vertical-align: middle;">尽量不安排时间</span>',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/periods?select=*,timeslots(*)×lots.and=(organization_id.eq.$centre_id,date.gte.${semester.since},date.lte.${semester.to},or(teacher_id.eq.$id,teacher_secondary_id.eq.${id}))&order=start_time',
|
||||
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\\.gte\\.)\\d+-\\d+-\\d+/g.exec(api.url)[0]\r\nlet end = /(?<=date\\.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 tip: 'no_id'\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.filter(\r\n f => f.teacher_id !== null\r\n ).forEach(fe => {\r\n const { day, week_num } = get_week_num(sem_start, fe.date)\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].id = fe.id\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].date = fe.date\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].tpl = \"<span title='\".concat(fe.date, \"' class='label label-primary'>\", week, \"</span>\")\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].tip = 't_id'\r\n })\r\n item.timeslots && item.timeslots.filter(\r\n f => f.teacher_secondary_id !== null\r\n ).forEach(fe => {\r\n const { day, week_num } = get_week_num(sem_start, fe.date)\r\n const week = items[(day - 1) * row_mem + index].weeks[week_num - 1].week\r\n if (items[(day - 1) * row_mem + index].weeks[week_num - 1].tip === 't_id') {\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].id = fe.id\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].date = fe.date\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].tpl = \"<span title='\".concat(fe.date, \"' class='label label-warning'>\", week, \"</span>\")\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].tip = 't_s_id'\r\n }\r\n })\r\n})\r\nreturn {\r\n ...payload,\r\n data: {\r\n items: items\r\n }\r\n}",
|
||||
requestAdaptor:
|
||||
'if (api.headers) {\r\n api.headers["query"] = api.query\r\n} else {\r\n api.headers = {\r\n query: api.query\r\n }\r\n}\r\nreturn api;',
|
||||
},
|
||||
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: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '预览',
|
||||
icon: 'fa fa-eye',
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
iconClassName: '',
|
||||
visibleOn: 'this.excludeTeacherTime',
|
||||
id: 'u:872e8943279c',
|
||||
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>\n<span class="label label-warning">X</span>\n<span style="vertical-align: middle;">尽量不安排时间</span>',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/periods?select=*,timeslots(*)×lots.and=(organization_id.eq.$centre_id,date.gte.${semester.since},date.lte.${semester.to},or(teacher_id.eq.$id,teacher_secondary_id.eq.$id))&order=start_time',
|
||||
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\\.gte\\.)\\d+-\\d+-\\d+/g.exec(api.url)[0]\r\nlet end = /(?<=date\\.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 tip: 'no_id'\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.filter(\r\n f => f.teacher_id !== null\r\n ).forEach(fe => {\r\n const { day, week_num } = get_week_num(sem_start, fe.date)\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].id = fe.id\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].date = fe.date\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].tpl = \"<span title='\".concat(fe.date, \"' class='label' style='background: #e5e7eb;'>\", week, \"</span>\")\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].tip = 't_id'\r\n })\r\n item.timeslots && item.timeslots.filter(\r\n f => f.teacher_secondary_id !== null\r\n ).forEach(fe => {\r\n const { day, week_num } = get_week_num(sem_start, fe.date)\r\n const week = items[(day - 1) * row_mem + index].weeks[week_num - 1].week\r\n if (items[(day - 1) * row_mem + index].weeks[week_num - 1].tip === 'no_id') {\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].id = fe.id\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].date = fe.date\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].tpl = \"<span title='\".concat(fe.date, \"' class='label label-warning'>\", week, \"</span>\")\r\n items[(day - 1) * row_mem + index].weeks[week_num - 1].tip = 't_s_id'\r\n }\r\n })\r\n})\r\nreturn {\r\n ...payload,\r\n data: {\r\n items: items\r\n }\r\n}",
|
||||
requestAdaptor:
|
||||
'if (api.headers) {\r\n api.headers["query"] = api.query\r\n} else {\r\n api.headers = {\r\n query: api.query\r\n }\r\n}\r\nreturn api;',
|
||||
},
|
||||
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: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '尽量不安排时间',
|
||||
icon: 'fa fa-group',
|
||||
iconClassName: '',
|
||||
reload: '',
|
||||
visibleOn:
|
||||
"this.user.role === 'admin' || this.user.role === 'centreadmin' ",
|
||||
id: 'u:91324ff3edf8',
|
||||
editorState: 'default',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '教师时间设置',
|
||||
body: [
|
||||
{
|
||||
type: 'timeslot-renderer',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/timeslots?select=id,date,organization_id,teacher_secondary_id,periods(id,start_time,end_time)&teacher_secondary_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 teacher_secondary_id: item.teacher_secondary_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: 'teacher_secondary',
|
||||
},
|
||||
],
|
||||
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: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
},
|
||||
target: 'teacher_secondary',
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'md',
|
||||
actions: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
actionsCount: 5,
|
||||
id: 'u:7b5b16eba035',
|
||||
},
|
||||
syncLocation: false,
|
||||
placeholder: '暂无数据',
|
||||
columnsCount: 3,
|
||||
name: 'tea_set',
|
||||
headerToolbar: [],
|
||||
footerToolbar: [
|
||||
{ type: 'pagination' },
|
||||
{ type: 'switch-per-page' },
|
||||
{ type: 'statistics' },
|
||||
],
|
||||
pageField: 'page',
|
||||
perPageField: 'perPage',
|
||||
perPageAvailable: [6, 18, 36, 54],
|
||||
defaultParams: { perPage: 6 },
|
||||
title: '',
|
||||
body: [
|
||||
{
|
||||
type: 'panel',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/locations?organization_id=eq.$centre_id&order=code',
|
||||
adaptor: '',
|
||||
data: null,
|
||||
replaceData: false,
|
||||
},
|
||||
messages: {},
|
||||
mode: 'cards',
|
||||
headerToolbar: [],
|
||||
card: {
|
||||
header: { title: '${name}' },
|
||||
body: [
|
||||
{ type: 'tpl', tpl: '${intro|raw}' },
|
||||
{
|
||||
type: 'each',
|
||||
name: 'location_list',
|
||||
items: [
|
||||
{
|
||||
type: 'plain',
|
||||
className: 'm-l-sm',
|
||||
tpl: '${users.name}',
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
actionType: 'ajax',
|
||||
level: 'link',
|
||||
icon: 'fa fa-trash',
|
||||
iconClassName: 'text-danger',
|
||||
api: {
|
||||
method: 'delete',
|
||||
url: 'rest/team_members?team_id=eq.${id}&teacher_id=eq.${users.id}',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: '未添加可用时间',
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '可用时间设置 ',
|
||||
actionType: 'dialog',
|
||||
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: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
},
|
||||
target: 'location',
|
||||
},
|
||||
],
|
||||
title: '场地时间设置',
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'md',
|
||||
actions: [],
|
||||
},
|
||||
icon: 'fa fa-group',
|
||||
iconClassName: '',
|
||||
reload: '',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '预览可用时间 ',
|
||||
actionType: 'dialog',
|
||||
icon: 'fa fa-eye',
|
||||
level: 'link',
|
||||
size: 'md',
|
||||
iconClassName: '',
|
||||
dialog: {
|
||||
title: '场地可用时间',
|
||||
body: [
|
||||
{
|
||||
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=start_time',
|
||||
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 class='label label-warning'>\".concat(j + 1, \"</span>\"),\r\n tip: date_temp.toISOString().split('T')[0]\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 label-success'>\".concat(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' },
|
||||
{
|
||||
type: 'text',
|
||||
label: '',
|
||||
name: 'period.lab',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
name: 'weeks',
|
||||
label: '',
|
||||
type: 'each',
|
||||
items: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<%= data.tpl %>',
|
||||
inline: true,
|
||||
className: 'm-r-xs',
|
||||
},
|
||||
],
|
||||
placeholder: '-',
|
||||
},
|
||||
],
|
||||
combineNum: 1,
|
||||
columnsTogglable: false,
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
data: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'card',
|
||||
actionsCount: 3,
|
||||
},
|
||||
columnsCount: 3,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
masonryLayout: false,
|
||||
itemClassName: '',
|
||||
filter: null,
|
||||
checkAll: false,
|
||||
id: 'u:2133b3bfaf09',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
title: '',
|
||||
cssVars: { '--Checkbox-onDisabled-color': 'transparent' },
|
||||
initApi: {
|
||||
method: 'get',
|
||||
url: 'rest/config?key=eq.system&organization_id=eq.${centre_id}',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n excludeTeacherTime: payload.data.items[0].value.excludeTeacherTime,\r\n excludeTeacherStudentGroup: payload.data.items[0].value.excludeTeacherStudentGroup,\r\n excludeTeacherSubject: payload.data.items[0].value.excludeTeacherSubject\r\n }\r\n}',
|
||||
},
|
||||
id: 'u:31421ebcd506',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
75
src/pages/schema/system/bucket.schema.ts
Normal file
75
src/pages/schema/system/bucket.schema.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
const schema = {
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
columns: [
|
||||
{ type: 'text', name: 'id', label: '编号' },
|
||||
{ type: 'text', name: 'name', label: '名称' },
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '是否公共空间',
|
||||
name: 'public',
|
||||
tpl: '<% if (data.public) { %>\n <span class="label label-success">是</span>\n<% } %>',
|
||||
inline: false,
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/buckets',
|
||||
headers: { 'Accept-Profile': 'storage' },
|
||||
},
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '添加存储空间',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/buckets',
|
||||
data: null,
|
||||
dataType: 'json',
|
||||
headers: { 'Content-Profile': 'storage' },
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '名称',
|
||||
name: 'name',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
{ type: 'formula', name: 'id', formula: 'data.name' },
|
||||
{ type: 'hidden', name: 'id' },
|
||||
{ type: 'hidden', name: 'public', value: 'true' },
|
||||
],
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
icon: 'fa fa-plus text-primary',
|
||||
size: 'lg',
|
||||
level: 'link',
|
||||
tooltip: '添加',
|
||||
tooltipPlacement: 'left',
|
||||
align: 'right',
|
||||
label: '',
|
||||
className: 'p-l-none',
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
bulkActions: [],
|
||||
itemActions: [],
|
||||
},
|
||||
],
|
||||
type: 'page',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
153
src/pages/schema/system/dicts.schema.ts
Normal file
153
src/pages/schema/system/dicts.schema.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
id: 'u:ac8687f8ba94',
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
mode: 'inline',
|
||||
feat: 'View',
|
||||
body: [
|
||||
{
|
||||
type: 'group',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '数据模型',
|
||||
name: 'typecode',
|
||||
mode: 'inline',
|
||||
clearable: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/dicts?order=typecode',
|
||||
adaptor:
|
||||
'const options = []\r\n\r\npayload.data.items.forEach(item => {\r\n if (!options.some(option => option.typecode === item.typecode))\r\n options.push({typecode: item.typecode, typename: item.typename})\r\n})\r\n\r\nreturn {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: options.map(item => {\r\n return {\r\n label: item.typename,\r\n value: item.typecode\r\n }\r\n })\r\n }\r\n}',
|
||||
requestAdaptor: '',
|
||||
messages: {},
|
||||
},
|
||||
checkAll: false,
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
inputClassName: 'm-l-xs',
|
||||
id: 'u:66e672b9a4eb',
|
||||
multiple: false,
|
||||
},
|
||||
],
|
||||
id: 'u:32358eae378f',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
id: 'u:41b4b3321711',
|
||||
},
|
||||
],
|
||||
className: '',
|
||||
submitOnChange: true,
|
||||
submitOnInit: true,
|
||||
reload: 'dict?typecode=$typecode',
|
||||
wrapWithPanel: false,
|
||||
name: 'filter',
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
id: 'u:7226d14e603b',
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
tpl: '内容',
|
||||
align: 'right',
|
||||
label: '',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '添加页面',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/pages',
|
||||
},
|
||||
controls: [
|
||||
{
|
||||
type: 'text',
|
||||
label: '名称',
|
||||
name: 'name',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
},
|
||||
icon: 'fa fa-plus text-primary',
|
||||
level: 'link',
|
||||
visibleOn: 'false',
|
||||
id: 'u:b509f4d7b6a2',
|
||||
},
|
||||
{
|
||||
type: 'bulk-actions',
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/dicts?order=typecode',
|
||||
messages: {},
|
||||
requestAdaptor:
|
||||
"if (api.query.typecode === '')\r\n api.url = api.url.replace('&typecode=', '')\r\nelse \r\n api.url = api.url.replace('&typecode=' + api.query.typecode, '&typecode=eq.' + api.query.typecode)\r\n\r\nreturn api",
|
||||
adaptor: '',
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
name: 'id',
|
||||
label: '编号',
|
||||
type: 'text',
|
||||
id: 'u:2e8854f46051',
|
||||
},
|
||||
{
|
||||
name: 'typename',
|
||||
label: '数据模型',
|
||||
type: 'text',
|
||||
placeholder: '-',
|
||||
sortable: false,
|
||||
searchable: false,
|
||||
id: 'u:a86a77369cc6',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '数据模型编号',
|
||||
id: 'u:a86a77369cc6',
|
||||
name: 'typecode',
|
||||
placeholder: '-',
|
||||
sortable: false,
|
||||
searchable: false,
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '字典项',
|
||||
name: 'dictkey',
|
||||
id: 'u:b97cdabd49f0',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '字典值',
|
||||
name: 'dictvalue',
|
||||
id: 'u:742d4dd946a7',
|
||||
},
|
||||
],
|
||||
bulkActions: [],
|
||||
itemActions: [],
|
||||
messages: {},
|
||||
perPageAvailable: [10],
|
||||
filter: null,
|
||||
syncLocation: false,
|
||||
name: 'dict',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
title: '',
|
||||
id: 'u:02a9ce84345c',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
49
src/pages/schema/system/init.schema.ts
Normal file
49
src/pages/schema/system/init.schema.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
const schema = {
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/orgs',
|
||||
requestAdaptor:
|
||||
"let orgs = [];\r\norgs.push({\r\n id: 1,\r\n code: 1,\r\n name: api.data.school,\r\n type: 'school',\r\n parent: null\r\n});\r\nconsole.log(api.data.centres);\r\napi.data.centres.forEach((x,index)=> {\r\n orgs.push({\r\n id: index+2,\r\n code: `centre${index}`,\r\n name: x,\r\n type: 'centre',\r\n parent: 1\r\n });\r\n})\r\n\r\nreturn {\r\n ...api,\r\n data: orgs\r\n};",
|
||||
dataType: 'json',
|
||||
},
|
||||
title: '初始化',
|
||||
submitText: '确定',
|
||||
affixFooter: false,
|
||||
visibleOn: '!this.items || this.items.length===0',
|
||||
body: [
|
||||
{
|
||||
label: '学校名称',
|
||||
type: 'input-text',
|
||||
name: 'school',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
{
|
||||
type: 'input-array',
|
||||
label: '教学中心',
|
||||
name: 'centres',
|
||||
items: { type: 'input-text', placeholder: '请输入' },
|
||||
addButtonText: '新增中心',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
value: '教学中心',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<p style="text-align: center;"><strong>初始化完成</strong></p>',
|
||||
inline: false,
|
||||
__mode: 'rich-text',
|
||||
visibleOn: 'this.items && this.items.length>0',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
initApi: 'rest/orgs?type=eq.school',
|
||||
type: 'page',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
273
src/pages/schema/system/nav.schema.ts
Normal file
273
src/pages/schema/system/nav.schema.ts
Normal file
@@ -0,0 +1,273 @@
|
||||
const schema = {
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
columns: [
|
||||
{ type: 'text', name: 'id', label: '编号' },
|
||||
{
|
||||
type: 'text',
|
||||
name: 'label',
|
||||
label: '菜单名称',
|
||||
placeholder: '-',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '图标',
|
||||
placeholder: '-',
|
||||
name: 'icon',
|
||||
tpl: '<i class="${icon}"></i>',
|
||||
inline: true,
|
||||
quickEdit: {
|
||||
|
||||
type: 'icon-picker',
|
||||
label: '图标',
|
||||
name: 'icon',
|
||||
labelField: 'text',
|
||||
mode: 'popOver',
|
||||
labelTpl: '${value}',
|
||||
saveImmediately: {
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/navs?id=eq.${id}',
|
||||
data: { icon: '${icon}' },
|
||||
dataType: 'form',
|
||||
},
|
||||
},
|
||||
},
|
||||
popOver: false,
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '上级菜单',
|
||||
name: 'parent_name',
|
||||
placeholder: '-',
|
||||
tpl: '<span class="label label-info">${parent_name}</span>',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '顺序',
|
||||
tpl: '${order}',
|
||||
inline: true,
|
||||
name: 'order',
|
||||
placeholder: '-',
|
||||
quickEdit: {
|
||||
type: 'input-number',
|
||||
name: 'order',
|
||||
label: '数字',
|
||||
min: '1',
|
||||
step: 1,
|
||||
value: 1,
|
||||
saveImmediately: {
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/navs?id=eq.${id}',
|
||||
data: { order: '$order' },
|
||||
dataType: 'form',
|
||||
},
|
||||
},
|
||||
mode: 'popOver',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '关联页面',
|
||||
placeholder: '-',
|
||||
tpl: '<span class="label label-info">${schema_path}</span>',
|
||||
inline: true,
|
||||
name: 'schema_path',
|
||||
className: '',
|
||||
quickEdit: {
|
||||
mode: 'popOver',
|
||||
type: 'input-text',
|
||||
label: '页面相对存储路径',
|
||||
name: 'schema_path',
|
||||
saveImmediately: {
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/navs?id=eq.${id}',
|
||||
data: { schema_path: '${schema_path}' },
|
||||
dataType: 'form',
|
||||
},
|
||||
},
|
||||
},
|
||||
style: {},
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '编辑菜单',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/navs/${id}',
|
||||
data: { '&': '$$' },
|
||||
dataType: 'form',
|
||||
requestAdaptor:
|
||||
'console.log(api)\r\nif(api.body.path === null) {\r\n delete api.body.path; \r\n}\r\n\r\nreturn api;',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
label: '菜单名称',
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
{
|
||||
type: 'icon-picker',
|
||||
label: '图标',
|
||||
name: 'icon',
|
||||
mode: 'normal',
|
||||
labelField: 'text',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '上级菜单',
|
||||
name: 'parent',
|
||||
options: [],
|
||||
mode: 'normal',
|
||||
checkAll: false,
|
||||
source: 'rest/navs?select=label:name,value:id',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '关联页面',
|
||||
name: 'path',
|
||||
options: [],
|
||||
mode: 'normal',
|
||||
checkAll: false,
|
||||
source: 'rest/pages?select=label:name,value:id',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
data: { '&': '$$' },
|
||||
},
|
||||
level: 'link',
|
||||
icon: 'fa fa-pencil text-info',
|
||||
iconClassName: 'pull-left',
|
||||
size: 'md',
|
||||
tooltip: '修改',
|
||||
tooltipPlacement: 'top',
|
||||
className: 'p-r-none p-l-none',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
actionType: 'ajax',
|
||||
size: 'md',
|
||||
icon: 'fa fa-times text-danger',
|
||||
level: 'link',
|
||||
iconClassName: 'pull-left',
|
||||
tooltip: '删除',
|
||||
tooltipPlacement: 'top',
|
||||
className: 'p-r-none p-l-none',
|
||||
api: { method: 'delete', url: 'rest/navs/${id}' },
|
||||
confirmText: '确认删除 ${label} 吗?',
|
||||
},
|
||||
],
|
||||
label: '操作',
|
||||
placeholder: '-',
|
||||
width: 200,
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/navs?select=label:name,value:id,*,pages!nav_path_fkey(name)&order=order.asc',
|
||||
data: {},
|
||||
adaptor:
|
||||
'let all_navs = payload.data.items;\r\nconst buildNavs = function(navs, id) {\r\n return navs.filter((x)=>x.parent===id).map((x)=>{\r\n let subNavs = buildNavs(navs, x.id);\r\n return {\r\n ...x,\r\n parent_name: all_navs.find(n=>n.id===x.parent)?.name,\r\n label: x.name,\r\n children: subNavs && subNavs.length > 0 ? subNavs : null\r\n }\r\n });\r\n }\r\n \r\n return {\r\n ...payload,\r\n data: {\r\n items: buildNavs(payload.data.items, null)\r\n }\r\n }',
|
||||
},
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '添加菜单',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/navs',
|
||||
data: null,
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '菜单名称',
|
||||
name: 'name',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
{
|
||||
type: 'icon-picker',
|
||||
label: '图标',
|
||||
name: 'icon',
|
||||
mode: 'normal',
|
||||
labelField: 'text',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'parent',
|
||||
label: '上级菜单',
|
||||
mode: 'normal',
|
||||
checkAll: false,
|
||||
source: 'rest/navs?select=label:name,value:id',
|
||||
options: [],
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'path',
|
||||
label: '关联页面',
|
||||
mode: 'normal',
|
||||
options: [],
|
||||
checkAll: false,
|
||||
source: 'rest/pages?select=label:name,value:id',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
icon: 'fa fa-plus',
|
||||
size: 'lg',
|
||||
level: 'link',
|
||||
tooltip: '添加',
|
||||
tooltipPlacement: 'left',
|
||||
className: 'pull-right p-l-none',
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
perPageAvailable: [10],
|
||||
perPageField: 'perPage',
|
||||
bulkActions: [],
|
||||
itemActions: [],
|
||||
filter: null,
|
||||
features: ['filter', 'create'],
|
||||
source: '',
|
||||
perPage: null,
|
||||
hideQuickSaveBtn: true,
|
||||
loadDataOnce: true,
|
||||
},
|
||||
],
|
||||
type: 'page',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
392
src/pages/schema/system/page.schema.ts
Normal file
392
src/pages/schema/system/page.schema.ts
Normal file
@@ -0,0 +1,392 @@
|
||||
const schema = {
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
columns: [
|
||||
{ type: 'text', name: 'id', label: '编号' },
|
||||
{ type: 'text', name: 'label', label: '菜单名称', placeholder: '-' },
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '图标',
|
||||
placeholder: '-',
|
||||
name: 'icon',
|
||||
tpl: '<i class="${icon}"></i>',
|
||||
inline: true,
|
||||
quickEdit: {
|
||||
type: 'picker',
|
||||
name: 'icon',
|
||||
label: '',
|
||||
options: [],
|
||||
source: 'icons.json',
|
||||
pickerSchema: {
|
||||
mode: 'cards',
|
||||
listItem: {
|
||||
title: '${value}',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<i class="${value}"></i>',
|
||||
inline: false,
|
||||
label: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
perPageAvailable: [10],
|
||||
messages: {},
|
||||
card: {
|
||||
type: 'card',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<i class="${value}"></i>',
|
||||
inline: false,
|
||||
label: '',
|
||||
},
|
||||
],
|
||||
actionsCount: 1,
|
||||
className: '',
|
||||
bodyClassName: '',
|
||||
},
|
||||
loadDataOnce: true,
|
||||
alwaysShowPagination: false,
|
||||
placeholder: '暂无数据',
|
||||
columnsCount: 8,
|
||||
perPageField: 'perPage',
|
||||
perPage: 50,
|
||||
className: '',
|
||||
masonryLayout: false,
|
||||
itemClassName: '',
|
||||
},
|
||||
modalMode: 'dialog',
|
||||
embed: false,
|
||||
size: 'lg',
|
||||
mode: 'popOver',
|
||||
labelTpl: '${value}',
|
||||
saveImmediately: {
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/navs?id=eq.${id}',
|
||||
data: { icon: '${icon}' },
|
||||
dataType: 'form',
|
||||
},
|
||||
},
|
||||
},
|
||||
popOver: false,
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '上级菜单',
|
||||
name: 'parent_name',
|
||||
placeholder: '-',
|
||||
tpl: '<span class="label label-info">${parent_name}</span>',
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '顺序',
|
||||
tpl: '${order}',
|
||||
inline: true,
|
||||
name: 'order',
|
||||
placeholder: '-',
|
||||
quickEdit: {
|
||||
type: 'input-number',
|
||||
name: 'order',
|
||||
label: '数字',
|
||||
min: '1',
|
||||
step: 1,
|
||||
value: 1,
|
||||
saveImmediately: {
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/navs?id=eq.${id}',
|
||||
data: { order: '$order' },
|
||||
dataType: 'form',
|
||||
},
|
||||
},
|
||||
mode: 'popOver',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '关联页面',
|
||||
placeholder: '-',
|
||||
tpl: '<span class="label label-info">${schema_path}</span>',
|
||||
inline: true,
|
||||
name: 'schema_path',
|
||||
className: '',
|
||||
quickEdit: {
|
||||
mode: 'popOver',
|
||||
type: 'input-text',
|
||||
label: '页面相对存储路径',
|
||||
name: 'schema_path',
|
||||
saveImmediately: {
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/navs?id=eq.${id}',
|
||||
data: { schema_path: '${schema_path}' },
|
||||
dataType: 'form',
|
||||
},
|
||||
},
|
||||
},
|
||||
style: {},
|
||||
},
|
||||
{
|
||||
type: 'operation',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '编辑菜单',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/navs/${id}',
|
||||
data: { '&': '$$' },
|
||||
dataType: 'form',
|
||||
requestAdaptor:
|
||||
'console.log(api)\r\nif(api.body.path === null) {\r\n delete api.body.path; \r\n}\r\n\r\nreturn api;',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
label: '菜单名称',
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
{
|
||||
type: 'picker',
|
||||
label: '图标',
|
||||
name: 'icon',
|
||||
options: [],
|
||||
mode: 'normal',
|
||||
modalMode: 'dialog',
|
||||
source: 'icons.json',
|
||||
pickerSchema: {
|
||||
mode: 'cards',
|
||||
listItem: {
|
||||
title: '${value}',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<i class="${value}"></i>',
|
||||
inline: false,
|
||||
label: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
perPageAvailable: [10],
|
||||
messages: {},
|
||||
card: {
|
||||
type: 'card',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<i class="${value}"></i>',
|
||||
inline: false,
|
||||
label: '',
|
||||
},
|
||||
],
|
||||
actionsCount: 1,
|
||||
className: '',
|
||||
bodyClassName: '',
|
||||
},
|
||||
loadDataOnce: true,
|
||||
alwaysShowPagination: false,
|
||||
placeholder: '暂无数据',
|
||||
columnsCount: 8,
|
||||
perPageField: 'perPage',
|
||||
perPage: 50,
|
||||
className: '',
|
||||
masonryLayout: false,
|
||||
itemClassName: '',
|
||||
},
|
||||
size: 'lg',
|
||||
labelTpl: '${value}',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '上级菜单',
|
||||
name: 'parent',
|
||||
options: [],
|
||||
mode: 'normal',
|
||||
checkAll: false,
|
||||
source: 'rest/navs?select=label:name,value:id',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '关联页面',
|
||||
name: 'path',
|
||||
options: [],
|
||||
mode: 'normal',
|
||||
checkAll: false,
|
||||
source: 'rest/pages?select=label:name,value:id',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
data: { '&': '$$' },
|
||||
},
|
||||
level: 'link',
|
||||
icon: 'fa fa-pencil text-info',
|
||||
iconClassName: 'pull-left',
|
||||
size: 'md',
|
||||
tooltip: '修改',
|
||||
tooltipPlacement: 'top',
|
||||
className: 'm-r-none m-l-none',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
actionType: 'ajax',
|
||||
size: 'md',
|
||||
icon: 'fa fa-times text-danger',
|
||||
level: 'link',
|
||||
iconClassName: 'pull-left',
|
||||
tooltip: '删除',
|
||||
tooltipPlacement: 'top',
|
||||
className: 'm-r-none m-l-none',
|
||||
api: { method: 'delete', url: 'rest/navs/${id}' },
|
||||
confirmText: '确认删除 ${label} 吗?',
|
||||
},
|
||||
],
|
||||
label: '操作',
|
||||
placeholder: '-',
|
||||
width: 200,
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/navs?select=label:name,value:id,*,pages!nav_path_fkey(name)&order=order.asc',
|
||||
data: {},
|
||||
adaptor:
|
||||
'let all_navs = payload.data.items;\r\nconst buildNavs = function(navs, id) {\r\n return navs.filter((x)=>x.parent===id).map((x)=>{\r\n let subNavs = buildNavs(navs, x.id);\r\n return {\r\n ...x,\r\n parent_name: all_navs.find(n=>n.id===x.parent)?.name,\r\n label: x.name,\r\n children: subNavs && subNavs.length > 0 ? subNavs : null\r\n }\r\n });\r\n }\r\n \r\n return {\r\n ...payload,\r\n data: {\r\n items: buildNavs(payload.data.items, null)\r\n }\r\n }',
|
||||
},
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '添加菜单',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: { method: 'post', url: 'rest/navs', data: null },
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '菜单名称',
|
||||
name: 'name',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
},
|
||||
{
|
||||
type: 'picker',
|
||||
name: 'icon',
|
||||
label: '图标',
|
||||
mode: 'normal',
|
||||
options: [],
|
||||
modalMode: 'dialog',
|
||||
source: 'icons.json',
|
||||
pickerSchema: {
|
||||
mode: 'cards',
|
||||
listItem: {
|
||||
title: '${value}',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<i class="${value}"></i>',
|
||||
inline: false,
|
||||
label: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
perPageAvailable: [10],
|
||||
messages: {},
|
||||
card: {
|
||||
type: 'card',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '<i class="${value}"></i>',
|
||||
inline: false,
|
||||
label: '',
|
||||
},
|
||||
],
|
||||
actionsCount: 1,
|
||||
className: '',
|
||||
bodyClassName: '',
|
||||
},
|
||||
loadDataOnce: true,
|
||||
alwaysShowPagination: false,
|
||||
placeholder: '暂无数据',
|
||||
columnsCount: 8,
|
||||
perPageField: 'perPage',
|
||||
perPage: 50,
|
||||
className: '',
|
||||
masonryLayout: false,
|
||||
itemClassName: '',
|
||||
},
|
||||
size: 'lg',
|
||||
labelTpl: '${value}',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'parent',
|
||||
label: '上级菜单',
|
||||
mode: 'normal',
|
||||
checkAll: false,
|
||||
source: 'rest/navs?select=label:name,value:id',
|
||||
options: [],
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'path',
|
||||
label: '关联页面',
|
||||
mode: 'normal',
|
||||
options: [],
|
||||
checkAll: false,
|
||||
source: 'rest/pages?select=label:name,value:id',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
},
|
||||
icon: 'fa fa-plus',
|
||||
size: 'lg',
|
||||
level: 'link',
|
||||
tooltip: '添加',
|
||||
tooltipPlacement: 'left',
|
||||
className: 'pull-right p-l-none',
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
perPageAvailable: [10],
|
||||
perPageField: 'perPage',
|
||||
bulkActions: [],
|
||||
itemActions: [],
|
||||
filter: null,
|
||||
features: ['filter', 'create'],
|
||||
source: '',
|
||||
perPage: null,
|
||||
hideQuickSaveBtn: true,
|
||||
loadDataOnce: true,
|
||||
},
|
||||
],
|
||||
type: 'page',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
160
src/pages/schema/system/role.schema.ts
Normal file
160
src/pages/schema/system/role.schema.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
const schema = {
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
api: 'rest/dicts?typecode=eq.001',
|
||||
columns: [
|
||||
{ name: 'dictvalue', label: '角色名称', type: 'text' },
|
||||
{ name: 'dictkey', label: '角色代号', type: 'text' },
|
||||
{
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '菜单权限',
|
||||
actionType: 'drawer',
|
||||
level: 'primary',
|
||||
size: 'sm',
|
||||
drawer: {
|
||||
type: 'drawer',
|
||||
title: '菜单权限',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
initApi: {
|
||||
method: 'get',
|
||||
url: 'rest/role2navs?role=eq.${dictkey}',
|
||||
adaptor:
|
||||
'return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n navs: payload.data.items.map(x=>x.nav_id)\r\n }\r\n}',
|
||||
},
|
||||
wrapWithPanel: false,
|
||||
mode: 'normal',
|
||||
className: 'h-full',
|
||||
body: [
|
||||
{ type: 'hidden', name: 'role' },
|
||||
{
|
||||
type: 'input-tree',
|
||||
label: '',
|
||||
name: 'navs',
|
||||
options: [],
|
||||
showRadio: true,
|
||||
multiple: true,
|
||||
joinValues: false,
|
||||
mode: 'normal',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/navs?select=label:name,value:id,*',
|
||||
adaptor:
|
||||
'const buildNavs = function(navs, id) {\r\n return navs.filter((x)=>x.parent===id).map((x)=>{\r\n let subNavs = buildNavs(navs, x.id);\r\n return {\r\n ...x,\r\n label: x.name,\r\n children: subNavs && subNavs.length > 0 ? subNavs : null\r\n }\r\n });\r\n }\r\n \r\n return {\r\n ...payload,\r\n data: {\r\n items: buildNavs(payload.data.items, null)\r\n }\r\n }',
|
||||
},
|
||||
hideRoot: true,
|
||||
extractValue: true,
|
||||
className: 'h-full',
|
||||
inputClassName: 'no-border h-full',
|
||||
showIcon: false,
|
||||
withChildren: true,
|
||||
cascade: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
size: 'md',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
level: 'primary',
|
||||
label: '保存',
|
||||
actionType: 'ajax',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/create_role2navs',
|
||||
requestAdaptor:
|
||||
'return {\r\n ...api,\r\n data: {\r\n role:api.data.role,\r\n navs: api.data.navs.map(item=>{\r\n return {\r\n role: api.data.role,\r\n nav_id: item\r\n }\r\n})\r\n }\r\n}',
|
||||
data: { '&': '$$' },
|
||||
dataType: 'json',
|
||||
headers: { Prefer: 'params=single-object' },
|
||||
},
|
||||
},
|
||||
],
|
||||
position: 'right',
|
||||
data: { '&': '$$', role: '${dictkey}' },
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '设置默认页面',
|
||||
type: 'button',
|
||||
actionType: 'drawer',
|
||||
level: 'warning',
|
||||
drawer: {
|
||||
type: 'drawer',
|
||||
title: '设置默认页面',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
initApi: {
|
||||
method: 'get',
|
||||
url: 'rest/role2navs?role=eq.${role}',
|
||||
adaptor:
|
||||
'let default_nav = payload.data.items.find(x=>x.is_default);\r\nreturn {\r\n ...payload,\r\n data: {\r\n nav_id: default_nav ? default_nav.nav_id : null\r\n }\r\n}',
|
||||
},
|
||||
wrapWithPanel: false,
|
||||
mode: 'normal',
|
||||
className: 'h-full',
|
||||
body: [
|
||||
{
|
||||
type: 'input-tree',
|
||||
name: 'nav_id',
|
||||
label: '',
|
||||
showRadio: true,
|
||||
multiple: false,
|
||||
joinValues: false,
|
||||
mode: 'normal',
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/navs?select=label:name,value:id,*',
|
||||
adaptor:
|
||||
'const buildNavs = function(navs, id) {\r\n return navs.filter((x)=>x.parent===id).map((x)=>{\r\n let subNavs = buildNavs(navs, x.id);\r\n return {\r\n ...x,\r\n label: x.name,\r\n children: subNavs && subNavs.length > 0 ? subNavs : null\r\n }\r\n });\r\n }\r\n \r\n return {\r\n ...payload,\r\n data: {\r\n items: buildNavs(payload.data.items, null)\r\n }\r\n }',
|
||||
},
|
||||
hideRoot: true,
|
||||
extractValue: true,
|
||||
className: 'h-full',
|
||||
inputClassName: 'no-border h-full',
|
||||
showIcon: false,
|
||||
withChildren: true,
|
||||
options: [],
|
||||
},
|
||||
],
|
||||
submitOnChange: true,
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/set_default_page',
|
||||
data: { role: '$role', nav_id: '${nav_id.id}' },
|
||||
},
|
||||
},
|
||||
],
|
||||
actions: [],
|
||||
data: { role: '${dictkey}' },
|
||||
position: 'right',
|
||||
size: 'md',
|
||||
},
|
||||
size: 'sm',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
bulkActions: [],
|
||||
itemActions: [],
|
||||
messages: {},
|
||||
perPageAvailable: [10],
|
||||
filter: null,
|
||||
features: ['create', 'update', 'delete', 'itemDelete'],
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
title: '',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
861
src/pages/schema/system/test.schema.ts
Normal file
861
src/pages/schema/system/test.schema.ts
Normal file
@@ -0,0 +1,861 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/users_ex?select=*,user2courses!inner(*)&and=(or(code.like.*${keywords}*,name.like.*${keywords}*))&user2courses.course_id=eq.1&org_id=in.($class_ids)&order=org_name,code',
|
||||
data: {
|
||||
page: '$page',
|
||||
perPage: '$perPage',
|
||||
},
|
||||
requestAdaptor:
|
||||
"api.url = api.url.replace('&org_id=in.%28%29', '')\r\n\r\nreturn api",
|
||||
},
|
||||
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: '',
|
||||
id: 'u:c5ca01d4b027',
|
||||
},
|
||||
{
|
||||
type: 'control',
|
||||
label: '',
|
||||
body: [
|
||||
{
|
||||
type: 'submit',
|
||||
level: 'primary',
|
||||
actionType: 'submit',
|
||||
label: '搜索',
|
||||
id: 'u:455b9e0bc653',
|
||||
},
|
||||
],
|
||||
id: 'u:77ed64056ac1',
|
||||
},
|
||||
],
|
||||
id: 'u:2289c530be59',
|
||||
},
|
||||
],
|
||||
id: 'u:50dafca9d406',
|
||||
feat: 'Insert',
|
||||
},
|
||||
headerToolbar: [
|
||||
'bulkActions',
|
||||
{
|
||||
type: 'button',
|
||||
align: 'right',
|
||||
label: '添加',
|
||||
icon: 'fa fa-plus',
|
||||
level: 'default',
|
||||
id: 'u:f303a4872364',
|
||||
visibleOn:
|
||||
'this.semesterSelect === this.currentSemester',
|
||||
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: 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: '',
|
||||
},
|
||||
submitOnChange: true,
|
||||
autoComplete: '',
|
||||
mode: 'inline',
|
||||
className: '',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '添加学生',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '学生',
|
||||
name: 'users',
|
||||
id: 'u:3b0736cc7da1',
|
||||
multiple: true,
|
||||
joinValues: false,
|
||||
extractValue: true,
|
||||
required: true,
|
||||
searchable: true,
|
||||
checkAll: false,
|
||||
defaultCheckAll: false,
|
||||
checkAllLabel: '全选',
|
||||
source: {
|
||||
url: 'rest/users?role=eq.student&order=code,name',
|
||||
method: 'get',
|
||||
requestAdaptor: '',
|
||||
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 + "(" + item.code + ")",\r\n value: item.id\r\n }\r\n })\r\n }\r\n}',
|
||||
messages: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/user2courses?on_conflict=user_id,course_id,semester_id',
|
||||
headers: {
|
||||
Prefer:
|
||||
'resolution=ignore-duplicates',
|
||||
},
|
||||
data: {
|
||||
users: '$users',
|
||||
course_id: '$id',
|
||||
semester_id: '$currentSemester',
|
||||
},
|
||||
dataType: 'json',
|
||||
requestAdaptor:
|
||||
'const data = api.data.users.map(item => {\r\n return {\r\n user_id: item,\r\n course_id: api.data.course_id,\r\n semester_id: api.data.semester_id\r\n }\r\n})\r\n\r\napi.data = data\r\n\r\nreturn api',
|
||||
},
|
||||
id: 'u:f305be19cdbe',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:148f700a4963',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
data: {},
|
||||
dataMergeMode: 'override',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
id: 'u:3fd328b506be',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:851befb0af70',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:916add780f41',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'export-excel',
|
||||
align: 'right',
|
||||
label: '导出学生',
|
||||
icon: 'fa fa-download',
|
||||
level: 'primary',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/users_ex?select=*,user2courses!inner(*)&and=(or(code.like.*${keywords}*,name.like.*${keywords}*))&user2courses.course_id=eq.$id&org_id=in.($class_ids)&order=org_name,code',
|
||||
requestAdaptor:
|
||||
"api.url = api.url.replace('&org_id=in.%28%29', '')\r\n\r\nreturn api",
|
||||
},
|
||||
filename: '学生',
|
||||
exportColumns: [
|
||||
{
|
||||
label: '*账号',
|
||||
name: 'code',
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: '*姓名',
|
||||
},
|
||||
{
|
||||
name: 'org_name',
|
||||
label: '班级'
|
||||
}
|
||||
],
|
||||
id: 'u:ab01cbc28399',
|
||||
},
|
||||
],
|
||||
bulkActions: [
|
||||
{
|
||||
label: '批量删除',
|
||||
actionType: 'ajax',
|
||||
api: {
|
||||
method: 'delete',
|
||||
url: 'rest/users?id=in.(${ids|raw})&order=code',
|
||||
},
|
||||
confirmText: '确定要批量删除?',
|
||||
align: 'right',
|
||||
id: 'u:261e4303f69a',
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
footerToolbar: [
|
||||
{
|
||||
type: 'pagination',
|
||||
tpl: '内容',
|
||||
wrapperComponent: '',
|
||||
id: 'u:727cc434bcb9',
|
||||
},
|
||||
{
|
||||
type: 'switch-per-page',
|
||||
tpl: '内容',
|
||||
wrapperComponent: '',
|
||||
id: 'u:bba45fa21764',
|
||||
},
|
||||
{
|
||||
type: 'statistics',
|
||||
tpl: '内容',
|
||||
wrapperComponent: '',
|
||||
id: 'u:d80c5e0ac620',
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'code',
|
||||
label: '学号',
|
||||
id: 'u:9233f140d314',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
label: '姓名',
|
||||
id: 'u:cb913014ed05',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '班级',
|
||||
name: 'org_name',
|
||||
id: 'u:d0de67b68472',
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
actionType: 'ajax',
|
||||
size: 'md',
|
||||
level: 'link',
|
||||
icon: 'fa fa-times text-danger',
|
||||
api: {
|
||||
method: 'delete',
|
||||
url: 'rest/users/$id',
|
||||
},
|
||||
confirmText: '确认删除"${name}(${code})"?',
|
||||
visibleOn: "this.role === 'student'",
|
||||
tooltip: '删除账号',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
id: 'u:e76d236dfbbc',
|
||||
}
|
||||
],
|
||||
placeholder: '-',
|
||||
width: 200,
|
||||
}
|
||||
],
|
||||
name: 'group',
|
||||
bodyClassName: '',
|
||||
title: '',
|
||||
initFetch: true,
|
||||
id: 'u:148f700a4963',
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
pageField: 'page',
|
||||
perPageField: 'perPage',
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
messages: {},
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/courses?select=*,select_projects:projects,classes,groups,course2projects(*,projects(*,project_type:project_type_fk(dictvalue))),course2orgs(users(*),orgs(*))&organization_id=eq.2&semester_id=eq.$semesterSelect&order=code',
|
||||
data: {
|
||||
id: 'eq.$courseSelect',
|
||||
},
|
||||
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 course2projects: item.course2projects.sort((a, b) => {\r\n return a.projects.name.localeCompare(b.projects.name)\r\n }),\r\n course2orgs: item.course2orgs.filter(item => item.orgs.type !== "studentgroup").sort((a, b) => {\r\n return a.orgs.name.localeCompare(b.orgs.name)\r\n }),\r\n course2groups: item.course2orgs.filter(item => item.orgs.type !== "class" && item.orgs.type !== "reelectclass").sort((a, b) => {\r\n return a.orgs.name.localeCompare(b.orgs.name)\r\n }),\r\n }\r\n })\r\n }\r\n}',
|
||||
requestAdaptor:
|
||||
"if (api.body.id === 'eq.') {\r\n api.url = api.url.replace('&id=eq.', '')\r\n}\r\nreturn {\r\n ...api\r\n}",
|
||||
},
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'form',
|
||||
id: 'u:8c0690c8c102',
|
||||
title: '表单',
|
||||
wrapWithPanel: false,
|
||||
target: '',
|
||||
reload:
|
||||
'plan?semesterSelect=$semesterSelect&courseSelect=$courseSelect',
|
||||
submitOnInit: false,
|
||||
submitOnChange: true,
|
||||
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: 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: '',
|
||||
},
|
||||
submitOnChange: true,
|
||||
visibleOn: '',
|
||||
autoComplete: '',
|
||||
mode: 'inline',
|
||||
inputClassName: 'm-l-xs',
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
id: 'u:9bdb10a2a5c7',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '查询',
|
||||
name: 'courseSelect',
|
||||
options: [],
|
||||
checkAll: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/courses?organization_id=eq.2&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.concat('(', item.code, ')'),\r\n value: item.id,\r\n }\r\n })\r\n}",
|
||||
sendOn: '',
|
||||
},
|
||||
submitOnChange: true,
|
||||
visibleOn: '',
|
||||
autoComplete: '',
|
||||
mode: 'inline',
|
||||
inputClassName: 'm-l-xs',
|
||||
clearable: true,
|
||||
labelClassName: '',
|
||||
className: 'm-l-sm',
|
||||
size: 'md',
|
||||
searchable: true,
|
||||
id: 'u:12122ee5c0ac',
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
align: 'right',
|
||||
label: '添加',
|
||||
icon: 'fa fa-plus',
|
||||
level: 'default',
|
||||
id: 'u:f303a4872364',
|
||||
visibleOn: 'this.semesterSelect === this.currentSemester',
|
||||
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: 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: '',
|
||||
},
|
||||
submitOnChange: true,
|
||||
autoComplete: '',
|
||||
mode: 'inline',
|
||||
className: '',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '添加课程',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
body: [
|
||||
{
|
||||
type: 'control',
|
||||
label: '',
|
||||
mode: 'normal',
|
||||
body: [
|
||||
{
|
||||
type: 'grid',
|
||||
columns: [
|
||||
{
|
||||
body: [
|
||||
{
|
||||
label: '编号',
|
||||
name: 'code',
|
||||
type: 'input-text',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:373b41b88f85',
|
||||
},
|
||||
],
|
||||
id: 'u:9c7fb175913e',
|
||||
},
|
||||
{
|
||||
body: [
|
||||
{
|
||||
label: '名称',
|
||||
name: 'name',
|
||||
type: 'input-text',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:6fb2647e4b89',
|
||||
},
|
||||
],
|
||||
id: 'u:661f33bd71ff',
|
||||
},
|
||||
],
|
||||
id: 'u:5e6d1055e4c9',
|
||||
},
|
||||
],
|
||||
id: 'u:c887113bf328',
|
||||
},
|
||||
{
|
||||
type: 'control',
|
||||
label: '',
|
||||
mode: 'normal',
|
||||
body: [
|
||||
{
|
||||
type: 'grid',
|
||||
columns: [
|
||||
{
|
||||
body: [
|
||||
{
|
||||
label: '',
|
||||
name: 'scoring_attendance',
|
||||
type: 'switch',
|
||||
mode: 'normal',
|
||||
value: false,
|
||||
required: true,
|
||||
option: '出勤情况是否计入评分',
|
||||
optionAtLeft: false,
|
||||
trueValue: true,
|
||||
falseValue: false,
|
||||
inputClassName: 'm-t-md m-b-sm',
|
||||
id: 'u:9efe86e2c743',
|
||||
},
|
||||
],
|
||||
id: 'u:09dd409d8a5f',
|
||||
},
|
||||
],
|
||||
id: 'u:8020115f2a97',
|
||||
},
|
||||
],
|
||||
id: 'u:71c3ed4c9568',
|
||||
},
|
||||
{
|
||||
name: 'item',
|
||||
type: 'radios',
|
||||
label: '考勤计分规则',
|
||||
options: [
|
||||
{
|
||||
label: '记零分',
|
||||
value: 'zero',
|
||||
},
|
||||
{
|
||||
label: '总数取平均',
|
||||
value: 'average',
|
||||
},
|
||||
{
|
||||
label: '逐个扣分',
|
||||
value: 'deduction',
|
||||
},
|
||||
],
|
||||
id: 'u:74ff4a000da2',
|
||||
value: 'average',
|
||||
hiddenOn: '${!scoring_attendance}',
|
||||
clearValueOnHidden: true,
|
||||
mode: 'inline',
|
||||
},
|
||||
{
|
||||
type: 'input-number',
|
||||
label: '扣分值',
|
||||
name: 'num',
|
||||
keyboard: true,
|
||||
id: 'u:ee239b61f5bb',
|
||||
step: 1,
|
||||
mode: 'inline',
|
||||
value: 10,
|
||||
min: 0,
|
||||
hiddenOn: '${item !== "deduction"}',
|
||||
clearValueOnHidden: true,
|
||||
},
|
||||
{
|
||||
type: 'attscorerule',
|
||||
name: 'att_score_rule',
|
||||
label: '考勤评分规则',
|
||||
api: 'rest/dicts?typecode=eq.014',
|
||||
visibleOn: 'this.scoring_attendance',
|
||||
mode: 'normal',
|
||||
remark: null,
|
||||
labelRemark: {
|
||||
icon: 'fa fa-question-circle',
|
||||
trigger: ['hover', 'focus'],
|
||||
className: 'Remark--warning',
|
||||
placement: 'right',
|
||||
content: '设置每个扣分项系数',
|
||||
},
|
||||
description: '',
|
||||
id: 'u:1a0b6874d2e2',
|
||||
},
|
||||
{
|
||||
label: '描述',
|
||||
type: 'input-rich-text',
|
||||
name: 'intro',
|
||||
mode: 'normal',
|
||||
options: {
|
||||
menubar: false,
|
||||
},
|
||||
id: 'u:28ff43fab6fc',
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/courses',
|
||||
data: {
|
||||
'&': '$$',
|
||||
organization_id: '${centre_id}',
|
||||
},
|
||||
dataType: 'json',
|
||||
},
|
||||
reload: 'find.courseSelect',
|
||||
id: 'u:f305be19cdbe',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:e36983b183b3',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
data: {},
|
||||
dataMergeMode: 'override',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
size: 'lg',
|
||||
id: 'u:3fd328b506be',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:851befb0af70',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:916add780f41',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
align: 'right',
|
||||
label: '导入',
|
||||
icon: 'fa fa-upload',
|
||||
level: 'warning',
|
||||
submitOnChange: true,
|
||||
visibleOn: 'this.semesterSelect === this.currentSemester',
|
||||
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: 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: '',
|
||||
},
|
||||
autoComplete: '',
|
||||
mode: 'inline',
|
||||
className: '',
|
||||
disabledOn: '',
|
||||
id: 'u:cdfcd31b8af8',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '导入历史数据',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/rpc/create_courses_from_history',
|
||||
data: null,
|
||||
dataType: 'json',
|
||||
headers: {
|
||||
Prefer: 'params=single-object',
|
||||
},
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '历史学期',
|
||||
mode: 'normal',
|
||||
name: 'history_semester',
|
||||
options: [],
|
||||
checkAll: false,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/semesters?is_open=eq.false',
|
||||
requestAdaptor: '',
|
||||
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}',
|
||||
},
|
||||
required: true,
|
||||
id: 'u:977dade3d972',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
label: '历史课程',
|
||||
mode: 'normal',
|
||||
name: 'course_list',
|
||||
required: true,
|
||||
options: [],
|
||||
checkAll: true,
|
||||
defaultCheckAll: false,
|
||||
checkAllLabel: '全选',
|
||||
multiple: true,
|
||||
joinValues: true,
|
||||
source: {
|
||||
method: 'get',
|
||||
url: 'rest/courses?semester_id=eq.${history_semester}&organization_id=eq.2',
|
||||
sendOn: 'this.history_semester',
|
||||
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}',
|
||||
},
|
||||
extractValue: false,
|
||||
id: 'u:64aa5241fbf1',
|
||||
},
|
||||
],
|
||||
id: 'u:b9168b13a3ed',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:34b04fed0264',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:f2fe245af0bf',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:c13e9c8b9584',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
perPageAvailable: [10],
|
||||
footerToolbar: [],
|
||||
perPageField: '',
|
||||
pageField: '',
|
||||
mode: 'cards',
|
||||
affixHeader: true,
|
||||
id: 'u:e36983b183b3',
|
||||
name: 'plan',
|
||||
card: {
|
||||
type: 'card',
|
||||
header: {
|
||||
title: '$name',
|
||||
subTitle: '$code',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'tabs',
|
||||
label: '',
|
||||
tabs: [
|
||||
{
|
||||
title: '上课名单',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
className: 'm-b-sm',
|
||||
id: 'u:90c6fa536042',
|
||||
mode: 'inline',
|
||||
name: 'filter',
|
||||
title: '表单',
|
||||
submitOnChange: true,
|
||||
submitOnInit: true,
|
||||
reload: 'group?class_ids=$class_ids',
|
||||
wrapWithPanel: false,
|
||||
body: [
|
||||
{
|
||||
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.${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}',
|
||||
messages: {},
|
||||
},
|
||||
withChildren: true,
|
||||
initiallyOpen: false,
|
||||
id: 'u:42ede681efa8',
|
||||
autoCheckChildren: true,
|
||||
enableNodePath: false,
|
||||
showIcon: true,
|
||||
heightAuto: true,
|
||||
virtualThreshold: false,
|
||||
},
|
||||
],
|
||||
id: 'u:f2885926e84d',
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
id: 'u:e807ff502b0a',
|
||||
lineStyle: 'solid',
|
||||
},
|
||||
],
|
||||
id: 'u:f7f436881a45',
|
||||
},
|
||||
],
|
||||
className: 'tab',
|
||||
id: 'u:1fce1665f076',
|
||||
},
|
||||
],
|
||||
actions: [],
|
||||
id: 'u:facec9ede01c',
|
||||
actionsCount: 4,
|
||||
},
|
||||
placeholder: '暂无数据',
|
||||
columnsCount: 1,
|
||||
loadDataOnce: true,
|
||||
itemClassName: '',
|
||||
matchFunc: '',
|
||||
},
|
||||
],
|
||||
id: 'u:08b8e7e7a2e5',
|
||||
headerToolbar: [
|
||||
{
|
||||
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: 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: '',
|
||||
},
|
||||
submitOnChange: true,
|
||||
visibleOn: '',
|
||||
autoComplete: '',
|
||||
mode: 'inline',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export { schema };
|
||||
1544
src/pages/schema/teaching/organization.schema.ts
Normal file
1544
src/pages/schema/teaching/organization.schema.ts
Normal file
File diff suppressed because it is too large
Load Diff
336
src/pages/schema/teaching/period.schema.ts
Normal file
336
src/pages/schema/teaching/period.schema.ts
Normal file
@@ -0,0 +1,336 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
columns: [
|
||||
{
|
||||
name: 'code',
|
||||
label: '编号',
|
||||
type: 'text',
|
||||
id: 'u:15c4fdd31360',
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: '节次',
|
||||
type: 'text',
|
||||
id: 'u:155ad2b2cd85',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '开始时间',
|
||||
id: 'u:90970bb8c69b',
|
||||
name: 'start_time',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '结束时间',
|
||||
name: 'end_time',
|
||||
id: 'u:c121345b5d56',
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
label: '操作',
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
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:d71da5ae8d5e',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '修改教学节次',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/periods/$id',
|
||||
data: {
|
||||
code: '$code',
|
||||
name: '$name',
|
||||
start_time: '$start_time',
|
||||
end_time: '$end_time',
|
||||
},
|
||||
dataType: 'json',
|
||||
},
|
||||
id: 'u:51ad7908291f',
|
||||
body: [
|
||||
{
|
||||
label: '编号',
|
||||
type: 'input-text',
|
||||
name: 'code',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:e8f8aaf50ddc',
|
||||
},
|
||||
{
|
||||
label: '节次',
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:f8e9d0c368f4',
|
||||
},
|
||||
{
|
||||
type: 'input-time',
|
||||
label: '开始时间',
|
||||
format: 'HH:mm:ss',
|
||||
name: 'start_time',
|
||||
required: true,
|
||||
timeFormat: 'HH:mm:ss',
|
||||
inputFormat: 'HH:mm:ss',
|
||||
mode: 'normal',
|
||||
id: 'u:9b49792274cd',
|
||||
},
|
||||
{
|
||||
type: 'input-time',
|
||||
label: '结束时间',
|
||||
name: 'end_time',
|
||||
format: 'HH:mm:ss',
|
||||
required: true,
|
||||
timeFormat: 'HH:mm:ss',
|
||||
inputFormat: 'HH:mm:ss',
|
||||
mode: 'normal',
|
||||
id: 'u:f32e3faee4dc',
|
||||
},
|
||||
],
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
actionType: 'reload',
|
||||
componentId: 'u:6da11c92cc84',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
dataMergeMode: 'merge',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'dialog',
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:c3be87cc2e31',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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:c0ec3a95f64c',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
outputVar: 'responseResult',
|
||||
actionType: 'ajax',
|
||||
api: {
|
||||
url: 'rest/periods/$id',
|
||||
method: 'delete',
|
||||
},
|
||||
ignoreError: false,
|
||||
},
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
componentId: 'u:6da11c92cc84',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: '-',
|
||||
width: 200,
|
||||
id: 'u:8906ce56dc14',
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/periods?order=start_time',
|
||||
data: {
|
||||
page: '$page',
|
||||
perPage: '$perPage',
|
||||
},
|
||||
requestAdaptor: '',
|
||||
},
|
||||
messages: {},
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
align: 'right',
|
||||
icon: 'fa fa-plus text-primary',
|
||||
size: 'lg',
|
||||
level: 'link',
|
||||
className: 'p-l-none',
|
||||
tooltip: '添加',
|
||||
tooltipPlacement: 'left',
|
||||
id: 'u:af1dabe3755c',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '添加教学节次',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/periods',
|
||||
data: null,
|
||||
dataType: 'json',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '编号',
|
||||
name: 'code',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:c526536d8568',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
label: '节次',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:fa59fcb75fc9',
|
||||
},
|
||||
{
|
||||
type: 'input-time',
|
||||
name: 'start_time',
|
||||
label: '开始时间',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
format: 'HH:mm:ss',
|
||||
timeFormat: 'HH:mm:ss',
|
||||
inputFormat: 'HH:mm:ss',
|
||||
id: 'u:29c058532b80',
|
||||
},
|
||||
{
|
||||
type: 'input-time',
|
||||
name: 'end_time',
|
||||
label: '结束时间',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
format: 'HH:mm:ss',
|
||||
timeFormat: 'HH:mm:ss',
|
||||
inputFormat: 'HH:mm:ss',
|
||||
id: 'u:ed17c9f2821a',
|
||||
},
|
||||
],
|
||||
id: 'u:2cac9a30a0f7',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:6da11c92cc84',
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
dataMergeMode: 'merge',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:7b13747ea64c',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:6205f65954af',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:212acf2a077a',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
footerToolbar: [
|
||||
{
|
||||
type: 'pagination',
|
||||
},
|
||||
{
|
||||
type: 'switch-per-page',
|
||||
},
|
||||
{
|
||||
type: 'statistics',
|
||||
},
|
||||
],
|
||||
name: 'per',
|
||||
bodyClassName: '',
|
||||
title: '',
|
||||
initFetch: true,
|
||||
columnsTogglable: true,
|
||||
id: 'u:6da11c92cc84',
|
||||
},
|
||||
],
|
||||
title: '',
|
||||
messages: {},
|
||||
id: 'u:49951881fbab',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
404
src/pages/schema/teaching/semester.schema.ts
Normal file
404
src/pages/schema/teaching/semester.schema.ts
Normal file
@@ -0,0 +1,404 @@
|
||||
const schema = {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'crud',
|
||||
name: 'sem',
|
||||
id: 'u:038917a084e7',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: 'rest/semesters?order=is_open.desc,since',
|
||||
data: {
|
||||
page: '$page',
|
||||
perPage: '$perPage',
|
||||
},
|
||||
requestAdaptor: '',
|
||||
adaptor: '',
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
type: 'text',
|
||||
label: '编号',
|
||||
name: 'code',
|
||||
placeholder: '-',
|
||||
id: 'u:7732470340a9',
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: '学期',
|
||||
type: 'text',
|
||||
placeholder: '-',
|
||||
id: 'u:25aaff536205',
|
||||
},
|
||||
{
|
||||
type: 'date',
|
||||
label: '开始日期',
|
||||
name: 'since',
|
||||
placeholder: '-',
|
||||
id: 'u:09f478f05013',
|
||||
},
|
||||
{
|
||||
type: 'date',
|
||||
label: '结束日期',
|
||||
name: 'to',
|
||||
placeholder: '-',
|
||||
id: 'u:8eaff00c32c8',
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
label: '当前学期',
|
||||
tpl: '<% if (data.is_open) { %>\n <span class="label label-success">是</span>\n<% } %>',
|
||||
inline: false,
|
||||
name: 'is_open',
|
||||
id: 'u:75d385b2039f',
|
||||
},
|
||||
{
|
||||
buttons: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '修改学年学期',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'patch',
|
||||
url: 'rest/semesters/$id',
|
||||
data: {
|
||||
code: '$code',
|
||||
name: '$name',
|
||||
since: '$since',
|
||||
to: '$to',
|
||||
},
|
||||
},
|
||||
body: [
|
||||
{
|
||||
label: '编号',
|
||||
type: 'input-text',
|
||||
name: 'code',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:6daa52675600',
|
||||
},
|
||||
{
|
||||
label: '学期',
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:ba2adf16abb3',
|
||||
},
|
||||
{
|
||||
type: 'input-date',
|
||||
label: '开始日期',
|
||||
format: 'YYYY-MM-DD',
|
||||
name: 'since',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:461905aad6ec',
|
||||
},
|
||||
{
|
||||
type: 'input-date',
|
||||
label: '结束日期',
|
||||
name: 'to',
|
||||
format: 'YYYY-MM-DD',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:2da01f867a44',
|
||||
},
|
||||
],
|
||||
id: 'u:c1a3d42f5f2c',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:038917a084e7',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:068175fcc455',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:7f3144efc819',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:eb87ba7f98a0',
|
||||
},
|
||||
],
|
||||
},
|
||||
level: 'link',
|
||||
icon: 'fa fa-pencil text-info',
|
||||
size: 'md',
|
||||
label: '',
|
||||
tooltip: '修改',
|
||||
tooltipPlacement: 'top',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
id: 'u:8a3d40a71b13',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
args: {
|
||||
fromCurrentDialog: true,
|
||||
},
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
id: 'u:15384435c118',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:e0d93aab4477',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:712f75a74eac',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
level: 'link',
|
||||
icon: 'fa fa-times text-danger',
|
||||
size: 'md',
|
||||
label: '',
|
||||
tooltip: '删除',
|
||||
tooltipPlacement: 'top',
|
||||
confirmText: '确认删除"${name}(${code})"?',
|
||||
iconClassName: 'pull-left',
|
||||
className: 'p-r-none p-l-none',
|
||||
id: 'u:a3c4adaf9dfc',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
outputVar: 'responseResult',
|
||||
actionType: 'ajax',
|
||||
options: {},
|
||||
api: {
|
||||
url: 'rest/semesters/$id',
|
||||
method: 'delete',
|
||||
requestAdaptor: '',
|
||||
adaptor: '',
|
||||
messages: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
componentId: 'u:038917a084e7',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'button-group',
|
||||
label: '操作',
|
||||
placeholder: '-',
|
||||
width: 200,
|
||||
id: 'u:ca40a2a8a453',
|
||||
},
|
||||
],
|
||||
messages: {},
|
||||
headerToolbar: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '',
|
||||
align: 'right',
|
||||
icon: 'fa fa-plus text-primary',
|
||||
size: 'lg',
|
||||
level: 'link',
|
||||
className: 'p-l-none',
|
||||
tooltip: '添加',
|
||||
tooltipPlacement: 'left',
|
||||
reload: 'window',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '添加学年学期',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: 'rest/semesters',
|
||||
data: null,
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
label: '编号',
|
||||
name: 'code',
|
||||
required: true,
|
||||
mode: 'normal',
|
||||
id: 'u:e91771a2c470',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
label: '学期',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
id: 'u:e04aeadb76df',
|
||||
},
|
||||
{
|
||||
type: 'input-date',
|
||||
name: 'since',
|
||||
label: '开始日期',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
format: 'YYYY-MM-DD',
|
||||
id: 'u:dcd81677a3df',
|
||||
},
|
||||
{
|
||||
type: 'input-date',
|
||||
name: 'to',
|
||||
label: '结束日期',
|
||||
mode: 'normal',
|
||||
required: true,
|
||||
format: 'YYYY-MM-DD',
|
||||
id: 'u:e66fb4a3f528',
|
||||
},
|
||||
],
|
||||
id: 'u:eaa0b0f46535',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
feat: 'Insert',
|
||||
dsType: 'api',
|
||||
onEvent: {
|
||||
submitSucc: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:038917a084e7',
|
||||
ignoreError: false,
|
||||
actionType: 'reload',
|
||||
args: {
|
||||
resetPage: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
closeOnEsc: true,
|
||||
showCloseButton: true,
|
||||
id: 'u:ccd3126a083c',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消',
|
||||
id: 'u:5d937c848ec9',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true,
|
||||
id: 'u:1a4a14d90601',
|
||||
},
|
||||
],
|
||||
},
|
||||
tpl: '内容',
|
||||
confirmText:
|
||||
'提示:新建的学期将被设置为新的“当前学期”,之前的学期将被置为“存档状态”,无法更改。请确认学期已经结束,再新建学期。',
|
||||
id: 'u:7029b79e611d',
|
||||
onEvent: {
|
||||
click: {
|
||||
weight: 0,
|
||||
actions: [
|
||||
{
|
||||
ignoreError: false,
|
||||
actionType: 'dialog',
|
||||
args: {
|
||||
fromCurrentDialog: true,
|
||||
},
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
syncLocation: false,
|
||||
perPageAvailable: [10, 20, 30, 40, 50],
|
||||
footerToolbar: [
|
||||
{
|
||||
type: 'pagination',
|
||||
},
|
||||
{
|
||||
type: 'switch-per-page',
|
||||
},
|
||||
{
|
||||
type: 'statistics',
|
||||
},
|
||||
],
|
||||
bodyClassName: '',
|
||||
},
|
||||
],
|
||||
id: 'u:58d186e17b9b',
|
||||
messages: {},
|
||||
title: '',
|
||||
bodyClassName: '',
|
||||
};
|
||||
|
||||
export { schema };
|
||||
196
src/renderer/AttScoreRule.tsx
Normal file
196
src/renderer/AttScoreRule.tsx
Normal file
@@ -0,0 +1,196 @@
|
||||
import {
|
||||
FormControlProps,
|
||||
FormItem,
|
||||
getVariable,
|
||||
isEffectiveApi,
|
||||
NumberInput,
|
||||
Payload,
|
||||
Switch,
|
||||
} from 'amis';
|
||||
import cx from 'classnames';
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* 考勤评分规则设置
|
||||
*/
|
||||
@FormItem({
|
||||
test: /(^|\/)attscorerule$/,
|
||||
name: 'attscorerule',
|
||||
})
|
||||
export class AttScoreRuleRenderer extends React.Component<
|
||||
FormControlProps,
|
||||
{ ruleItems: Array<any> }
|
||||
> {
|
||||
constructor(props: FormControlProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
ruleItems: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { data, value, env, api } = this.props;
|
||||
let att_score_rule = getVariable(data, 'att_score_rule');
|
||||
|
||||
if (isEffectiveApi(api)) {
|
||||
env.fetcher(api, data).then((payload: Payload) => {
|
||||
let items = payload.data.items.map((item: any) => {
|
||||
let ruleItem: any;
|
||||
|
||||
if (att_score_rule !== '')
|
||||
ruleItem = att_score_rule?.find(
|
||||
(x: any) => x.item === item.dictkey
|
||||
);
|
||||
else ruleItem = null;
|
||||
|
||||
return {
|
||||
item: item.dictkey,
|
||||
label: item.dictvalue,
|
||||
enable: ruleItem ? ruleItem.enable : false,
|
||||
score: ruleItem ? ruleItem.score : 0,
|
||||
ratio: ruleItem ? ruleItem.ratio : 0,
|
||||
limit: ruleItem ? ruleItem.limit : 0,
|
||||
};
|
||||
});
|
||||
|
||||
this.setState({
|
||||
ruleItems: items,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { onChange, className } = this.props;
|
||||
const { ruleItems } = this.state;
|
||||
|
||||
return (
|
||||
<div className={``}>
|
||||
{ruleItems.map((item, index) => (
|
||||
<div
|
||||
key={item.item}
|
||||
className="cxd-Form-item cxd-Form-item--horizontal"
|
||||
>
|
||||
<div
|
||||
className={cx(
|
||||
`cxd-NumberControl cxd-Form-control is-inline`,
|
||||
className
|
||||
)}
|
||||
>
|
||||
<label className="m-l cxd-Form-label cxd-Form-itemColumn--2 w-xs">
|
||||
<span>{item.label}</span>
|
||||
</label>
|
||||
<Switch
|
||||
className="m-l-sm"
|
||||
checked={item.enable}
|
||||
onChange={(value: boolean) => {
|
||||
ruleItems[index].enable = value;
|
||||
this.setState({
|
||||
ruleItems,
|
||||
});
|
||||
onChange(
|
||||
ruleItems.map((x: any) => {
|
||||
return {
|
||||
item: x.item,
|
||||
enable: x.enable,
|
||||
score: x.score,
|
||||
ratio: x.ratio,
|
||||
limit: x.limit,
|
||||
label: x.label,
|
||||
};
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<label className="m-l cxd-Form-label cxd-Form-itemColumn--2">
|
||||
<span>每次扣:</span>
|
||||
</label>
|
||||
<NumberInput
|
||||
className="m-l-sm w-xs"
|
||||
label="每次扣:"
|
||||
value={item.score}
|
||||
required={item.enable}
|
||||
step={1}
|
||||
max={100}
|
||||
onChange={(value: number) => {
|
||||
ruleItems[index].score = value;
|
||||
this.setState({
|
||||
ruleItems,
|
||||
});
|
||||
onChange(
|
||||
ruleItems.map((x: any) => {
|
||||
return {
|
||||
item: x.item,
|
||||
enable: x.enable,
|
||||
score: x.score,
|
||||
ratio: x.ratio,
|
||||
limit: x.limit,
|
||||
};
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<label className="m-l cxd-Form-label cxd-Form-itemColumn--2">
|
||||
<span>累次加扣比例:</span>
|
||||
</label>
|
||||
<NumberInput
|
||||
className="m-l-sm w-xs"
|
||||
label="累次加扣比例:"
|
||||
value={item.ratio}
|
||||
required={item.enable}
|
||||
step={1}
|
||||
max={100}
|
||||
onChange={(value: number) => {
|
||||
ruleItems[index].ratio = value;
|
||||
this.setState({
|
||||
ruleItems,
|
||||
});
|
||||
onChange(
|
||||
ruleItems.map((x: any) => {
|
||||
return {
|
||||
item: x.item,
|
||||
enable: x.enable,
|
||||
score: x.score,
|
||||
ratio: x.ratio,
|
||||
limit: x.limit,
|
||||
};
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<label className="m-l cxd-Form-label cxd-Form-itemColumn--2">
|
||||
<span>扣分上限:</span>
|
||||
</label>
|
||||
|
||||
<NumberInput
|
||||
className="m-l-sm w-xs"
|
||||
label="扣分上限:"
|
||||
value={item.limit}
|
||||
required={item.enable}
|
||||
step={1}
|
||||
max={100}
|
||||
onChange={(value: number) => {
|
||||
ruleItems[index].limit = value;
|
||||
this.setState({
|
||||
ruleItems,
|
||||
});
|
||||
onChange(
|
||||
ruleItems.map((x: any) => {
|
||||
return {
|
||||
item: x.item,
|
||||
enable: x.enable,
|
||||
score: x.score,
|
||||
ratio: x.ratio,
|
||||
limit: x.limit,
|
||||
};
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
185
src/renderer/BatchGroup.tsx
Normal file
185
src/renderer/BatchGroup.tsx
Normal file
@@ -0,0 +1,185 @@
|
||||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import { Button, FormItem, FormControlProps, getVariable } from 'amis';
|
||||
|
||||
@FormItem({
|
||||
test: /(^|\/)batch-group$/,
|
||||
name: 'batch-group',
|
||||
})
|
||||
export class Group extends React.Component<
|
||||
FormControlProps,
|
||||
{
|
||||
createdGroups: any;
|
||||
studentCount: any;
|
||||
groupsStatus: any;
|
||||
}
|
||||
> {
|
||||
constructor(props: FormControlProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
createdGroups: [],
|
||||
studentCount: 0,
|
||||
groupsStatus: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { data } = this.props;
|
||||
const students = getVariable(data, 'selectedItems');
|
||||
this.setState({ studentCount: students.length });
|
||||
}
|
||||
|
||||
group(payload) {
|
||||
const { way, students, groups } = payload;
|
||||
let user2org = [];
|
||||
let groupsStatus = [];
|
||||
|
||||
if (way === 'order') {
|
||||
const count = Math.ceil(students.length / groups.length);
|
||||
const remainder = students.length % groups.length;
|
||||
let i = 0;
|
||||
|
||||
for (let j = 0; i < students.length; ++j) {
|
||||
if (j < remainder || !remainder) {
|
||||
user2org.push({
|
||||
user_id: students
|
||||
.slice(i, i + count)
|
||||
.map(item => item.student_id),
|
||||
org: groups[j],
|
||||
});
|
||||
|
||||
i += count;
|
||||
} else {
|
||||
user2org.push({
|
||||
user_id: students
|
||||
.slice(i, i + count - 1)
|
||||
.map(item => item.student_id),
|
||||
org: groups[j],
|
||||
});
|
||||
|
||||
i += count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
user2org = user2org.flat();
|
||||
groupsStatus = this.getStatus(user2org);
|
||||
} else if (way === 'random') {
|
||||
user2org = groups.map(item => ({
|
||||
user_id: [],
|
||||
org: item,
|
||||
}));
|
||||
|
||||
for (let i = 0; i < students.length; ++i) {
|
||||
user2org[i % groups.length].user_id.push(students[i].student_id);
|
||||
}
|
||||
|
||||
groupsStatus = this.getStatus(user2org);
|
||||
}
|
||||
|
||||
return {
|
||||
user2org,
|
||||
groupsStatus
|
||||
};
|
||||
}
|
||||
|
||||
getStatus(user2org) {
|
||||
let groupsStatus = user2org.map(item => `分组 ${item.org} 已分配 ${item.user_id.length} 名学生`);
|
||||
|
||||
return groupsStatus;
|
||||
}
|
||||
|
||||
check(payload) {
|
||||
const { env } = this.props;
|
||||
const { parent, groups, way, students } = payload;
|
||||
|
||||
const { groupsStatus } = this.group({ way, students, groups });
|
||||
|
||||
env
|
||||
.fetcher({
|
||||
url: `rest/orgs?parent=eq.${parent}&type=eq.studentgroup`,
|
||||
method: 'get',
|
||||
})
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
createdGroups: res.data.items.map((item) => item.name),
|
||||
groupsStatus,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
create(payload) {
|
||||
const { env } = this.props;
|
||||
const { parent, groups, course_id, way, students } = payload;
|
||||
const newGroups = groups.map(item => ({
|
||||
name: item,
|
||||
parent: parseInt(parent),
|
||||
}));
|
||||
|
||||
const { user2org } = this.group({ way, students, groups: newGroups });
|
||||
const result = { course_id, user2org };
|
||||
|
||||
env
|
||||
.fetcher({
|
||||
url: 'rest/rpc/group',
|
||||
method: 'post',
|
||||
data: result,
|
||||
headers: {
|
||||
Prefer: 'params=single-object',
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
env.notify('success', '创建分组成功');
|
||||
})
|
||||
.catch(() => {
|
||||
env.notify('error', '创建分组失败');
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { data } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<div style={{ overflowWrap: 'break-word' }}>{`已建立分组: ${this.state.createdGroups.toString()}`}</div>
|
||||
<div>{`已选学生人数:${this.state.studentCount}`}</div>
|
||||
<div>
|
||||
{this.state.groupsStatus.map((item, index) => (
|
||||
<div key={`${item}-${index}`}>{item}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
level="primary"
|
||||
style={{ marginRight: '5px' }}
|
||||
onClick={() => {
|
||||
const parent = this.props.store.data.parent;
|
||||
const groups = this.props.store.data.groups;
|
||||
const way = this.props.store.data.way;
|
||||
const students = getVariable(data, 'selectedItems');
|
||||
|
||||
this.check({ parent, groups, way, students });
|
||||
}}
|
||||
>
|
||||
核对
|
||||
</Button>
|
||||
<Button
|
||||
level="primary"
|
||||
onClick={() => {
|
||||
const parent = this.props.store.data.parent;
|
||||
const groups = this.props.store.data.groups;
|
||||
const way = this.props.store.data.way;
|
||||
const course_id = getVariable(data, 'course_id');
|
||||
const students = getVariable(data, 'selectedItems');
|
||||
|
||||
this.create({ parent, groups, course_id, way, students });
|
||||
}}
|
||||
>
|
||||
创建
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
687
src/renderer/Calendar/Header.tsx
Normal file
687
src/renderer/Calendar/Header.tsx
Normal file
@@ -0,0 +1,687 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { ToolbarProps, View } from 'react-big-calendar';
|
||||
import { Button, getVariable, Payload, RendererProps, Select } from 'amis';
|
||||
import {
|
||||
addDays,
|
||||
differenceInDays,
|
||||
format,
|
||||
parse,
|
||||
startOfMonth,
|
||||
startOfWeek,
|
||||
} from 'date-fns';
|
||||
|
||||
export default inject('store')(
|
||||
observer(function ({
|
||||
env,
|
||||
data,
|
||||
render,
|
||||
body,
|
||||
label,
|
||||
date,
|
||||
view,
|
||||
onNavigate,
|
||||
onView,
|
||||
course,
|
||||
project,
|
||||
location,
|
||||
teacher,
|
||||
period,
|
||||
mode,
|
||||
events,
|
||||
copiedEvents,
|
||||
input,
|
||||
onOutput,
|
||||
onImport,
|
||||
onAction,
|
||||
onRotation,
|
||||
onCopy,
|
||||
onCourseChange,
|
||||
onProjectChange,
|
||||
onLocationChange,
|
||||
onTeacherChange,
|
||||
onPeriodChange,
|
||||
onModeChange,
|
||||
onEventsChange,
|
||||
onPaste,
|
||||
onSelectAll,
|
||||
onEdit,
|
||||
onDateChange,
|
||||
onDateCopy,
|
||||
}: RendererProps & ToolbarProps) {
|
||||
const [courses, setCourses] = useState([]);
|
||||
const [projects, setProjects] = useState([]);
|
||||
const [locations, setLocations] = useState([]);
|
||||
const [teachers, setTeachers] = useState([]);
|
||||
const [periods, setPeriods] = useState([]);
|
||||
|
||||
let centre_id = getVariable(data, 'centre_id');
|
||||
let user = getVariable(data, 'user');
|
||||
let semester_id = getVariable(data, 'currentSemester');
|
||||
|
||||
useEffect(() => {
|
||||
env
|
||||
.fetcher(
|
||||
`rest/courses?organization_id=eq.${centre_id}&semester_id=eq.${semester_id}`
|
||||
)
|
||||
.then((data: any) => {
|
||||
setCourses(
|
||||
data.data.items.map((item: any) => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// env
|
||||
// .fetcher(
|
||||
// `rest/projects?organization_id=eq.${centre_id}&semester_id=eq.${semester_id}`
|
||||
// )
|
||||
// .then((data: any) => {
|
||||
// setProjects(
|
||||
// data.data.items.map((item: any) => {
|
||||
// return {
|
||||
// label: item.name,
|
||||
// value: item.id,
|
||||
// };
|
||||
// })
|
||||
// );
|
||||
// });
|
||||
|
||||
env.fetcher(`rest/periods?order=start_time,code`).then((data: any) => {
|
||||
setPeriods(
|
||||
data.data.items.map((item: any) => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (course) {
|
||||
env
|
||||
.fetcher(
|
||||
`rest/course2projects?select=projects!inner(id,name)&projects.organization_id=eq.${centre_id}&projects.semester_id=eq.${semester_id}&projects.free_schedule=eq.false&course_id=eq.${course}`
|
||||
)
|
||||
.then((data: any) => {
|
||||
setProjects(
|
||||
data.data.items.map((item: any) => {
|
||||
return {
|
||||
label: item.projects.name,
|
||||
value: item.projects.id,
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
} else {
|
||||
env
|
||||
.fetcher(
|
||||
`rest/projects?organization_id=eq.${centre_id}&semester_id=eq.${semester_id}&free_schedule=eq.false`
|
||||
)
|
||||
.then((data: any) => {
|
||||
setProjects(
|
||||
data.data.items.map((item: any) => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
}, [course]);
|
||||
|
||||
useEffect(() => {
|
||||
if (project) {
|
||||
env
|
||||
.fetcher({
|
||||
url: `rest/rpc/project_resources`,
|
||||
method: 'post',
|
||||
data: {
|
||||
project_id: project,
|
||||
type: 'location',
|
||||
centre_id: centre_id,
|
||||
},
|
||||
})
|
||||
.then((data: any) => {
|
||||
setLocations(
|
||||
data.data.items.map((item: any) => {
|
||||
return {
|
||||
label: item.label,
|
||||
value: item.value,
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
env
|
||||
.fetcher({
|
||||
method: 'post',
|
||||
url: `rest/rpc/project_resources`,
|
||||
data: {
|
||||
project_id: project,
|
||||
type: 'teacher',
|
||||
centre_id: centre_id,
|
||||
},
|
||||
})
|
||||
.then((data: any) => {
|
||||
setTeachers(
|
||||
data.data.items.map((item: any) => {
|
||||
return {
|
||||
label: item.label,
|
||||
value: item.value,
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
} else {
|
||||
env
|
||||
.fetcher(
|
||||
`rest/locations?organization_id=eq.${centre_id}&is_valid=eq.true`
|
||||
)
|
||||
.then((data: any) => {
|
||||
setLocations(
|
||||
data.data.items.map((item: any) => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
env
|
||||
.fetcher(`rest/users_ex?org_id=eq.${centre_id}&role=eq.teacher`)
|
||||
.then((data: any) => {
|
||||
setTeachers(
|
||||
data.data.items.map((item: any) => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
}, [project]);
|
||||
|
||||
const changeGroup = (events: Array<any>) => {
|
||||
env
|
||||
.fetcher({
|
||||
method: 'patch',
|
||||
url: `rest/schedules?id=in.(${events.join(',')})`,
|
||||
data: {
|
||||
group: Date.now().toString(),
|
||||
},
|
||||
})
|
||||
.then((payload: Payload) => {
|
||||
onEventsChange();
|
||||
});
|
||||
};
|
||||
const handleDelete = () => {
|
||||
env.confirm('确认删除选择的场次?', '提示').then((value: boolean) => {
|
||||
if (value) {
|
||||
let ids = [];
|
||||
events.forEach((x) => {
|
||||
if (x.isSelected) {
|
||||
ids.push(x.id);
|
||||
}
|
||||
});
|
||||
env
|
||||
.fetcher({
|
||||
url: `rest/rpc/delete_schedules`,
|
||||
method: 'post',
|
||||
data: {
|
||||
schedules: ids,
|
||||
},
|
||||
dataType: 'json',
|
||||
headers: {
|
||||
Prefer: 'params=single-object',
|
||||
},
|
||||
})
|
||||
.then((payload: Payload) => {
|
||||
onEventsChange();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleGroup = () => {
|
||||
let selectedEvents = events.filter((x: any) => x.isSelected);
|
||||
let group = selectedEvents.filter((x: any) => x.group !== null);
|
||||
let selectedEventId = selectedEvents.map((x: any) => x.id);
|
||||
|
||||
if (selectedEvents.length <= 1) {
|
||||
env.notify('warning', '请选择多个场次进行分组');
|
||||
} else {
|
||||
if (group.length > 0) {
|
||||
env
|
||||
.confirm('选择的场次中存在已分组场次,是否合并分组?', '提示')
|
||||
.then((value: boolean) => {
|
||||
if (value) {
|
||||
env
|
||||
.fetcher({
|
||||
url: `rest/schedules?group=in.(${group
|
||||
.map((x: any) => group.id)
|
||||
.join(',')})`,
|
||||
})
|
||||
.then((payload: Payload) => {
|
||||
let exps = selectedEvents
|
||||
.map((x: any) => x.project.id)
|
||||
.concat(
|
||||
payload.data.items
|
||||
.filter(
|
||||
(x: any) =>
|
||||
selectedEvents.findIndex(
|
||||
(y: any) => y.id === x.id
|
||||
) === -1
|
||||
)
|
||||
.map((x: any) => x.project_id)
|
||||
);
|
||||
let obj: any = {};
|
||||
for (let i = 0; i < exps.length; i++) {
|
||||
obj[exps[i]] = i;
|
||||
}
|
||||
if (exps.length !== Object.keys(obj).length) {
|
||||
env.notify('error', '关联场次不能有相同的实验');
|
||||
return;
|
||||
}
|
||||
changeGroup(
|
||||
selectedEventId.concat(
|
||||
payload.data.items.map((x: any) => x.id)
|
||||
)
|
||||
);
|
||||
});
|
||||
} else {
|
||||
changeGroup(selectedEventId);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
changeGroup(selectedEventId);
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleUnGroup = () => {
|
||||
let selectedEvents = events.filter((x: any) => x.isSelected);
|
||||
let selectedEventId = selectedEvents.map((x: any) => x.id);
|
||||
|
||||
if (selectedEvents.length <= 1) {
|
||||
env.notify('warning', '未选择要取消分组的场次');
|
||||
} else {
|
||||
env
|
||||
.confirm('是否取消选择的场次中存在的分组?', '提示')
|
||||
.then((value: boolean) => {
|
||||
if (value) {
|
||||
env
|
||||
.fetcher({
|
||||
url: `rest/schedules?id=in.(${selectedEventId})`,
|
||||
method: 'patch',
|
||||
data: {
|
||||
group: null,
|
||||
},
|
||||
})
|
||||
.then((payload: Payload) => {
|
||||
onEventsChange();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="hbox m-b-xs" style={{ height: 'auto' }}>
|
||||
<div
|
||||
className="col wrapper-sm b-r w-xxl "
|
||||
style={{ verticalAlign: 'middle' }}
|
||||
>
|
||||
<Button
|
||||
className="col"
|
||||
level="link"
|
||||
onClick={(event: any) => {
|
||||
event.preventDefault();
|
||||
onNavigate('PREV');
|
||||
}}
|
||||
>
|
||||
<i className="fa fa-chevron-left" aria-hidden="true"></i>
|
||||
</Button>
|
||||
<span className="m-l m-r inline-block w text-center">{`${label}`}</span>
|
||||
<Button
|
||||
className="col"
|
||||
level="link"
|
||||
onClick={(event: any) => {
|
||||
event.preventDefault();
|
||||
onNavigate('NEXT');
|
||||
}}
|
||||
>
|
||||
<i className="fa fa-chevron-right" aria-hidden="true"></i>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col wrapper-sm lter">
|
||||
<div
|
||||
className=" center-block btn-group text-right "
|
||||
role="group"
|
||||
aria-label="..."
|
||||
>
|
||||
<Select
|
||||
className={`w m-l-sm`}
|
||||
options={courses}
|
||||
clearable={true}
|
||||
searchable={true}
|
||||
placeholder={`请选择课程`}
|
||||
onChange={onCourseChange}
|
||||
value={course}
|
||||
/>
|
||||
<Select
|
||||
className={`w m-l-sm`}
|
||||
options={projects}
|
||||
clearable={true}
|
||||
searchable={true}
|
||||
placeholder={`请选择实验`}
|
||||
onChange={onProjectChange}
|
||||
value={project}
|
||||
resetValue={null}
|
||||
disabled={false}
|
||||
/>
|
||||
<Select
|
||||
className={`w-sm m-l-sm`}
|
||||
options={locations}
|
||||
clearable={true}
|
||||
searchable={true}
|
||||
placeholder={`请选择场地`}
|
||||
onChange={onLocationChange}
|
||||
value={location}
|
||||
/>
|
||||
<Select
|
||||
className={`w-sm m-l-sm`}
|
||||
options={teachers}
|
||||
clearable={true}
|
||||
searchable={true}
|
||||
placeholder={`请选择教师`}
|
||||
onChange={onTeacherChange}
|
||||
value={teacher}
|
||||
/>
|
||||
<Select
|
||||
className={`w-sm m-l-sm`}
|
||||
options={periods}
|
||||
clearable={true}
|
||||
searchable={true}
|
||||
placeholder={`请选择节次`}
|
||||
onChange={onPeriodChange}
|
||||
value={period}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="center-block m-t pull-right">
|
||||
{user?.role === 'admin' ||
|
||||
user?.role === 'centreadmin' ||
|
||||
user?.role === 'dev' ||
|
||||
user?.role === 'teacher' ? (
|
||||
<div
|
||||
className="btn-group inline-block m-l-sm "
|
||||
role="group"
|
||||
aria-label="..."
|
||||
>
|
||||
<Button disabled={mode === 'select'} onClick={onRotation}>
|
||||
轮转
|
||||
</Button>
|
||||
<Button
|
||||
disabled={mode === 'select'}
|
||||
onClick={() => {
|
||||
const myEvents = events.filter((item) => item.isMine);
|
||||
|
||||
if (!myEvents.length) {
|
||||
env.notify('warning', '当前视图没有您负责的场次!');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let url;
|
||||
|
||||
if (user?.role === 'teacher') {
|
||||
url = `rest/schedules?id=in.(${myEvents
|
||||
.map((item: any) => item.id)
|
||||
.join(',')})`;
|
||||
} else {
|
||||
url = `rest/schedules?id=in.(${events
|
||||
.map((item: any) => item.id)
|
||||
.join(',')})`;
|
||||
}
|
||||
|
||||
env
|
||||
.confirm('确认发布当前视图下所有您负责的场次?')
|
||||
.then((value: boolean) => {
|
||||
if (value) {
|
||||
env
|
||||
.fetcher({
|
||||
url: url,
|
||||
method: 'patch',
|
||||
data: {
|
||||
is_publish: true,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
onEventsChange();
|
||||
});
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
发布
|
||||
</Button>
|
||||
<Button
|
||||
disabled={mode === 'select'}
|
||||
onClick={() => {
|
||||
const myEvents = events.filter((item) => item.isMine);
|
||||
|
||||
if (!myEvents.length) {
|
||||
env.notify('warning', '当前视图没有您负责的场次!');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let url;
|
||||
|
||||
if (user?.role === 'teacher') {
|
||||
url = `rest/schedules?id=in.(${myEvents
|
||||
.map((item: any) => item.id)
|
||||
.join(',')})`;
|
||||
} else {
|
||||
url = `rest/schedules?id=in.(${events
|
||||
.map((item: any) => item.id)
|
||||
.join(',')})`;
|
||||
}
|
||||
|
||||
env
|
||||
.confirm('确认将当前视图下所有您负责的场次设置为草稿?')
|
||||
.then((value: boolean) => {
|
||||
if (value) {
|
||||
env
|
||||
.fetcher({
|
||||
url: url,
|
||||
method: 'patch',
|
||||
data: {
|
||||
is_publish: false,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
onEventsChange();
|
||||
});
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
草稿
|
||||
</Button>
|
||||
<Button disabled={mode === 'event'} onClick={onCopy}>
|
||||
复制
|
||||
</Button>
|
||||
<Button
|
||||
disabled={mode === 'event' || copiedEvents.length === 0}
|
||||
onClick={() => {
|
||||
if (copiedEvents.length === 0) {
|
||||
env.notify('warning', '没有复制场次');
|
||||
return;
|
||||
}
|
||||
|
||||
env
|
||||
.confirm('是否保留场次班级?', '粘贴场次')
|
||||
.then((value: boolean) => {
|
||||
let schedules = copiedEvents.map((schedule: any) => {
|
||||
let scheduleDate = parse(
|
||||
schedule.date,
|
||||
'yyyy-MM-dd',
|
||||
new Date(),
|
||||
{
|
||||
weekStartsOn: 1,
|
||||
}
|
||||
);
|
||||
let targetDate = schedule.date;
|
||||
if (view === 'day') {
|
||||
targetDate = date;
|
||||
}
|
||||
if (view === 'week') {
|
||||
targetDate = addDays(
|
||||
scheduleDate,
|
||||
differenceInDays(
|
||||
startOfWeek(date, {
|
||||
weekStartsOn: 1,
|
||||
}),
|
||||
startOfWeek(scheduleDate, {
|
||||
weekStartsOn: 1,
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
if (view === 'month') {
|
||||
targetDate = addDays(
|
||||
scheduleDate,
|
||||
differenceInDays(
|
||||
startOfMonth(date),
|
||||
startOfMonth(scheduleDate)
|
||||
)
|
||||
);
|
||||
}
|
||||
return {
|
||||
is_reserved: schedule.is_reserved,
|
||||
is_scheduled: schedule.is_scheduled,
|
||||
date: format(targetDate, 'yyyy-MM-dd'),
|
||||
period_id: schedule.period_id,
|
||||
location_id: schedule.location_id,
|
||||
course_id: schedule.course_id,
|
||||
project_id: schedule.project_id,
|
||||
teacher_id: schedule.teacher_id,
|
||||
organization_id: schedule.organization_id,
|
||||
class_list: value ? schedule.class_list : [],
|
||||
max_student_number: schedule.max_student_number,
|
||||
min_student_number: schedule.min_student_number,
|
||||
max_group_student_number:
|
||||
schedule.max_group_student_number,
|
||||
};
|
||||
});
|
||||
if (schedules.length > 0) {
|
||||
env
|
||||
.fetcher({
|
||||
url: `rest/schedules`,
|
||||
method: 'post',
|
||||
data: schedules,
|
||||
})
|
||||
.then(() => {
|
||||
onEventsChange();
|
||||
onPaste();
|
||||
});
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
粘贴
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (mode === 'event') {
|
||||
onModeChange('select');
|
||||
} else {
|
||||
onModeChange('event');
|
||||
}
|
||||
}}
|
||||
>
|
||||
{mode === 'event' ? '选择' : '取消'}
|
||||
</Button>
|
||||
<Button disabled={mode === 'event'} onClick={onSelectAll}>
|
||||
全选
|
||||
</Button>
|
||||
<Button disabled={mode === 'event'} onClick={onEdit}>
|
||||
编辑
|
||||
</Button>
|
||||
<Button disabled={mode === 'event'} onClick={onDateChange}>
|
||||
移动
|
||||
</Button>
|
||||
<Button disabled={mode === 'event'} onClick={onDateCopy}>
|
||||
复制
|
||||
</Button>
|
||||
<Button disabled={mode === 'event'} onClick={handleGroup}>
|
||||
分组
|
||||
</Button>
|
||||
<Button disabled={mode === 'event'} onClick={handleUnGroup}>
|
||||
取消分组
|
||||
</Button>
|
||||
<Button
|
||||
level="danger"
|
||||
disabled={mode === 'event'}
|
||||
onClick={handleDelete}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
<Button level="primary" onClick={onOutput}>
|
||||
导出
|
||||
</Button>
|
||||
<Button
|
||||
level="warning"
|
||||
icon="fa fa-upload"
|
||||
onClick={onImport}
|
||||
>
|
||||
导入
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<div
|
||||
className="btn-group inline-block m-l-sm "
|
||||
role="group"
|
||||
aria-label="..."
|
||||
>
|
||||
<Button
|
||||
onClick={() => {
|
||||
onView('month');
|
||||
}}
|
||||
>
|
||||
月视图
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
onView('week');
|
||||
}}
|
||||
>
|
||||
周视图
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
onView('day');
|
||||
}}
|
||||
>
|
||||
天视图
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
})
|
||||
);
|
||||
12
src/renderer/Calendar/groupColors.ts
Normal file
12
src/renderer/Calendar/groupColors.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export default [
|
||||
'rgb(0, 153, 102)',
|
||||
'rgb(51, 153, 51)',
|
||||
'rgb(255, 204, 204)',
|
||||
'rgb(102, 102, 153)',
|
||||
'rgb(102, 204, 204)',
|
||||
'rgb(204, 51, 51)',
|
||||
'rgb(255, 153, 102)',
|
||||
'rgb(102, 51, 102)',
|
||||
'rgb(204, 153, 51)',
|
||||
'rgb(255, 204, 51)',
|
||||
];
|
||||
830
src/renderer/Calendar/index.tsx
Normal file
830
src/renderer/Calendar/index.tsx
Normal file
@@ -0,0 +1,830 @@
|
||||
import { getVariable, Renderer, RendererProps, ScopedContext } from 'amis';
|
||||
import {
|
||||
endOfMonth,
|
||||
endOfWeek,
|
||||
format,
|
||||
getDay,
|
||||
isAfter,
|
||||
isBefore,
|
||||
parse,
|
||||
startOfMonth,
|
||||
startOfWeek,
|
||||
} from 'date-fns';
|
||||
import { zhCN } from 'date-fns/locale';
|
||||
import React from 'react';
|
||||
import { Calendar, dateFnsLocalizer, View } from 'react-big-calendar';
|
||||
import 'react-big-calendar/lib/css/react-big-calendar.css';
|
||||
import XLSX from 'xlsx';
|
||||
import groupColors from './groupColors';
|
||||
import Header from './Header';
|
||||
|
||||
const locales = { zh: zhCN };
|
||||
const localizer = dateFnsLocalizer({
|
||||
format,
|
||||
parse,
|
||||
startOfWeek,
|
||||
getDay,
|
||||
locales,
|
||||
});
|
||||
@Renderer({
|
||||
test: /(^|\/)calendar\-renderer$/,
|
||||
name: 'calendar-renderer',
|
||||
})
|
||||
export class CalendarRenderer extends React.Component<
|
||||
RendererProps,
|
||||
{
|
||||
min: Date;
|
||||
max: Date;
|
||||
date: Date;
|
||||
start: Date;
|
||||
end: Date;
|
||||
mode: string;
|
||||
view: View;
|
||||
events: Array<any>;
|
||||
copiedEvents: Array<any>;
|
||||
periods: Array<any>;
|
||||
groups: Array<any>;
|
||||
output: Array<any>;
|
||||
course?: number;
|
||||
project?: number;
|
||||
location?: number;
|
||||
teacher?: number;
|
||||
period?: number;
|
||||
courses: Array<any>;
|
||||
defaultLessonStudentMin?: number;
|
||||
defaultLessonStudentMax?: number;
|
||||
defaultGroupStudentMax?: number;
|
||||
showDraftToTeacher: boolean;
|
||||
}
|
||||
> {
|
||||
static defaultProps = {
|
||||
items: [],
|
||||
};
|
||||
|
||||
static contextType = ScopedContext;
|
||||
|
||||
constructor(props: RendererProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
min: parse('08:00:00', 'kk:mm:ss', new Date()),
|
||||
max: parse('22:00:00', 'kk:mm:ss', new Date()),
|
||||
date: new Date(),
|
||||
start: new Date(),
|
||||
end: new Date(),
|
||||
mode: 'event',
|
||||
view: 'week',
|
||||
events: [],
|
||||
copiedEvents: [],
|
||||
periods: [],
|
||||
groups: [],
|
||||
output: [],
|
||||
course: undefined,
|
||||
project: undefined,
|
||||
location: undefined,
|
||||
teacher: undefined,
|
||||
period: undefined,
|
||||
courses: undefined,
|
||||
defaultLessonStudentMin: 1,
|
||||
defaultLessonStudentMax: 32,
|
||||
defaultGroupStudentMax: 5,
|
||||
showDraftToTeacher: false,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchSchedules(start: string, end: string) {
|
||||
const currentSemester = getVariable(this.props.data, 'currentSemester');
|
||||
const user = getVariable(this.props.data, 'user');
|
||||
|
||||
let url = `rest/schedules?select=*,course:courses!schedule_course_id_fkey(id,name,course2teachers(*)),project:projects!schedule_project_id_fkey(id,name), period:schedule_period_id_fkey(id,start_time,end_time),location:locations!schedule_location_id_fkey(id,name), teacher:users!schedule_teacher_id_fkey(id,code,name),reserved_classes,reserved_classes_code&date=gte.${start}&date=lte.${end}&semester_id=eq.${currentSemester}&course.course2teachers.teacher_id=eq.${user.id}`;
|
||||
|
||||
if (
|
||||
user?.role === 'teacher' &&
|
||||
!this.state.courses.length &&
|
||||
!this.state.showDraftToTeacher
|
||||
) {
|
||||
url += `&is_publish=eq.true`;
|
||||
}
|
||||
|
||||
if (this.state.course) {
|
||||
url += `&course_id=eq.${this.state.course}`;
|
||||
}
|
||||
if (this.state.project) {
|
||||
url += `&project_id=eq.${this.state.project}`;
|
||||
}
|
||||
if (this.state.location) {
|
||||
url += `&location_id=eq.${this.state.location}`;
|
||||
}
|
||||
if (this.state.teacher) {
|
||||
url += `&teacher_id=eq.${this.state.teacher}`;
|
||||
}
|
||||
if (this.state.period) {
|
||||
url += `&period_id=eq.${this.state.period}`;
|
||||
}
|
||||
const { data } = await this.props.env.fetcher(url);
|
||||
|
||||
const events = data.items.map((item: any) => {
|
||||
if (item.is_reserved) {
|
||||
return {
|
||||
title: '(保留)'.concat(item.project.name),
|
||||
start: parse(
|
||||
`${item.date} ${item.period.start_time}`,
|
||||
'yyyy-MM-dd kk:mm:ss',
|
||||
new Date()
|
||||
),
|
||||
end: parse(
|
||||
`${item.date} ${item.period.end_time}`,
|
||||
'yyyy-MM-dd kk:mm:ss',
|
||||
new Date()
|
||||
),
|
||||
allDay: false,
|
||||
isSelected: false,
|
||||
isMine: !!(
|
||||
(user?.role === 'teacher' &&
|
||||
item.course &&
|
||||
item.course.course2teachers.length) ||
|
||||
user?.role === 'admin' ||
|
||||
user?.role === 'centreadmin' ||
|
||||
user?.role === 'dev'
|
||||
),
|
||||
...item,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
title: item.project.name,
|
||||
start: parse(
|
||||
`${item.date} ${item.period.start_time}`,
|
||||
'yyyy-MM-dd kk:mm:ss',
|
||||
new Date()
|
||||
),
|
||||
end: parse(
|
||||
`${item.date} ${item.period.end_time}`,
|
||||
'yyyy-MM-dd kk:mm:ss',
|
||||
new Date()
|
||||
),
|
||||
allDay: false,
|
||||
isSelected: false,
|
||||
isMine: !!(
|
||||
(user?.role === 'teacher' &&
|
||||
item.course &&
|
||||
item.course.course2teachers.length) ||
|
||||
user?.role === 'admin' ||
|
||||
user?.role === 'centreadmin' ||
|
||||
user?.role === 'dev'
|
||||
),
|
||||
...item,
|
||||
};
|
||||
}
|
||||
});
|
||||
this.setState({
|
||||
start: parse(`${start}`, 'yyyy-MM-dd', new Date()),
|
||||
end: parse(`${end}`, 'yyyy-MM-dd', new Date()),
|
||||
events: events,
|
||||
groups: events.map((x: any) => x.group).sort(),
|
||||
});
|
||||
}
|
||||
|
||||
async fetchCourses(start, end) {
|
||||
const user = getVariable(this.props.data, 'user');
|
||||
const currentSemester = getVariable(this.props.data, 'currentSemester');
|
||||
const centre_id = getVariable(this.props.data, 'centre_id');
|
||||
|
||||
let url = `rest/courses?select=*,course2teachers!inner(*)&semester_id=eq.${currentSemester}&organization_id=eq.${centre_id}&course2teachers.in_charge=is.true&course2teachers.teacher_id=eq.${user.id}`;
|
||||
|
||||
const { data } = await this.props.env.fetcher(url);
|
||||
|
||||
this.setState({ courses: data.items });
|
||||
|
||||
if (start) this.fetchSchedules(start, end);
|
||||
}
|
||||
|
||||
async fetchOutput() {
|
||||
const currentSemester = getVariable(this.props.data, 'currentSemester');
|
||||
const user = getVariable(this.props.data, 'user');
|
||||
|
||||
let url = `rest/schedules?select=*,course:courses!schedule_course_id_fkey(id,code,name),project:projects!schedule_project_id_fkey(id,code,name), period:schedule_period_id_fkey(id,code,name,start_time,end_time),location:locations!schedule_location_id_fkey(id,code,name), teams(id,code,name),teacher:users!schedule_teacher_id_fkey(id,code,name),reserved_classes,reserved_classes_code&semester_id=eq.${currentSemester}&order=date`;
|
||||
|
||||
if (
|
||||
user?.role === 'teacher' &&
|
||||
!this.state.courses.length &&
|
||||
!this.state.showDraftToTeacher
|
||||
) {
|
||||
url += `&is_publish=eq.true`;
|
||||
}
|
||||
|
||||
if (this.state.course) {
|
||||
url += `&course_id=eq.${this.state.course}`;
|
||||
}
|
||||
if (this.state.project) {
|
||||
url += `&project_id=eq.${this.state.project}`;
|
||||
}
|
||||
if (this.state.location) {
|
||||
url += `&location_id=eq.${this.state.location}`;
|
||||
}
|
||||
if (this.state.teacher) {
|
||||
url += `&teacher_id=eq.${this.state.teacher}`;
|
||||
}
|
||||
if (this.state.period) {
|
||||
url += `&period_id=eq.${this.state.period}`;
|
||||
}
|
||||
const { data } = await this.props.env.fetcher(url);
|
||||
|
||||
this.setState({ output: data.items });
|
||||
|
||||
let workbook = XLSX.utils.book_new();
|
||||
let tempSheet: Array<any> = [
|
||||
[
|
||||
'*日期',
|
||||
'*节次编号',
|
||||
'节次',
|
||||
'*课程编号',
|
||||
'课程',
|
||||
'*实验编号',
|
||||
'实验',
|
||||
'*场地编号',
|
||||
'场地',
|
||||
'团队编号',
|
||||
'团队',
|
||||
'教师账号',
|
||||
'教师',
|
||||
'*最大人数',
|
||||
'*最小人数',
|
||||
'是否自动排课',
|
||||
'*是否发布',
|
||||
'*是否保留场次',
|
||||
'班级编号',
|
||||
'班级',
|
||||
],
|
||||
];
|
||||
this.state.output.forEach((item: any) => {
|
||||
tempSheet.push([
|
||||
{ t: 's', v: item.date },
|
||||
{ t: 's', v: item.period.code },
|
||||
{ t: 's', v: item.period.name },
|
||||
{ t: 's', v: item.course ? item.course.code : '' },
|
||||
{ t: 's', v: item.course ? item.course.name : '' },
|
||||
{ t: 's', v: item.project.code },
|
||||
{ t: 's', v: item.project.name },
|
||||
{ t: 's', v: item.location.code },
|
||||
{ t: 's', v: item.location.name },
|
||||
{ t: 's', v: item.teams ? item.teams.code : '' },
|
||||
{ t: 's', v: item.teams ? item.teams.name : '' },
|
||||
{ t: 's', v: item.teacher ? item.teacher.code : '' },
|
||||
{ t: 's', v: item.teacher ? item.teacher.name : '' },
|
||||
{ t: 's', v: item.max_student_number },
|
||||
{ t: 's', v: item.min_student_number },
|
||||
{ t: 'b', v: item.is_scheduled },
|
||||
{ t: 'b', v: item.is_publish },
|
||||
{ t: 'b', v: item.is_reserved },
|
||||
{
|
||||
t: 's',
|
||||
v: item.reserved_classes_code ? item.reserved_classes_code : '',
|
||||
},
|
||||
{ t: 's', v: item.reserved_classes ? item.reserved_classes : '' },
|
||||
]);
|
||||
});
|
||||
|
||||
let worksheet = XLSX.utils.aoa_to_sheet(tempSheet);
|
||||
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'sheet1');
|
||||
|
||||
XLSX.writeFile(workbook, '开放选课.xlsx');
|
||||
}
|
||||
|
||||
async handleViewChange(date: Date, view: View) {
|
||||
this.fetchCourses(
|
||||
format(this.state.start, 'yyyy-MM-dd'),
|
||||
format(this.state.end, 'yyyy-MM-dd')
|
||||
);
|
||||
}
|
||||
async componentDidMount() {
|
||||
const { env, data } = this.props;
|
||||
const user = getVariable(data, 'user');
|
||||
const payload = await env.fetcher(`rest/config?key=eq.system`);
|
||||
const configData =
|
||||
payload.data.items && payload.data.items.length > 0
|
||||
? payload.data.items[0].value
|
||||
: undefined;
|
||||
|
||||
const { data: periodsData } = await env.fetcher(
|
||||
'rest/periods?order=start_time.asc'
|
||||
);
|
||||
let start_time =
|
||||
periodsData && periodsData.items.length > 0
|
||||
? periodsData.items[0].start_time
|
||||
: '08:00:00';
|
||||
let end_time =
|
||||
periodsData && periodsData.items.length > 0
|
||||
? periodsData.items[periodsData.items.length - 1].end_time
|
||||
: '20:00:00';
|
||||
|
||||
this.setState({
|
||||
teacher: user?.role === 'teacher' ? user?.id : undefined,
|
||||
periods: periodsData.items,
|
||||
min: parse(start_time, 'kk:mm:ss', new Date()),
|
||||
max: parse(end_time, 'kk:mm:ss', new Date()),
|
||||
defaultLessonStudentMin: configData.defaultLessonStudentMin,
|
||||
defaultLessonStudentMax: configData.defaultLessonStudentMax,
|
||||
defaultGroupStudentMax: configData.defaultGroupStudentMax,
|
||||
showDraftToTeacher: configData.showDraftToTeacher,
|
||||
});
|
||||
this.fetchCourses(null, null);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: any, prevState: any, snapshot: any) {
|
||||
let start = this.state.date;
|
||||
let end = this.state.date;
|
||||
if (this.state.view === 'week') {
|
||||
start = startOfWeek(this.state.date, { weekStartsOn: 1 });
|
||||
end = endOfWeek(this.state.date, { weekStartsOn: 1 });
|
||||
}
|
||||
if (this.state.view === 'month') {
|
||||
start = startOfMonth(this.state.date);
|
||||
end = endOfMonth(this.state.date);
|
||||
}
|
||||
|
||||
let format_start = format(start, 'yyyy-MM-dd');
|
||||
let format_end = format(end, 'yyyy-MM-dd');
|
||||
let prev_start = format(prevState.start, 'yyyy-MM-dd');
|
||||
let prev_end = format(prevState.end, 'yyyy-MM-dd');
|
||||
|
||||
if (format_start !== prev_start || format_end !== prev_end) {
|
||||
this.fetchCourses(format_start, format_end);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const scoped = this.context;
|
||||
scoped.registerComponent(this);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const scoped = this.context;
|
||||
scoped.unRegisterComponent(this);
|
||||
}
|
||||
|
||||
// 增删改查后触发
|
||||
reload() {
|
||||
this.handleViewChange(this.state.date, this.state.view);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { env, data } = this.props;
|
||||
const { date, view, mode, events, copiedEvents, groups } = this.state;
|
||||
const user = getVariable(data, 'user');
|
||||
|
||||
let filterEvents = events;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Calendar
|
||||
// className={classes.calendar}
|
||||
selectable
|
||||
localizer={localizer}
|
||||
culture="zh"
|
||||
events={filterEvents}
|
||||
defaultView={'week'}
|
||||
view={view}
|
||||
date={date}
|
||||
startAccessor="start"
|
||||
endAccessor="end"
|
||||
tooltipAccessor={(event) => {
|
||||
let tip: any;
|
||||
|
||||
if (event.is_reserved) {
|
||||
tip = `\r
|
||||
实验:${event.title}\r
|
||||
地点:${event.location.name}\r
|
||||
教师:${event.teacher?.name ? event.teacher.name : '未指定'}
|
||||
保留班级:${event.reserved_classes}`;
|
||||
} else {
|
||||
tip = `\r
|
||||
实验:${event.title}\r
|
||||
地点:${event.location.name}\r
|
||||
教师:${event.teacher?.name ? event.teacher.name : '未指定'}`;
|
||||
}
|
||||
return tip;
|
||||
}}
|
||||
eventPropGetter={({ group, is_publish, isSelected }) => {
|
||||
let style: any = Object.assign({}, {});
|
||||
|
||||
if (group) {
|
||||
if (groups.indexOf(group) !== -1) {
|
||||
style.backgroundColor = groupColors[groups.indexOf(group)];
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_publish) {
|
||||
style.border = 'dashed';
|
||||
}
|
||||
|
||||
if (isSelected) {
|
||||
style.opacity = '.5';
|
||||
}
|
||||
|
||||
return {
|
||||
style: style,
|
||||
};
|
||||
}}
|
||||
step={30}
|
||||
min={this.state.min}
|
||||
max={this.state.max}
|
||||
showMultiDayTimes
|
||||
components={{
|
||||
toolbar: (props) => {
|
||||
return (
|
||||
<Header
|
||||
{...props}
|
||||
{...this.props}
|
||||
view={this.state.view}
|
||||
course={this.state.course}
|
||||
project={this.state.project}
|
||||
location={this.state.location}
|
||||
teacher={this.state.teacher}
|
||||
period={this.state.period}
|
||||
mode={this.state.mode}
|
||||
events={filterEvents}
|
||||
copiedEvents={this.state.copiedEvents}
|
||||
onOutput={() => {
|
||||
this.fetchOutput();
|
||||
}}
|
||||
onRotation={() => {
|
||||
const { rotation } = this.props;
|
||||
|
||||
if (user?.role === 'teacher')
|
||||
rotation.dialog.actions[0].dialog.body[0].body[2].body[0].source.url =
|
||||
'rest/courses?select=*,course2teachers!inner(*)&semester_id=eq.${currentSemester}&organization_id=eq.${centre_id}&course2teachers.in_charge=is.true&course2teachers.teacher_id=eq.${user.id}';
|
||||
|
||||
this.props.onAction(
|
||||
null,
|
||||
rotation,
|
||||
{},
|
||||
false,
|
||||
this.context
|
||||
);
|
||||
}}
|
||||
onCopy={() => {
|
||||
this.setState({
|
||||
copiedEvents: filterEvents.filter((item) => {
|
||||
return item.isSelected;
|
||||
}),
|
||||
});
|
||||
env.notify('success', '请选择要粘贴到的时间段');
|
||||
}}
|
||||
onCourseChange={(item: any) => {
|
||||
this.setState(
|
||||
{
|
||||
course: item?.value,
|
||||
project: undefined,
|
||||
location: undefined,
|
||||
teacher: undefined,
|
||||
},
|
||||
() => {
|
||||
this.handleViewChange(this.state.date, this.state.view);
|
||||
}
|
||||
);
|
||||
}}
|
||||
onProjectChange={(item: any) => {
|
||||
this.setState(
|
||||
{
|
||||
project: item?.value,
|
||||
location: undefined,
|
||||
teacher: undefined,
|
||||
},
|
||||
() => {
|
||||
this.handleViewChange(this.state.date, this.state.view);
|
||||
}
|
||||
);
|
||||
}}
|
||||
onLocationChange={(item: any) => {
|
||||
this.setState(
|
||||
{
|
||||
location: item?.value,
|
||||
},
|
||||
() => {
|
||||
this.handleViewChange(this.state.date, this.state.view);
|
||||
}
|
||||
);
|
||||
}}
|
||||
onTeacherChange={(item: any) => {
|
||||
this.setState(
|
||||
{
|
||||
teacher: item?.value,
|
||||
},
|
||||
() => {
|
||||
this.handleViewChange(this.state.date, this.state.view);
|
||||
}
|
||||
);
|
||||
}}
|
||||
onPeriodChange={(item: any) => {
|
||||
this.setState(
|
||||
{
|
||||
period: item?.value,
|
||||
},
|
||||
() => {
|
||||
this.handleViewChange(this.state.date, this.state.view);
|
||||
}
|
||||
);
|
||||
}}
|
||||
onModeChange={(mode: string) => {
|
||||
this.setState({
|
||||
mode: mode,
|
||||
copiedEvents: [],
|
||||
});
|
||||
|
||||
this.setState({
|
||||
events: events.map((item: any) =>
|
||||
Object.assign(item, {
|
||||
isSelected: false,
|
||||
})
|
||||
),
|
||||
});
|
||||
}}
|
||||
onEventsChange={() => {
|
||||
this.fetchCourses(
|
||||
format(this.state.start, 'yyyy-MM-dd'),
|
||||
format(this.state.end, 'yyyy-MM-dd')
|
||||
);
|
||||
}}
|
||||
onPaste={() => {
|
||||
this.setState({
|
||||
copiedEvents: [],
|
||||
});
|
||||
}}
|
||||
onSelectAll={() => {
|
||||
this.setState({
|
||||
events: events.map((item: any) => {
|
||||
if (item.isMine) {
|
||||
Object.assign(item, {
|
||||
isSelected: !item.isSelected,
|
||||
});
|
||||
}
|
||||
|
||||
return item;
|
||||
}),
|
||||
});
|
||||
}}
|
||||
onImport={() => {
|
||||
const { input } = this.props;
|
||||
this.props.onAction(null, input, {}, false, this.context);
|
||||
}}
|
||||
onEdit={() => {
|
||||
const { edit } = this.props;
|
||||
|
||||
if (user?.role === 'teacher')
|
||||
edit.dialog.body[0].tabs[0].body[0].body[2].source.url =
|
||||
'rest/courses?select=*,course2teachers!inner(*)&semester_id=eq.${currentSemester}&organization_id=eq.${centre_id}&course2teachers.in_charge=is.true&course2teachers.teacher_id=eq.${user.id}';
|
||||
|
||||
let selectedEvents = [];
|
||||
|
||||
selectedEvents = events.filter((x: any) => x.isSelected);
|
||||
|
||||
let selectedEventId = selectedEvents.map((x: any) => x.id);
|
||||
|
||||
if (selectedEvents.length === 0) {
|
||||
env.notify('warning', '未选择要编辑的场次');
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.onAction(
|
||||
null,
|
||||
edit,
|
||||
{
|
||||
schedules: selectedEventId,
|
||||
},
|
||||
false,
|
||||
this.context
|
||||
);
|
||||
}}
|
||||
onDateChange={() => {
|
||||
const { change } = this.props;
|
||||
let selectedEvents = [];
|
||||
|
||||
selectedEvents = events.filter((x: any) => x.isSelected);
|
||||
|
||||
let selectedEventId = selectedEvents.map((x: any) => x.id);
|
||||
|
||||
if (selectedEvents.length === 0) {
|
||||
env.notify('warning', '未选择要移动的场次');
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.onAction(
|
||||
null,
|
||||
change,
|
||||
{
|
||||
schedules: selectedEventId,
|
||||
op: 'move',
|
||||
day: 1,
|
||||
withReserved: false,
|
||||
},
|
||||
false,
|
||||
this.context
|
||||
);
|
||||
}}
|
||||
onDateCopy={() => {
|
||||
const { copy } = this.props;
|
||||
let selectedEvents = [];
|
||||
|
||||
selectedEvents = events.filter((x: any) => x.isSelected);
|
||||
|
||||
let selectedEventId = selectedEvents.map((x: any) => x.id);
|
||||
|
||||
if (selectedEvents.length === 0) {
|
||||
env.notify('warning', '未选择要复制的场次');
|
||||
return;
|
||||
}
|
||||
|
||||
let selectedEventItems = selectedEvents.map((x: any) => {
|
||||
return {
|
||||
is_reserved: x.is_reserved,
|
||||
is_scheduled: x.is_scheduled,
|
||||
date: x.date,
|
||||
location_id: x.location_id,
|
||||
course_id: x.course_id,
|
||||
project_id: x.project_id,
|
||||
teacher_id: x.teacher_id,
|
||||
semester_id: x.semester_id,
|
||||
organization_id: x.organization_id,
|
||||
class_list: x.class_list,
|
||||
max_student_number: x.max_student_number,
|
||||
min_student_number: x.min_student_number,
|
||||
max_group_student_number: x.max_group_student_number,
|
||||
};
|
||||
});
|
||||
|
||||
this.props.onAction(
|
||||
null,
|
||||
copy,
|
||||
{
|
||||
schedules: selectedEventId,
|
||||
items: selectedEventItems,
|
||||
op: 'copy',
|
||||
day: 1,
|
||||
},
|
||||
false,
|
||||
this.context
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
}}
|
||||
onNavigate={(date, view) => {
|
||||
this.setState({
|
||||
date: date,
|
||||
});
|
||||
}}
|
||||
onView={(view) => {
|
||||
if (copiedEvents.length > 0 && view != this.state.view) {
|
||||
env
|
||||
.confirm('切换视图后请重新复制!', '提示')
|
||||
.then((value: boolean) => {
|
||||
if (value) {
|
||||
this.setState({
|
||||
copiedEvents: [],
|
||||
view: view,
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
view: view,
|
||||
});
|
||||
}
|
||||
}}
|
||||
onRangeChange={(range: any) => {}}
|
||||
onSelectEvent={(event) => {
|
||||
const { body, add, list } = this.props;
|
||||
|
||||
if (event.isMine) {
|
||||
if (user?.role === 'teacher')
|
||||
add.dialog.body[0].controls[1].controls[1].source.url =
|
||||
'rest/courses?select=*,course2teachers!inner(*)&semester_id=eq.${currentSemester}&organization_id=eq.${centre_id}&course2teachers.in_charge=is.true&course2teachers.teacher_id=eq.${user.id}';
|
||||
|
||||
if (mode === 'select') {
|
||||
this.setState({
|
||||
events: events.map((item: any) => {
|
||||
return item.id === event.id && item.isMine
|
||||
? Object.assign(item, {
|
||||
isSelected: !item.isSelected,
|
||||
})
|
||||
: item;
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
this.props.onAction(
|
||||
null,
|
||||
add,
|
||||
{
|
||||
id: event.id,
|
||||
date: event.date,
|
||||
period_id: event.period?.id,
|
||||
location_id: event.location?.id,
|
||||
course_id: event.course?.id,
|
||||
project_id: event.project?.id,
|
||||
team_id: event.team_id,
|
||||
teacher_id: event.teacher?.id,
|
||||
max_student_number: event.max_student_number,
|
||||
min_student_number: event.min_student_number,
|
||||
max_group_student_number: event.max_group_student_number,
|
||||
is_publish: event.is_publish,
|
||||
is_reserved: event.is_reserved,
|
||||
is_scheduled: event.is_scheduled,
|
||||
organization_id: event.organization_id,
|
||||
class_list: event.class_list,
|
||||
},
|
||||
false,
|
||||
this.context
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (mode === 'select') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.onAction(
|
||||
null,
|
||||
list,
|
||||
{
|
||||
id: event.id,
|
||||
date: event.date,
|
||||
start_time: event.period?.start_time,
|
||||
project_name: event.project?.name,
|
||||
location_name: event.location?.name,
|
||||
teacher_name: event.teacher?.name,
|
||||
},
|
||||
false,
|
||||
this.context
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}}
|
||||
onSelectSlot={(slotInfo: any) => {
|
||||
if (
|
||||
user?.role !== 'admin' &&
|
||||
user?.role !== 'centreadmin' &&
|
||||
user?.role !== 'dev' &&
|
||||
!(user?.role === 'teacher' && this.state.courses.length)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { body, add } = this.props;
|
||||
|
||||
if (user?.role === 'teacher' && this.state.courses.length) {
|
||||
if (
|
||||
this.state.course &&
|
||||
!this.state.courses.some((item) => item.id == this.state.course)
|
||||
) {
|
||||
env.notify('error', '您不能对所选课程进行排课!');
|
||||
|
||||
return;
|
||||
} else {
|
||||
add.dialog.body[0].controls[1].controls[1].source.url =
|
||||
'rest/courses?select=*,course2teachers!inner(*)&semester_id=eq.${currentSemester}&organization_id=eq.${centre_id}&course2teachers.in_charge=is.true&course2teachers.teacher_id=eq.${user.id}';
|
||||
}
|
||||
}
|
||||
|
||||
const period = this.state.periods.find((item) => {
|
||||
let start = parse(
|
||||
`${format(slotInfo.start, 'yyyy-MM-dd')} ${item.start_time}`,
|
||||
'yyyy-MM-dd kk:mm:ss',
|
||||
new Date()
|
||||
);
|
||||
let end = parse(
|
||||
`${format(slotInfo.start, 'yyyy-MM-dd')} ${item.end_time}`,
|
||||
'yyyy-MM-dd kk:mm:ss',
|
||||
new Date()
|
||||
);
|
||||
|
||||
return (
|
||||
!isBefore(slotInfo.start, start) &&
|
||||
!isAfter(slotInfo.start, end)
|
||||
);
|
||||
});
|
||||
|
||||
if (period && this.state.mode === 'event') {
|
||||
let data: any = {
|
||||
date: format(slotInfo.slots[0], 'yyyy-MM-dd'),
|
||||
period_id: period.id,
|
||||
max_student_number: this.state.defaultLessonStudentMax,
|
||||
min_student_number: this.state.defaultLessonStudentMin,
|
||||
max_group_student_number: this.state.defaultGroupStudentMax,
|
||||
};
|
||||
|
||||
if (this.state.course) data.course_id = this.state.course;
|
||||
if (this.state.project) data.project_id = this.state.project;
|
||||
if (this.state.project && this.state.location)
|
||||
data.location_id = this.state.location;
|
||||
if (this.state.project && this.state.teacher)
|
||||
data.teacher_id = this.state.teacher;
|
||||
|
||||
this.props.onAction(null, add, data);
|
||||
}
|
||||
}}
|
||||
></Calendar>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
367
src/renderer/CourseList.tsx
Normal file
367
src/renderer/CourseList.tsx
Normal file
@@ -0,0 +1,367 @@
|
||||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import { isAfter, isBefore, parse } from 'date-fns';
|
||||
import { getVariable, isEffectiveApi, OptionsControlProps, Payload, OptionsControl, Select } from 'amis';
|
||||
|
||||
@OptionsControl({
|
||||
test: /(^|\/)course\-list$/,
|
||||
name: 'course-list',
|
||||
})
|
||||
export class CourseListRenderer extends React.Component<
|
||||
OptionsControlProps,
|
||||
{
|
||||
options: Array<any>;
|
||||
selectedOptions: Array<any>;
|
||||
selectedProjects: Array<any>;
|
||||
selectedClasses: Array<any>;
|
||||
}
|
||||
> {
|
||||
constructor(props: OptionsControlProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
options: [],
|
||||
selectedOptions: [],
|
||||
selectedProjects: [],
|
||||
selectedClasses: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { data, value, env, source } = this.props;
|
||||
const dateRange = getVariable(data, 'date');
|
||||
const startDate = parse(
|
||||
dateRange.split(',')[0],
|
||||
'yyyy-MM-dd',
|
||||
new Date()
|
||||
);
|
||||
const endDate = parse(
|
||||
dateRange.split(',')[1],
|
||||
'yyyy-MM-dd',
|
||||
new Date()
|
||||
);
|
||||
let selectedOptions: Array<any> = [];
|
||||
if (value) {
|
||||
selectedOptions = value.map((item: any) => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
subjectList: item.subjectList.map((sub: any) => {
|
||||
return {
|
||||
value: sub.id,
|
||||
label: sub.name,
|
||||
id: sub.id,
|
||||
name: sub.name,
|
||||
validRoomList: sub.validRoomList
|
||||
? sub.validRoomList.map((room: any) => {
|
||||
return {
|
||||
id: room.id,
|
||||
name: room.name,
|
||||
timeslotList: room.timeslotlist
|
||||
? room.timeslotlist
|
||||
.filter(
|
||||
(t: any) =>
|
||||
!isBefore(
|
||||
parse(
|
||||
t.date,
|
||||
'yyyy-MM-dd',
|
||||
new Date()
|
||||
),
|
||||
startDate
|
||||
) &&
|
||||
!isAfter(
|
||||
parse(
|
||||
t.date,
|
||||
'yyyy-MM-dd',
|
||||
new Date()
|
||||
),
|
||||
endDate
|
||||
)
|
||||
)
|
||||
.map((t: any) => {
|
||||
return {
|
||||
id: t.id,
|
||||
date: t.date,
|
||||
period: {
|
||||
id: t.period.id,
|
||||
name: t.period
|
||||
.name,
|
||||
startTime:
|
||||
t.period
|
||||
.start_time,
|
||||
endTime:
|
||||
t.period
|
||||
.end_time,
|
||||
},
|
||||
};
|
||||
})
|
||||
: [],
|
||||
};
|
||||
})
|
||||
: [],
|
||||
preSubjectList: sub.preSubjectList
|
||||
? sub.preSubjectList
|
||||
: [],
|
||||
};
|
||||
}),
|
||||
studentGroupList: item.studentGroupList.map((sub: any) => {
|
||||
return {
|
||||
value: sub.id,
|
||||
label: sub.name,
|
||||
id: sub.id,
|
||||
name: sub.name,
|
||||
validTimeslotList: sub.validTimeslotList
|
||||
? sub.validTimeslotList.filter((t: any) => {
|
||||
let tDate = parse(
|
||||
t.date,
|
||||
'yyyy-MM-dd',
|
||||
new Date()
|
||||
);
|
||||
return (
|
||||
!isBefore(tDate, startDate) &&
|
||||
!isAfter(tDate, endDate)
|
||||
);
|
||||
})
|
||||
: [],
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
}
|
||||
if (isEffectiveApi(source)) {
|
||||
env.fetcher(source, data).then((payload: Payload) => {
|
||||
this.setState({
|
||||
options: payload.data.items.map((item: any) => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
subjectList: item.course2projects.map(
|
||||
(sub: any) => {
|
||||
return {
|
||||
id: sub.projects.id,
|
||||
name: sub.projects.name,
|
||||
validRoomList: sub.projects
|
||||
.validRoomList
|
||||
? sub.projects.validRoomList.map(
|
||||
(room: any) => {
|
||||
return {
|
||||
id: room.id,
|
||||
name: room.name,
|
||||
timeslotList:
|
||||
room.timeslotlist
|
||||
? room.timeslotlist
|
||||
.filter(
|
||||
(
|
||||
t: any
|
||||
) =>
|
||||
!isBefore(
|
||||
parse(
|
||||
t.date,
|
||||
'yyyy-MM-dd',
|
||||
new Date()
|
||||
),
|
||||
startDate
|
||||
) &&
|
||||
!isAfter(
|
||||
parse(
|
||||
t.date,
|
||||
'yyyy-MM-dd',
|
||||
new Date()
|
||||
),
|
||||
endDate
|
||||
)
|
||||
)
|
||||
.map(
|
||||
(
|
||||
t: any
|
||||
) => {
|
||||
return {
|
||||
id: t.id,
|
||||
date: t.date,
|
||||
period: {
|
||||
id: t
|
||||
.period
|
||||
.id,
|
||||
name: t
|
||||
.period
|
||||
.name,
|
||||
startTime:
|
||||
t
|
||||
.period
|
||||
.start_time,
|
||||
endTime:
|
||||
t
|
||||
.period
|
||||
.end_time,
|
||||
},
|
||||
};
|
||||
}
|
||||
)
|
||||
: [],
|
||||
};
|
||||
}
|
||||
)
|
||||
: [],
|
||||
preSubjectList: sub.projects
|
||||
.preSubjectList
|
||||
? sub.projects.preSubjectList
|
||||
: [],
|
||||
value: sub.projects.id,
|
||||
label: sub.projects.name,
|
||||
};
|
||||
}
|
||||
),
|
||||
studentGroupList: item.course2orgs.map(
|
||||
(sub: any) => {
|
||||
return {
|
||||
id: sub.orgs.id,
|
||||
name: sub.orgs.name,
|
||||
validTimeslotList: sub.orgs
|
||||
.validTimeslotList
|
||||
? sub.orgs.validTimeslotList.filter(
|
||||
(t: any) => {
|
||||
let tDate = parse(
|
||||
t.date,
|
||||
'yyyy-MM-dd',
|
||||
new Date()
|
||||
);
|
||||
return (
|
||||
!isBefore(
|
||||
tDate,
|
||||
startDate
|
||||
) &&
|
||||
!isAfter(
|
||||
tDate,
|
||||
endDate
|
||||
)
|
||||
);
|
||||
}
|
||||
)
|
||||
: [],
|
||||
value: sub.orgs.id,
|
||||
label: sub.orgs.name,
|
||||
};
|
||||
}
|
||||
),
|
||||
};
|
||||
}),
|
||||
selectedOptions: selectedOptions,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleClick(option: any, e: React.MouseEvent<HTMLElement>) {
|
||||
if (e.target && (e.target as HTMLElement).closest('a,button')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { onChange } = this.props;
|
||||
let index = this.state.selectedOptions.findIndex(
|
||||
(item: any) => item.value === option.value
|
||||
);
|
||||
let newOptions = Object.assign([], this.state.selectedOptions);
|
||||
if (index === -1) {
|
||||
newOptions.push(option);
|
||||
this.setState({
|
||||
selectedOptions: newOptions,
|
||||
});
|
||||
} else {
|
||||
newOptions.splice(index, 1);
|
||||
this.setState({
|
||||
selectedOptions: newOptions,
|
||||
});
|
||||
}
|
||||
onChange(newOptions);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { placeholder, className, onChange } = this.props;
|
||||
|
||||
const { options, selectedOptions } = this.state;
|
||||
|
||||
let body: JSX.Element | null = null;
|
||||
if (options && options.length) {
|
||||
body = (
|
||||
<div>
|
||||
<div className={cx('a-ListControl-items')}>
|
||||
{options.map((option, key) => (
|
||||
<div
|
||||
key={key}
|
||||
className={cx(`a-ListControl-item`, 'w-full', {
|
||||
'is-active':
|
||||
selectedOptions.findIndex(
|
||||
(o: any) => o.value === option.value
|
||||
) !== -1,
|
||||
'is-disabled': option.disabled,
|
||||
})}
|
||||
style={{ maxWidth: '100%' }}
|
||||
onClick={this.handleClick.bind(this, option)}
|
||||
>
|
||||
{option.label}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className={`m-t-md`}>
|
||||
{selectedOptions.map((option, key) => {
|
||||
let optionData = options.find(
|
||||
(item: any) => item.value === option.value
|
||||
);
|
||||
return (
|
||||
<div
|
||||
key={key}
|
||||
className={cx('w-full', 'm-t-xs')}
|
||||
>
|
||||
<div>{optionData.label}</div>
|
||||
<Select
|
||||
multiple
|
||||
clearable={false}
|
||||
joinValues
|
||||
defaultCheckAll
|
||||
className={`w-full m-xs`}
|
||||
options={optionData.subjectList}
|
||||
onChange={(value: any) => {
|
||||
option.subjectList = value;
|
||||
onChange(
|
||||
this.state.selectedOptions
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
multiple
|
||||
clearable={false}
|
||||
joinValues
|
||||
defaultCheckAll
|
||||
className={`w-full m-xs`}
|
||||
options={optionData.studentGroupList}
|
||||
onChange={(value: any) => {
|
||||
option.studentGroupList = value;
|
||||
onChange(
|
||||
this.state.selectedOptions
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cx('a-ListControl', className)}>
|
||||
{body ? (
|
||||
body
|
||||
) : (
|
||||
<span className={cx('a-ListControl-placeholder')}>
|
||||
{placeholder}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
270
src/renderer/CustomImageUpload.tsx
Normal file
270
src/renderer/CustomImageUpload.tsx
Normal file
@@ -0,0 +1,270 @@
|
||||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import { FormControlProps, FormItem, LazyComponent } from 'amis';
|
||||
// // import { noop } from 'amis/lib/utils/helper';
|
||||
// import { Editor, TinyMCE, } from 'tinymce';
|
||||
// // @ts-ignore
|
||||
// import tinymce from 'tinymce/tinymce';
|
||||
// tinymce.PluginManager.add('fileselector', function (editor: Editor) {
|
||||
// editor.ui.registry.addButton('fileselector', {
|
||||
// // text: '选择视频',
|
||||
// icon: 'embed',
|
||||
// onAction: () => {
|
||||
|
||||
// editor.windowManager.open({
|
||||
// title: '',
|
||||
// body: {
|
||||
// type: 'panel',
|
||||
// items: [
|
||||
// {
|
||||
// name: "source",
|
||||
// type: "urlinput",
|
||||
// filetype: "media",
|
||||
// label: "Source",
|
||||
// },
|
||||
// {
|
||||
// type: "htmlpanel",
|
||||
// html: '<table><thead></thead><tbody><tr><td>文件x</td></tr></tbody></table>'
|
||||
// },
|
||||
// {
|
||||
// type: 'table', // component type
|
||||
// header: [ 'Heading 1', 'Heading 2', 'Heading 3' ],
|
||||
// cells: [
|
||||
// [ 'Cell 1', 'Cell 2', 'Cell 3' ],
|
||||
// [ 'Cell 4', 'Cell 5', 'Cell 6' ]
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// type: 'urlinput', // component type
|
||||
// name: 'url', // identifier
|
||||
// filetype: 'file', // allow any file types
|
||||
// label: 'Url', // text for component label
|
||||
// disabled: true // disabled state
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// buttons: [{
|
||||
// text: 'Close',
|
||||
// type: 'cancel',
|
||||
// },
|
||||
// {
|
||||
// text: 'Insert',
|
||||
// type: 'submit',
|
||||
// primary: true,
|
||||
// }]
|
||||
// })
|
||||
// },
|
||||
// });
|
||||
// });
|
||||
export interface RichTextProps extends FormControlProps {
|
||||
options?: any;
|
||||
vendor?: 'froala' | 'tinymce';
|
||||
}
|
||||
|
||||
function loadRichText(): () => Promise<any> {
|
||||
return () =>
|
||||
import('amis-ui/lib/components/Tinymce').then(
|
||||
(item) => item.default
|
||||
);
|
||||
}
|
||||
|
||||
export default class CustomImageUpload extends React.Component<
|
||||
RichTextProps,
|
||||
any
|
||||
> {
|
||||
static defaultProps: Partial<RichTextProps> = {
|
||||
imageEditable: true,
|
||||
videoReceiver: '/api/upload/video',
|
||||
placeholder: 'placeholder.enter',
|
||||
options: {
|
||||
toolbarButtonsSM: [
|
||||
'paragraphFormat',
|
||||
'quote',
|
||||
'color',
|
||||
'|',
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'strikeThrough',
|
||||
'|',
|
||||
'formatOL',
|
||||
'formatUL',
|
||||
'align',
|
||||
'|',
|
||||
'insertLink',
|
||||
'insertImage',
|
||||
'insertEmotion',
|
||||
'insertTable',
|
||||
'|',
|
||||
'undo',
|
||||
'redo',
|
||||
'html',
|
||||
],
|
||||
toolbarButtonsMD: [
|
||||
'paragraphFormat',
|
||||
'quote',
|
||||
'color',
|
||||
'|',
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'strikeThrough',
|
||||
'|',
|
||||
'formatOL',
|
||||
'formatUL',
|
||||
'align',
|
||||
'|',
|
||||
'insertLink',
|
||||
'insertImage',
|
||||
'insertEmotion',
|
||||
'insertTable',
|
||||
'|',
|
||||
'undo',
|
||||
'redo',
|
||||
'html',
|
||||
],
|
||||
toolbarButtons: [
|
||||
'paragraphFormat',
|
||||
'quote',
|
||||
'color',
|
||||
'|',
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'strikeThrough',
|
||||
'|',
|
||||
'formatOL',
|
||||
'formatUL',
|
||||
'align',
|
||||
'|',
|
||||
'insertLink',
|
||||
'insertImage',
|
||||
'insertEmotion',
|
||||
'insertTable',
|
||||
'|',
|
||||
'undo',
|
||||
'redo',
|
||||
'html',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
state = {
|
||||
focused: false,
|
||||
};
|
||||
config: any = null;
|
||||
constructor(props: RichTextProps) {
|
||||
super(props);
|
||||
|
||||
const finnalVendor = props.vendor || 'tinymce';
|
||||
this.handleFocus = this.handleFocus.bind(this);
|
||||
this.handleBlur = this.handleBlur.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
|
||||
const fetcher = props.env.fetcher;
|
||||
this.config = {
|
||||
...props.options,
|
||||
// plugins: [
|
||||
// 'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker',
|
||||
// 'searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking',
|
||||
// 'table emoticons template paste help fileselector',
|
||||
// ],
|
||||
// toolbar:
|
||||
// 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | ' +
|
||||
// 'bullist numlist outdent indent | link image | print preview fileselector fullpage | ' +
|
||||
// 'forecolor backcolor emoticons | help',
|
||||
// document_base_url: `${location.origin}`,
|
||||
relative_urls: false,
|
||||
remove_script_host: true,
|
||||
images_upload_handler: async (
|
||||
blobInfo: any,
|
||||
success: (locaiton: string) => void,
|
||||
failure: (reason: string) => void
|
||||
) => {
|
||||
let formData = new FormData();
|
||||
formData.set('file', blobInfo.blob());
|
||||
|
||||
this.props.env
|
||||
.fetcher({
|
||||
url: 'storage/v1/object/exam',
|
||||
method: 'post',
|
||||
dataType: 'form-data',
|
||||
data: formData,
|
||||
})
|
||||
.then(res => {
|
||||
success(res.data.data.value);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
handleFocus() {
|
||||
this.setState({
|
||||
focused: true,
|
||||
});
|
||||
}
|
||||
|
||||
handleBlur() {
|
||||
this.setState({
|
||||
focused: false,
|
||||
});
|
||||
}
|
||||
|
||||
handleChange(
|
||||
value: any,
|
||||
submitOnChange?: boolean,
|
||||
changeImmediately?: boolean
|
||||
) {
|
||||
const { onChange, disabled } = this.props;
|
||||
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
onChange?.(value, submitOnChange, changeImmediately);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
className,
|
||||
classPrefix: ns,
|
||||
value,
|
||||
onChange,
|
||||
disabled,
|
||||
size,
|
||||
vendor,
|
||||
env,
|
||||
locale,
|
||||
translate,
|
||||
} = this.props;
|
||||
|
||||
const finnalVendor = vendor || (env.richTextToken ? 'froala' : 'tinymce');
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(`${ns}RichTextControl`, className, {
|
||||
'is-focused': this.state.focused,
|
||||
'is-disabled': disabled,
|
||||
})}
|
||||
>
|
||||
<LazyComponent
|
||||
getComponent={loadRichText()}
|
||||
model={value}
|
||||
onModelChange={this.handleChange}
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
config={this.config}
|
||||
disabled={disabled}
|
||||
locale={locale}
|
||||
translate={translate}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@FormItem({
|
||||
test: /(^|\/)image-upload$/,
|
||||
name: 'image-upload',
|
||||
})
|
||||
export class CustomImageUploadRenderer extends CustomImageUpload {}
|
||||
192
src/renderer/CustomRichtext.tsx
Normal file
192
src/renderer/CustomRichtext.tsx
Normal file
@@ -0,0 +1,192 @@
|
||||
import React from 'react';
|
||||
import { FormItem, FormControlProps } from 'amis';
|
||||
import cx from 'classnames';
|
||||
import CustomTinymceEditor from './CustomTiny';
|
||||
|
||||
export interface RichTextProps extends FormControlProps {
|
||||
options?: any;
|
||||
vendor?: 'froala' | 'tinymce';
|
||||
}
|
||||
|
||||
function loadRichText(): () => Promise<any> {
|
||||
return () =>
|
||||
import('./CustomTiny').then((item) => item.default);
|
||||
}
|
||||
|
||||
export default class CustomRichtext extends React.Component<
|
||||
RichTextProps,
|
||||
any
|
||||
> {
|
||||
static defaultProps: Partial<RichTextProps> = {
|
||||
imageEditable: true,
|
||||
videoReceiver: '/api/upload/video',
|
||||
placeholder: 'placeholder.enter',
|
||||
options: {
|
||||
toolbarButtonsSM: [
|
||||
'paragraphFormat',
|
||||
'quote',
|
||||
'color',
|
||||
'|',
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'strikeThrough',
|
||||
'|',
|
||||
'formatOL',
|
||||
'formatUL',
|
||||
'align',
|
||||
'|',
|
||||
'insertLink',
|
||||
'insertImage',
|
||||
'insertEmotion',
|
||||
'insertTable',
|
||||
'|',
|
||||
'undo',
|
||||
'redo',
|
||||
'html',
|
||||
],
|
||||
toolbarButtonsMD: [
|
||||
'paragraphFormat',
|
||||
'quote',
|
||||
'color',
|
||||
'|',
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'strikeThrough',
|
||||
'|',
|
||||
'formatOL',
|
||||
'formatUL',
|
||||
'align',
|
||||
'|',
|
||||
'insertLink',
|
||||
'insertImage',
|
||||
'insertEmotion',
|
||||
'insertTable',
|
||||
'|',
|
||||
'undo',
|
||||
'redo',
|
||||
'html',
|
||||
],
|
||||
toolbarButtons: [
|
||||
'paragraphFormat',
|
||||
'quote',
|
||||
'color',
|
||||
'|',
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'strikeThrough',
|
||||
'|',
|
||||
'formatOL',
|
||||
'formatUL',
|
||||
'align',
|
||||
'|',
|
||||
'insertLink',
|
||||
'insertImage',
|
||||
'insertEmotion',
|
||||
'insertTable',
|
||||
'|',
|
||||
'undo',
|
||||
'redo',
|
||||
'html',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
state = {
|
||||
focused: false,
|
||||
};
|
||||
config: any = null;
|
||||
|
||||
constructor(props: RichTextProps) {
|
||||
super(props);
|
||||
|
||||
const finnalVendor = props.vendor || 'tinymce';
|
||||
this.handleFocus = this.handleFocus.bind(this);
|
||||
this.handleBlur = this.handleBlur.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.config = {
|
||||
...props.options,
|
||||
// plugins: [
|
||||
// 'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker',
|
||||
// 'searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking',
|
||||
// 'table emoticons template paste help',
|
||||
// ],
|
||||
// toolbar:
|
||||
// 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | ' +
|
||||
// 'bullist numlist outdent indent | link image | print preview fullpage | ' +
|
||||
// 'forecolor backcolor emoticons | help',
|
||||
relative_urls: false,
|
||||
remove_script_host: true,
|
||||
};
|
||||
}
|
||||
|
||||
handleFocus() {
|
||||
this.setState({
|
||||
focused: true,
|
||||
});
|
||||
}
|
||||
|
||||
handleBlur() {
|
||||
this.setState({
|
||||
focused: false,
|
||||
});
|
||||
}
|
||||
|
||||
handleChange(
|
||||
value: any,
|
||||
submitOnChange?: boolean,
|
||||
changeImmediately?: boolean
|
||||
) {
|
||||
const { onChange, disabled } = this.props;
|
||||
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
onChange?.(value, submitOnChange, changeImmediately);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
className,
|
||||
classPrefix: ns,
|
||||
value,
|
||||
onChange,
|
||||
disabled,
|
||||
size,
|
||||
vendor,
|
||||
env,
|
||||
locale,
|
||||
translate,
|
||||
} = this.props;
|
||||
|
||||
const finnalVendor = vendor || (env.richTextToken ? 'froala' : 'tinymce');
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(`${ns}RichTextControl`, className, {
|
||||
'is-focused': this.state.focused,
|
||||
'is-disabled': disabled,
|
||||
})}
|
||||
>
|
||||
<CustomTinymceEditor
|
||||
model={value}
|
||||
onModelChange={this.handleChange}
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
config={this.config}
|
||||
disabled={disabled}
|
||||
fetcher={this.props.env.fetcher}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@FormItem({
|
||||
test: /(^|\/)custom-richtext$/,
|
||||
name: 'custom-richtext',
|
||||
})
|
||||
export class CustomRichtextRenderer extends CustomRichtext {}
|
||||
732
src/renderer/CustomTiny.tsx
Normal file
732
src/renderer/CustomTiny.tsx
Normal file
@@ -0,0 +1,732 @@
|
||||
import React from 'react';
|
||||
// Import TinyMCE
|
||||
// @ts-ignore
|
||||
import tinymce from 'tinymce/tinymce';
|
||||
|
||||
// A theme is also required
|
||||
import 'tinymce/icons/default/index';
|
||||
import 'tinymce/themes/silver';
|
||||
|
||||
import 'tinymce/models/dom/model';
|
||||
// import 'tinymce/skins/ui/oxide/skin.css';
|
||||
|
||||
// Any plugins you want to use has to be imported
|
||||
import 'tinymce/plugins/advlist';
|
||||
import 'tinymce/plugins/autolink';
|
||||
import 'tinymce/plugins/autoresize';
|
||||
import 'tinymce/plugins/lists';
|
||||
import 'tinymce/plugins/link';
|
||||
import 'tinymce/plugins/image';
|
||||
import 'tinymce/plugins/charmap';
|
||||
import 'tinymce/plugins/preview';
|
||||
import 'tinymce/plugins/anchor';
|
||||
import 'tinymce/plugins/searchreplace';
|
||||
import 'tinymce/plugins/visualblocks';
|
||||
import 'tinymce/plugins/code';
|
||||
import 'tinymce/plugins/fullscreen';
|
||||
import 'tinymce/plugins/insertdatetime';
|
||||
import 'tinymce/plugins/media';
|
||||
import 'tinymce/plugins/table';
|
||||
import 'tinymce/plugins/help';
|
||||
import 'tinymce/plugins/wordcount';
|
||||
import 'tinymce/plugins/pagebreak';
|
||||
import 'tinymce/plugins/visualchars';
|
||||
import 'tinymce/plugins/template';
|
||||
import 'tinymce/plugins/nonbreaking';
|
||||
import 'tinymce/plugins/emoticons';
|
||||
import 'tinymce/plugins/emoticons/js/emojis';
|
||||
import 'tinymce/plugins/quickbars/plugin';
|
||||
import {LocaleProps} from 'amis-core';
|
||||
|
||||
import DestPicker from './DestPicker';
|
||||
|
||||
interface TinymceEditorProps extends LocaleProps {
|
||||
model: string;
|
||||
onModelChange?: (value: string) => void;
|
||||
onFocus?: () => void;
|
||||
onBlur?: () => void;
|
||||
disabled?: boolean;
|
||||
config?: any;
|
||||
outputFormat?: 'html' | 'text';
|
||||
receiver?: string;
|
||||
fetcher?: any;
|
||||
}
|
||||
|
||||
export default class CustomTinymceEditor extends React.Component<TinymceEditorProps> {
|
||||
static defaultProps = {
|
||||
outputFormat: 'html',
|
||||
};
|
||||
config?: any;
|
||||
editor?: any;
|
||||
currentContent?: string;
|
||||
|
||||
elementRef: React.RefObject<HTMLTextAreaElement> = React.createRef();
|
||||
|
||||
constructor(props: TinymceEditorProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
pickedBreadcrumb: ['根目录'],
|
||||
showDialog: false,
|
||||
editor: null,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const _this = this;
|
||||
const locale = this.props.locale;
|
||||
|
||||
tinymce.PluginManager.add('source', function (editor) {
|
||||
const openDialog = () => {
|
||||
_this.setState({
|
||||
showDialog: true,
|
||||
editor: editor,
|
||||
});
|
||||
|
||||
// editor.windowManager.open({
|
||||
// title: 'Source Picker',
|
||||
// body: {
|
||||
// type: 'panel',
|
||||
// items: [
|
||||
// {
|
||||
// type: 'htmlpanel',
|
||||
// html: '<div>hello</div>',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// buttons: [
|
||||
// {
|
||||
// type: 'cancel',
|
||||
// text: 'Close',
|
||||
// },
|
||||
// {
|
||||
// type: 'submit',
|
||||
// text: 'Save',
|
||||
// },
|
||||
// ],
|
||||
// onSubmit: (api) => {
|
||||
// const data = api.getData();
|
||||
// editor.insertContent('Title: ' + data.title);
|
||||
// api.close();
|
||||
// },
|
||||
// });
|
||||
};
|
||||
editor.ui.registry.addButton('source', {
|
||||
icon: 'more-drawer',
|
||||
onAction: () => {
|
||||
openDialog();
|
||||
},
|
||||
});
|
||||
editor.ui.registry.addMenuItem('source', {
|
||||
icon: 'more-drawer',
|
||||
text: 'Source Picker',
|
||||
onAction: () => {
|
||||
openDialog();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
this.config = {
|
||||
inline: false,
|
||||
skin: false,
|
||||
content_css: false,
|
||||
height: 400,
|
||||
language: !locale || locale === 'zh-CN' ? 'zh_CN' : 'en',
|
||||
plugins: [
|
||||
'advlist',
|
||||
'autolink',
|
||||
'autoresize',
|
||||
'link',
|
||||
'image',
|
||||
'source',
|
||||
'lists',
|
||||
'charmap',
|
||||
'preview',
|
||||
'anchor',
|
||||
'pagebreak',
|
||||
'searchreplace',
|
||||
'wordcount',
|
||||
'visualblocks',
|
||||
'visualchars',
|
||||
'code',
|
||||
'fullscreen',
|
||||
'insertdatetime',
|
||||
'media',
|
||||
'nonbreaking',
|
||||
'table',
|
||||
'emoticons',
|
||||
'template',
|
||||
'help',
|
||||
'quickbars'
|
||||
],
|
||||
toolbar:
|
||||
'undo redo | blocks | bold italic | alignleft aligncenter alignright alignjustify | ' +
|
||||
'bullist numlist outdent indent | link image source | preview media | ' +
|
||||
'fontfamily fontsize forecolor backcolor emoticons | print help',
|
||||
quickbars_selection_toolbar: 'bold italic | link h2 h3 blockquote',
|
||||
quickbars_insert_toolbar: 'quickimage quicktable',
|
||||
menu: {
|
||||
file: {
|
||||
title: 'File',
|
||||
items: 'newdocument restoredraft | preview | print ',
|
||||
},
|
||||
edit: {
|
||||
title: 'Edit',
|
||||
items: 'undo redo | cut copy paste | selectall | searchreplace',
|
||||
},
|
||||
view: {
|
||||
title: 'View',
|
||||
items:
|
||||
'code | visualaid visualchars visualblocks | preview fullscreen',
|
||||
},
|
||||
insert: {
|
||||
title: 'Insert',
|
||||
items:
|
||||
'image link media source template codesample inserttable | charmap emoticons hr | pagebreak nonbreaking anchor toc | insertdatetime',
|
||||
},
|
||||
format: {
|
||||
title: 'Format',
|
||||
items:
|
||||
'bold italic underline strikethrough superscript subscript codeformat | formats blockformats fontformats fontsizes align | forecolor backcolor | removeformat',
|
||||
},
|
||||
tools: {
|
||||
title: 'Tools',
|
||||
items: 'code wordcount',
|
||||
},
|
||||
table: {
|
||||
title: 'Table',
|
||||
items: 'inserttable | cell row column | tableprops deletetable',
|
||||
},
|
||||
help: { title: 'Help', items: 'help' },
|
||||
},
|
||||
paste_data_images: true,
|
||||
content_style: '[data-mce-bogus] video {display:none;}',
|
||||
...this.props.config,
|
||||
target: this.elementRef.current,
|
||||
readOnly: this.props.disabled,
|
||||
setup: (editor: any) => {
|
||||
this.editor = editor;
|
||||
|
||||
editor.on('init', (e: Event) => {
|
||||
this.initEditor(e, editor);
|
||||
});
|
||||
editor.on('NodeChange', function(e) {
|
||||
if (e && e.element.firstChild.nodeName.toLowerCase() == 'video') {
|
||||
tinymce.DOM.setAttribs(e.element.firstChild, {'width': 640, 'height': 480});
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
tinymce.init(this.config);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: TinymceEditorProps) {
|
||||
const props = this.props;
|
||||
|
||||
if (
|
||||
props.model !== prevProps.model &&
|
||||
props.model !== this.currentContent
|
||||
) {
|
||||
this.editor?.setContent(props.model || '');
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
tinymce.remove(this.editor);
|
||||
}
|
||||
|
||||
initEditor(e: any, editor: any) {
|
||||
const { model, onModelChange, outputFormat, onFocus, onBlur } = this.props;
|
||||
|
||||
const value = model || '';
|
||||
editor.setContent(value);
|
||||
|
||||
if (onModelChange) {
|
||||
editor.on('change keyup setcontent', (e: any) => {
|
||||
const newContent = editor.getContent({ format: outputFormat });
|
||||
|
||||
if (newContent !== this.currentContent) {
|
||||
this.currentContent = newContent;
|
||||
onModelChange(newContent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onFocus && editor.on('focus', onFocus);
|
||||
onBlur && editor.on('blur', onBlur);
|
||||
}
|
||||
|
||||
onConfirm(html) {
|
||||
this.state.editor.insertContent(html);
|
||||
this.setState({ showDialog: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<textarea ref={this.elementRef} />
|
||||
{this.state.showDialog && (
|
||||
<DestPicker
|
||||
isRichtext={true}
|
||||
fetcher={this.props.fetcher}
|
||||
bucket="mooc"
|
||||
breadcrumb={this.state.pickedBreadcrumb}
|
||||
setBreadcrumb={(breadcrumb) =>
|
||||
this.setState({ pickedBreadcrumb: breadcrumb })
|
||||
}
|
||||
onConfirm={(html) => this.onConfirm(html)}
|
||||
onCancel={() => this.setState({ showDialog: false })}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
tinymce.addI18n('zh_CN', {
|
||||
Redo: '\u91cd\u505a',
|
||||
Undo: '\u64a4\u9500',
|
||||
Cut: '\u526a\u5207',
|
||||
Copy: '\u590d\u5236',
|
||||
Paste: '\u7c98\u8d34',
|
||||
'Select all': '\u5168\u9009',
|
||||
'New document': '\u65b0\u6587\u4ef6',
|
||||
Ok: '\u786e\u5b9a',
|
||||
Cancel: '\u53d6\u6d88',
|
||||
'Visual aids': '\u7f51\u683c\u7ebf',
|
||||
Bold: '\u7c97\u4f53',
|
||||
Italic: '\u659c\u4f53',
|
||||
Underline: '\u4e0b\u5212\u7ebf',
|
||||
Strikethrough: '\u5220\u9664\u7ebf',
|
||||
Superscript: '\u4e0a\u6807',
|
||||
Subscript: '\u4e0b\u6807',
|
||||
'Clear formatting': '\u6e05\u9664\u683c\u5f0f',
|
||||
'Align left': '\u5de6\u8fb9\u5bf9\u9f50',
|
||||
'Align center': '\u4e2d\u95f4\u5bf9\u9f50',
|
||||
'Align right': '\u53f3\u8fb9\u5bf9\u9f50',
|
||||
Justify: '\u4e24\u7aef\u5bf9\u9f50',
|
||||
'Bullet list': '\u9879\u76ee\u7b26\u53f7',
|
||||
'Numbered list': '\u7f16\u53f7\u5217\u8868',
|
||||
'Decrease indent': '\u51cf\u5c11\u7f29\u8fdb',
|
||||
'Increase indent': '\u589e\u52a0\u7f29\u8fdb',
|
||||
Close: '\u5173\u95ed',
|
||||
Formats: '\u683c\u5f0f',
|
||||
"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.":
|
||||
'\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u6253\u5f00\u526a\u8d34\u677f\uff0c\u8bf7\u4f7f\u7528Ctrl+X/C/V\u7b49\u5feb\u6377\u952e\u3002',
|
||||
Headers: '\u6807\u9898',
|
||||
'Header 1': '\u6807\u98981',
|
||||
'Header 2': '\u6807\u98982',
|
||||
'Header 3': '\u6807\u98983',
|
||||
'Header 4': '\u6807\u98984',
|
||||
'Header 5': '\u6807\u98985',
|
||||
'Header 6': '\u6807\u98986',
|
||||
Headings: '\u6807\u9898',
|
||||
'Heading 1': '\u6807\u98981',
|
||||
'Heading 2': '\u6807\u98982',
|
||||
'Heading 3': '\u6807\u98983',
|
||||
'Heading 4': '\u6807\u98984',
|
||||
'Heading 5': '\u6807\u98985',
|
||||
'Heading 6': '\u6807\u98986',
|
||||
Preformatted: '\u9884\u5148\u683c\u5f0f\u5316\u7684',
|
||||
Div: 'Div',
|
||||
Pre: 'Pre',
|
||||
Code: '\u4ee3\u7801',
|
||||
Paragraph: '\u6bb5\u843d',
|
||||
Blockquote: '\u5f15\u6587\u533a\u5757',
|
||||
Inline: '\u6587\u672c',
|
||||
Blocks: '\u57fa\u5757',
|
||||
'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.':
|
||||
'\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002',
|
||||
Fonts: '\u5b57\u4f53',
|
||||
'Font Sizes': '\u5b57\u53f7',
|
||||
Class: '\u7c7b\u578b',
|
||||
'Browse for an image': '\u6d4f\u89c8\u56fe\u50cf',
|
||||
OR: '\u6216',
|
||||
'Drop an image here': '\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64',
|
||||
Upload: '\u4e0a\u4f20',
|
||||
Block: '\u5757',
|
||||
Align: '\u5bf9\u9f50',
|
||||
Default: '\u9ed8\u8ba4',
|
||||
Circle: '\u7a7a\u5fc3\u5706',
|
||||
Disc: '\u5b9e\u5fc3\u5706',
|
||||
Square: '\u65b9\u5757',
|
||||
'Lower Alpha': '\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd',
|
||||
'Lower Greek': '\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd',
|
||||
'Lower Roman': '\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd',
|
||||
'Upper Alpha': '\u5927\u5199\u82f1\u6587\u5b57\u6bcd',
|
||||
'Upper Roman': '\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd',
|
||||
'Anchor...': '\u951a\u70b9...',
|
||||
Name: '\u540d\u79f0',
|
||||
Id: '\u6807\u8bc6\u7b26',
|
||||
'Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.':
|
||||
'\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002',
|
||||
'You have unsaved changes are you sure you want to navigate away?':
|
||||
'\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f',
|
||||
'Restore last draft': '\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f',
|
||||
'Special character...': '\u7279\u6b8a\u5b57\u7b26...',
|
||||
'Source code': '\u6e90\u4ee3\u7801',
|
||||
'Insert/Edit code sample':
|
||||
'\u63d2\u5165/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b',
|
||||
Language: '\u8bed\u8a00',
|
||||
'Code sample...': '\u793a\u4f8b\u4ee3\u7801...',
|
||||
'Color Picker': '\u9009\u8272\u5668',
|
||||
R: 'R',
|
||||
G: 'G',
|
||||
B: 'B',
|
||||
'Left to right': '\u4ece\u5de6\u5230\u53f3',
|
||||
'Right to left': '\u4ece\u53f3\u5230\u5de6',
|
||||
'Emoticons...': '\u8868\u60c5\u7b26\u53f7...',
|
||||
'Metadata and Document Properties':
|
||||
'\u5143\u6570\u636e\u548c\u6587\u6863\u5c5e\u6027',
|
||||
Title: '\u6807\u9898',
|
||||
Keywords: '\u5173\u952e\u8bcd',
|
||||
Description: '\u63cf\u8ff0',
|
||||
Robots: '\u673a\u5668\u4eba',
|
||||
Author: '\u4f5c\u8005',
|
||||
Encoding: '\u7f16\u7801',
|
||||
Fullscreen: '\u5168\u5c4f',
|
||||
Action: '\u64cd\u4f5c',
|
||||
Shortcut: '\u5feb\u6377\u952e',
|
||||
Help: '\u5e2e\u52a9',
|
||||
Address: '\u5730\u5740',
|
||||
'Focus to menubar': '\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f',
|
||||
'Focus to toolbar': '\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f',
|
||||
'Focus to element path':
|
||||
'\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84',
|
||||
'Focus to contextual toolbar':
|
||||
'\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355',
|
||||
'Insert link (if link plugin activated)':
|
||||
'\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)',
|
||||
'Save (if save plugin activated)':
|
||||
'\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)',
|
||||
'Find (if searchreplace plugin activated)':
|
||||
'\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)',
|
||||
'Plugins installed ({0}):': '\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):',
|
||||
'Premium plugins:': '\u4f18\u79c0\u63d2\u4ef6\uff1a',
|
||||
'Learn more...': '\u4e86\u89e3\u66f4\u591a...',
|
||||
'You are using {0}': '\u4f60\u6b63\u5728\u4f7f\u7528 {0}',
|
||||
Plugins: '\u63d2\u4ef6',
|
||||
'Handy Shortcuts': '\u5feb\u6377\u952e',
|
||||
'Horizontal line': '\u6c34\u5e73\u5206\u5272\u7ebf',
|
||||
'Insert/edit image': '\u63d2\u5165/\u7f16\u8f91\u56fe\u7247',
|
||||
'Image description': '\u56fe\u7247\u63cf\u8ff0',
|
||||
Source: '\u5730\u5740',
|
||||
Dimensions: '\u5927\u5c0f',
|
||||
'Constrain proportions': '\u4fdd\u6301\u7eb5\u6a2a\u6bd4',
|
||||
General: '\u666e\u901a',
|
||||
Advanced: '\u9ad8\u7ea7',
|
||||
Style: '\u6837\u5f0f',
|
||||
'Vertical space': '\u5782\u76f4\u8fb9\u8ddd',
|
||||
'Horizontal space': '\u6c34\u5e73\u8fb9\u8ddd',
|
||||
Border: '\u8fb9\u6846',
|
||||
'Insert image': '\u63d2\u5165\u56fe\u7247',
|
||||
'Image...': '\u56fe\u7247...',
|
||||
'Image list': '\u56fe\u7247\u5217\u8868',
|
||||
'Source Picker': '\u63d2\u5165\u8d44\u6e90\u5e93\u4e2d\u6587\u4ef6',
|
||||
'Rotate counterclockwise': '\u9006\u65f6\u9488\u65cb\u8f6c',
|
||||
'Rotate clockwise': '\u987a\u65f6\u9488\u65cb\u8f6c',
|
||||
'Flip vertically': '\u5782\u76f4\u7ffb\u8f6c',
|
||||
'Flip horizontally': '\u6c34\u5e73\u7ffb\u8f6c',
|
||||
'Edit image': '\u7f16\u8f91\u56fe\u7247',
|
||||
'Image options': '\u56fe\u7247\u9009\u9879',
|
||||
'Zoom in': '\u653e\u5927',
|
||||
'Zoom out': '\u7f29\u5c0f',
|
||||
Crop: '\u88c1\u526a',
|
||||
Resize: '\u8c03\u6574\u5927\u5c0f',
|
||||
Orientation: '\u65b9\u5411',
|
||||
Brightness: '\u4eae\u5ea6',
|
||||
Sharpen: '\u9510\u5316',
|
||||
Contrast: '\u5bf9\u6bd4\u5ea6',
|
||||
'Color levels': '\u989c\u8272\u5c42\u6b21',
|
||||
Gamma: '\u4f3d\u9a6c\u503c',
|
||||
Invert: '\u53cd\u8f6c',
|
||||
Apply: '\u5e94\u7528',
|
||||
Back: '\u540e\u9000',
|
||||
'Insert date/time': '\u63d2\u5165\u65e5\u671f/\u65f6\u95f4',
|
||||
'Date/time': '\u65e5\u671f/\u65f6\u95f4',
|
||||
'Insert/Edit Link': '\u63d2\u5165/\u7f16\u8f91\u94fe\u63a5',
|
||||
'Insert/edit link': '\u63d2\u5165/\u7f16\u8f91\u94fe\u63a5',
|
||||
'Text to display': '\u663e\u793a\u6587\u5b57',
|
||||
Url: '\u5730\u5740',
|
||||
'Open link in...': '\u94fe\u63a5\u6253\u5f00\u4f4d\u7f6e...',
|
||||
'Current window': '\u5f53\u524d\u7a97\u53e3',
|
||||
None: '\u65e0',
|
||||
'New window': '\u5728\u65b0\u7a97\u53e3\u6253\u5f00',
|
||||
'Remove link': '\u5220\u9664\u94fe\u63a5',
|
||||
Anchors: '\u951a\u70b9',
|
||||
'Link...': '\u94fe\u63a5...',
|
||||
'Paste or type a link': '\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5',
|
||||
'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?':
|
||||
'\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f',
|
||||
'The URL you entered seems to be an external link. Do you want to add the required http:// prefix?':
|
||||
'\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp://:\u524d\u7f00\u5417\uff1f',
|
||||
'Link list': '\u94fe\u63a5\u5217\u8868',
|
||||
'Insert video': '\u63d2\u5165\u89c6\u9891',
|
||||
'Insert/edit video': '\u63d2\u5165/\u7f16\u8f91\u89c6\u9891',
|
||||
'Insert/edit media': '\u63d2\u5165/\u7f16\u8f91\u5a92\u4f53',
|
||||
'Alternative source': '\u955c\u50cf',
|
||||
'Alternative source URL': '\u66ff\u4ee3\u6765\u6e90\u7f51\u5740',
|
||||
'Media poster (Image URL)': '\u5c01\u9762(\u56fe\u7247\u5730\u5740)',
|
||||
'Paste your embed code below:':
|
||||
'\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:',
|
||||
Embed: '\u5185\u5d4c',
|
||||
'Media...': '\u591a\u5a92\u4f53...',
|
||||
'Nonbreaking space': '\u4e0d\u95f4\u65ad\u7a7a\u683c',
|
||||
'Page break': '\u5206\u9875\u7b26',
|
||||
'Paste as text': '\u7c98\u8d34\u4e3a\u6587\u672c',
|
||||
Preview: '\u9884\u89c8',
|
||||
'Print...': '\u6253\u5370...',
|
||||
Save: '\u4fdd\u5b58',
|
||||
Find: '\u67e5\u627e',
|
||||
'Replace with': '\u66ff\u6362\u4e3a',
|
||||
Replace: '\u66ff\u6362',
|
||||
'Replace all': '\u5168\u90e8\u66ff\u6362',
|
||||
Previous: '\u4e0a\u4e00\u4e2a',
|
||||
Next: '\u4e0b\u4e00\u4e2a',
|
||||
'Find and replace...': '\u67e5\u627e\u5e76\u66ff\u6362...',
|
||||
'Could not find the specified string.':
|
||||
'\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.',
|
||||
'Match case': '\u533a\u5206\u5927\u5c0f\u5199',
|
||||
'Find whole words only': '\u5168\u5b57\u5339\u914d',
|
||||
'Spell check': '\u62fc\u5199\u68c0\u67e5',
|
||||
Ignore: '\u5ffd\u7565',
|
||||
'Ignore all': '\u5168\u90e8\u5ffd\u7565',
|
||||
Finish: '\u5b8c\u6210',
|
||||
'Add to Dictionary': '\u6dfb\u52a0\u5230\u5b57\u5178',
|
||||
'Insert table': '\u63d2\u5165\u8868\u683c',
|
||||
'Table properties': '\u8868\u683c\u5c5e\u6027',
|
||||
'Delete table': '\u5220\u9664\u8868\u683c',
|
||||
Cell: '\u5355\u5143\u683c',
|
||||
Row: '\u884c',
|
||||
Column: '\u5217',
|
||||
'Cell properties': '\u5355\u5143\u683c\u5c5e\u6027',
|
||||
'Merge cells': '\u5408\u5e76\u5355\u5143\u683c',
|
||||
'Split cell': '\u62c6\u5206\u5355\u5143\u683c',
|
||||
'Insert row before': '\u5728\u4e0a\u65b9\u63d2\u5165',
|
||||
'Insert row after': '\u5728\u4e0b\u65b9\u63d2\u5165',
|
||||
'Delete row': '\u5220\u9664\u884c',
|
||||
'Row properties': '\u884c\u5c5e\u6027',
|
||||
'Cut row': '\u526a\u5207\u884c',
|
||||
'Copy row': '\u590d\u5236\u884c',
|
||||
'Paste row before': '\u7c98\u8d34\u5230\u4e0a\u65b9',
|
||||
'Paste row after': '\u7c98\u8d34\u5230\u4e0b\u65b9',
|
||||
'Insert column before': '\u5728\u5de6\u4fa7\u63d2\u5165',
|
||||
'Insert column after': '\u5728\u53f3\u4fa7\u63d2\u5165',
|
||||
'Delete column': '\u5220\u9664\u5217',
|
||||
Cols: '\u5217',
|
||||
Rows: '\u884c',
|
||||
Width: '\u5bbd',
|
||||
Height: '\u9ad8',
|
||||
'Cell spacing': '\u5355\u5143\u683c\u5916\u95f4\u8ddd',
|
||||
'Cell padding': '\u5355\u5143\u683c\u5185\u8fb9\u8ddd',
|
||||
'Show caption': '\u663e\u793a\u6807\u9898',
|
||||
Left: '\u5de6\u5bf9\u9f50',
|
||||
Center: '\u5c45\u4e2d',
|
||||
Right: '\u53f3\u5bf9\u9f50',
|
||||
'Cell type': '\u5355\u5143\u683c\u7c7b\u578b',
|
||||
Scope: '\u8303\u56f4',
|
||||
Alignment: '\u5bf9\u9f50\u65b9\u5f0f',
|
||||
'H Align': '\u6c34\u5e73\u5bf9\u9f50',
|
||||
'V Align': '\u5782\u76f4\u5bf9\u9f50',
|
||||
Top: '\u9876\u90e8\u5bf9\u9f50',
|
||||
Middle: '\u5782\u76f4\u5c45\u4e2d',
|
||||
Bottom: '\u5e95\u90e8\u5bf9\u9f50',
|
||||
'Header cell': '\u8868\u5934\u5355\u5143\u683c',
|
||||
'Row group': '\u884c\u7ec4',
|
||||
'Column group': '\u5217\u7ec4',
|
||||
'Row type': '\u884c\u7c7b\u578b',
|
||||
Header: '\u8868\u5934',
|
||||
Body: '\u8868\u4f53',
|
||||
Footer: '\u8868\u5c3e',
|
||||
'Border color': '\u8fb9\u6846\u989c\u8272',
|
||||
'Insert template...': '\u63d2\u5165\u6a21\u677f...',
|
||||
Templates: '\u6a21\u677f',
|
||||
Template: '\u6a21\u677f',
|
||||
'Text color': '\u6587\u5b57\u989c\u8272',
|
||||
'Background color': '\u80cc\u666f\u8272',
|
||||
'Custom...': '\u81ea\u5b9a\u4e49...',
|
||||
'Custom color': '\u81ea\u5b9a\u4e49\u989c\u8272',
|
||||
'No color': '\u65e0',
|
||||
'Remove color': '\u79fb\u9664\u989c\u8272',
|
||||
'Table of Contents': '\u5185\u5bb9\u5217\u8868',
|
||||
'Show blocks': '\u663e\u793a\u533a\u5757\u8fb9\u6846',
|
||||
'Show invisible characters': '\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26',
|
||||
'Word count': '\u5b57\u6570',
|
||||
Count: '\u8ba1\u6570',
|
||||
Document: '\u6587\u6863',
|
||||
Selection: '\u9009\u62e9',
|
||||
Words: '\u5355\u8bcd',
|
||||
'Words: {0}': '\u5b57\u6570\uff1a{0}',
|
||||
'{0} words': '{0} \u5b57',
|
||||
File: '\u6587\u4ef6',
|
||||
Edit: '\u7f16\u8f91',
|
||||
Insert: '\u63d2\u5165',
|
||||
View: '\u89c6\u56fe',
|
||||
Format: '\u683c\u5f0f',
|
||||
Table: '\u8868\u683c',
|
||||
Tools: '\u5de5\u5177',
|
||||
'Powered by {0}': '\u7531{0}\u9a71\u52a8',
|
||||
'Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help':
|
||||
'\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9',
|
||||
'Image title': '\u56fe\u7247\u6807\u9898',
|
||||
'Border width': '\u8fb9\u6846\u5bbd\u5ea6',
|
||||
'Border style': '\u8fb9\u6846\u6837\u5f0f',
|
||||
Error: '\u9519\u8bef',
|
||||
Warn: '\u8b66\u544a',
|
||||
Valid: '\u6709\u6548',
|
||||
'To open the popup, press Shift+Enter':
|
||||
'\u6309Shitf+Enter\u952e\u6253\u5f00\u5bf9\u8bdd\u6846',
|
||||
'Rich Text Area. Press ALT-0 for help.':
|
||||
'\u7f16\u8f91\u533a\u3002\u6309Alt+0\u952e\u6253\u5f00\u5e2e\u52a9\u3002',
|
||||
'System Font': '\u7cfb\u7edf\u5b57\u4f53',
|
||||
'Failed to upload image: {0}': '\u56fe\u7247\u4e0a\u4f20\u5931\u8d25: {0}',
|
||||
'Failed to load plugin: {0} from url {1}':
|
||||
'\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25: {0} \u6765\u81ea\u94fe\u63a5 {1}',
|
||||
'Failed to load plugin url: {0}':
|
||||
'\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25 \u94fe\u63a5: {0}',
|
||||
'Failed to initialize plugin: {0}':
|
||||
'\u63d2\u4ef6\u521d\u59cb\u5316\u5931\u8d25: {0}',
|
||||
example: '\u793a\u4f8b',
|
||||
Search: '\u641c\u7d22',
|
||||
All: '\u5168\u90e8',
|
||||
Currency: '\u8d27\u5e01',
|
||||
Text: '\u6587\u5b57',
|
||||
Quotations: '\u5f15\u7528',
|
||||
Mathematical: '\u6570\u5b66',
|
||||
'Extended Latin': '\u62c9\u4e01\u8bed\u6269\u5145',
|
||||
Symbols: '\u7b26\u53f7',
|
||||
Arrows: '\u7bad\u5934',
|
||||
'User Defined': '\u81ea\u5b9a\u4e49',
|
||||
'dollar sign': '\u7f8e\u5143\u7b26\u53f7',
|
||||
'currency sign': '\u8d27\u5e01\u7b26\u53f7',
|
||||
'euro-currency sign': '\u6b27\u5143\u7b26\u53f7',
|
||||
'colon sign': '\u5192\u53f7',
|
||||
'cruzeiro sign': '\u514b\u9c81\u8d5b\u7f57\u5e01\u7b26\u53f7',
|
||||
'french franc sign': '\u6cd5\u90ce\u7b26\u53f7',
|
||||
'lira sign': '\u91cc\u62c9\u7b26\u53f7',
|
||||
'mill sign': '\u5bc6\u5c14\u7b26\u53f7',
|
||||
'naira sign': '\u5948\u62c9\u7b26\u53f7',
|
||||
'peseta sign': '\u6bd4\u585e\u5854\u7b26\u53f7',
|
||||
'rupee sign': '\u5362\u6bd4\u7b26\u53f7',
|
||||
'won sign': '\u97e9\u5143\u7b26\u53f7',
|
||||
'new sheqel sign': '\u65b0\u8c22\u514b\u5c14\u7b26\u53f7',
|
||||
'dong sign': '\u8d8a\u5357\u76fe\u7b26\u53f7',
|
||||
'kip sign': '\u8001\u631d\u57fa\u666e\u7b26\u53f7',
|
||||
'tugrik sign': '\u56fe\u683c\u91cc\u514b\u7b26\u53f7',
|
||||
'drachma sign': '\u5fb7\u62c9\u514b\u9a6c\u7b26\u53f7',
|
||||
'german penny symbol': '\u5fb7\u56fd\u4fbf\u58eb\u7b26\u53f7',
|
||||
'peso sign': '\u6bd4\u7d22\u7b26\u53f7',
|
||||
'guarani sign': '\u74dc\u62c9\u5c3c\u7b26\u53f7',
|
||||
'austral sign': '\u6fb3\u5143\u7b26\u53f7',
|
||||
'hryvnia sign': '\u683c\u91cc\u592b\u5c3c\u4e9a\u7b26\u53f7',
|
||||
'cedi sign': '\u585e\u5730\u7b26\u53f7',
|
||||
'livre tournois sign': '\u91cc\u5f17\u5f17\u5c14\u7b26\u53f7',
|
||||
'spesmilo sign': 'spesmilo\u7b26\u53f7',
|
||||
'tenge sign': '\u575a\u6208\u7b26\u53f7',
|
||||
'indian rupee sign': '\u5370\u5ea6\u5362\u6bd4',
|
||||
'turkish lira sign': '\u571f\u8033\u5176\u91cc\u62c9',
|
||||
'nordic mark sign': '\u5317\u6b27\u9a6c\u514b',
|
||||
'manat sign': '\u9a6c\u7eb3\u7279\u7b26\u53f7',
|
||||
'ruble sign': '\u5362\u5e03\u7b26\u53f7',
|
||||
'yen character': '\u65e5\u5143\u5b57\u6837',
|
||||
'yuan character': '\u4eba\u6c11\u5e01\u5143\u5b57\u6837',
|
||||
'yuan character, in hong kong and taiwan':
|
||||
'\u5143\u5b57\u6837\uff08\u6e2f\u53f0\u5730\u533a\uff09',
|
||||
'yen/yuan character variant one':
|
||||
'\u5143\u5b57\u6837\uff08\u5927\u5199\uff09',
|
||||
'Loading emoticons...': '\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7...',
|
||||
'Could not load emoticons':
|
||||
'\u4e0d\u80fd\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7',
|
||||
People: '\u4eba\u7c7b',
|
||||
'Animals and Nature': '\u52a8\u7269\u548c\u81ea\u7136',
|
||||
'Food and Drink': '\u98df\u7269\u548c\u996e\u54c1',
|
||||
Activity: '\u6d3b\u52a8',
|
||||
'Travel and Places': '\u65c5\u6e38\u548c\u5730\u70b9',
|
||||
Objects: '\u7269\u4ef6',
|
||||
Flags: '\u65d7\u5e1c',
|
||||
Characters: '\u5b57\u7b26',
|
||||
'Characters (no spaces)': '\u5b57\u7b26(\u65e0\u7a7a\u683c)',
|
||||
'{0} characters': '{0} \u4e2a\u5b57\u7b26',
|
||||
'Error: Form submit field collision.':
|
||||
'\u9519\u8bef: \u8868\u5355\u63d0\u4ea4\u5b57\u6bb5\u51b2\u7a81\u3002',
|
||||
'Error: No form element found.':
|
||||
'\u9519\u8bef: \u6ca1\u6709\u8868\u5355\u63a7\u4ef6\u3002',
|
||||
Update: '\u66f4\u65b0',
|
||||
'Color swatch': '\u989c\u8272\u6837\u672c',
|
||||
Turquoise: '\u9752\u7eff\u8272',
|
||||
Green: '\u7eff\u8272',
|
||||
Blue: '\u84dd\u8272',
|
||||
Purple: '\u7d2b\u8272',
|
||||
'Navy Blue': '\u6d77\u519b\u84dd',
|
||||
'Dark Turquoise': '\u6df1\u84dd\u7eff\u8272',
|
||||
'Dark Green': '\u6df1\u7eff\u8272',
|
||||
'Medium Blue': '\u4e2d\u84dd\u8272',
|
||||
'Medium Purple': '\u4e2d\u7d2b\u8272',
|
||||
'Midnight Blue': '\u6df1\u84dd\u8272',
|
||||
Yellow: '\u9ec4\u8272',
|
||||
Orange: '\u6a59\u8272',
|
||||
Red: '\u7ea2\u8272',
|
||||
'Light Gray': '\u6d45\u7070\u8272',
|
||||
Gray: '\u7070\u8272',
|
||||
'Dark Yellow': '\u6697\u9ec4\u8272',
|
||||
'Dark Orange': '\u6df1\u6a59\u8272',
|
||||
'Dark Red': '\u6df1\u7ea2\u8272',
|
||||
'Medium Gray': '\u4e2d\u7070\u8272',
|
||||
'Dark Gray': '\u6df1\u7070\u8272',
|
||||
'Light Green': '\u6d45\u7eff\u8272',
|
||||
'Light Yellow': '\u6d45\u9ec4\u8272',
|
||||
'Light Red': '\u6d45\u7ea2\u8272',
|
||||
'Light Purple': '\u6d45\u7d2b\u8272',
|
||||
'Light Blue': '\u6d45\u84dd\u8272',
|
||||
'Dark Purple': '\u6df1\u7d2b\u8272',
|
||||
'Dark Blue': '\u6df1\u84dd\u8272',
|
||||
Black: '\u9ed1\u8272',
|
||||
White: '\u767d\u8272',
|
||||
'Switch to or from fullscreen mode': '\u5207\u6362\u5168\u5c4f\u6a21\u5f0f',
|
||||
'Open help dialog': '\u6253\u5f00\u5e2e\u52a9\u5bf9\u8bdd\u6846',
|
||||
history: '\u5386\u53f2',
|
||||
styles: '\u6837\u5f0f',
|
||||
formatting: '\u683c\u5f0f\u5316',
|
||||
alignment: '\u5bf9\u9f50',
|
||||
indentation: '\u7f29\u8fdb',
|
||||
'permanent pen': '\u8bb0\u53f7\u7b14',
|
||||
comments: '\u5907\u6ce8',
|
||||
'Format Painter': '\u683c\u5f0f\u5237',
|
||||
'Insert/edit iframe': '\u63d2\u5165/\u7f16\u8f91\u6846\u67b6',
|
||||
Capitalization: '\u5927\u5199',
|
||||
lowercase: '\u5c0f\u5199',
|
||||
UPPERCASE: '\u5927\u5199',
|
||||
'Title Case': '\u9996\u5b57\u6bcd\u5927\u5199',
|
||||
'Permanent Pen Properties': '\u6c38\u4e45\u7b14\u5c5e\u6027',
|
||||
'Permanent pen properties...': '\u6c38\u4e45\u7b14\u5c5e\u6027...',
|
||||
Font: '\u5b57\u4f53',
|
||||
Size: '\u5b57\u53f7',
|
||||
'More...': '\u66f4\u591a...',
|
||||
'Spellcheck Language': '\u62fc\u5199\u68c0\u67e5\u8bed\u8a00',
|
||||
'Select...': '\u9009\u62e9...',
|
||||
Preferences: '\u9996\u9009\u9879',
|
||||
Yes: '\u662f',
|
||||
No: '\u5426',
|
||||
'Keyboard Navigation': '\u952e\u76d8\u6307\u5f15',
|
||||
Version: '\u7248\u672c',
|
||||
Anchor: '\u951a\u70b9',
|
||||
'Special character': '\u7279\u6b8a\u7b26\u53f7',
|
||||
'Code sample': '\u4ee3\u7801\u793a\u4f8b',
|
||||
Color: '\u989c\u8272',
|
||||
Emoticons: '\u8868\u60c5',
|
||||
'Document properties': '\u6587\u6863\u5c5e\u6027',
|
||||
Image: '\u56fe\u7247',
|
||||
'Insert link': '\u63d2\u5165\u94fe\u63a5',
|
||||
Target: '\u6253\u5f00\u65b9\u5f0f',
|
||||
Link: '\u94fe\u63a5',
|
||||
Poster: '\u5c01\u9762',
|
||||
Media: '\u5a92\u4f53',
|
||||
Print: '\u6253\u5370',
|
||||
Prev: '\u4e0a\u4e00\u4e2a',
|
||||
'Find and replace': '\u67e5\u627e\u548c\u66ff\u6362',
|
||||
'Whole words': '\u5168\u5b57\u5339\u914d',
|
||||
Spellcheck: '\u62fc\u5199\u68c0\u67e5',
|
||||
Caption: '\u6807\u9898',
|
||||
'Insert template': '\u63d2\u5165\u6a21\u677f',
|
||||
});
|
||||
550
src/renderer/DataPreview.tsx
Normal file
550
src/renderer/DataPreview.tsx
Normal file
@@ -0,0 +1,550 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { FormItem, FormControlProps, getVariable, isEffectiveApi, Payload, render } from 'amis';
|
||||
import Form from '@rjsf/fluent-ui';
|
||||
import _ from 'lodash';
|
||||
@FormItem({
|
||||
test: /(^|\/)datapreview$/,
|
||||
name: 'datapreview',
|
||||
})
|
||||
export class DataPreviewRenderer extends React.Component<
|
||||
FormControlProps,
|
||||
{
|
||||
data_template: any;
|
||||
result_template: any;
|
||||
trial_data: any;
|
||||
trial_result: any;
|
||||
student: any;
|
||||
data: any;
|
||||
data_id: any;
|
||||
u2p_id: any;
|
||||
canNext: boolean;
|
||||
}
|
||||
> {
|
||||
trialInterval: any;
|
||||
trial_data: any;
|
||||
trial_result: any;
|
||||
dataTemplate: any;
|
||||
resultTemplate: any;
|
||||
constructor(props: FormControlProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
data_template: {},
|
||||
result_template: {},
|
||||
trial_data: {},
|
||||
trial_result: {},
|
||||
student: {},
|
||||
data: {},
|
||||
data_id: null,
|
||||
u2p_id: null,
|
||||
canNext: true,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { data } = this.props;
|
||||
const { data_template, result_template } = this.state;
|
||||
|
||||
this.fetchData(getVariable(data, 'id'));
|
||||
// this.trialInterval = setInterval(() => {
|
||||
// if (
|
||||
// store?.data &&
|
||||
// (!_.isEqual(this.dataTemplate, store?.data.data) ||
|
||||
// !_.isEqual(this.resultTemplate, store?.data.result))
|
||||
// ) {
|
||||
// this.dataTemplate = store?.data.data;
|
||||
// this.resultTemplate = store?.data.result;
|
||||
// this.setState({
|
||||
// data_template: store?.data.data,
|
||||
// result_template: store?.data.result,
|
||||
// });
|
||||
// }
|
||||
// }, 1000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.trialInterval) {
|
||||
clearInterval(this.trialInterval);
|
||||
}
|
||||
}
|
||||
|
||||
fetchData(u2p_id) {
|
||||
const { env, api, dataApi, data } = this.props;
|
||||
|
||||
let project_code = getVariable(data, 'projects.code');
|
||||
|
||||
if (isEffectiveApi(api)) {
|
||||
env
|
||||
.fetcher(api, { project_code: project_code })
|
||||
.then((payload: Payload) => {
|
||||
if (payload.data) {
|
||||
this.dataTemplate = payload.data.data;
|
||||
this.resultTemplate = payload.data.result;
|
||||
this.setState({
|
||||
data_template: payload.data.data,
|
||||
result_template: payload.data.result,
|
||||
});
|
||||
}
|
||||
});
|
||||
env
|
||||
.fetcher(dataApi, { user2project_id: u2p_id })
|
||||
.then((payload: Payload) => {
|
||||
if (payload.data) {
|
||||
this.setState({
|
||||
data: payload.data,
|
||||
trial_data: payload.data.data,
|
||||
trial_result: payload.data.result,
|
||||
data_id: payload.data.id,
|
||||
u2p_id: u2p_id,
|
||||
student: payload.data.user2projects.users,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getConst(node: any, index: any) {
|
||||
let i = index;
|
||||
if (node.type === 'string') {
|
||||
let tpl_value = node.default ? node.default : '';
|
||||
while (
|
||||
tpl_value &&
|
||||
tpl_value.match(/{{(.*?)}}/) &&
|
||||
tpl_value.match(/{{(.*?)}}/).length === 2
|
||||
) {
|
||||
try {
|
||||
tpl_value = tpl_value.replace(
|
||||
/({{(.*?)}})/,
|
||||
`${eval(tpl_value.match(/{{(.*?)}}/)[1])}`
|
||||
);
|
||||
} catch (error) {
|
||||
tpl_value = '';
|
||||
}
|
||||
}
|
||||
return tpl_value;
|
||||
} else if (node.type === 'object') {
|
||||
if (node.children && Array.isArray(node.children.data)) {
|
||||
let o: any = {};
|
||||
|
||||
node.children.data.forEach((item: any) => {
|
||||
o[item.name] = this.getConst(item, index);
|
||||
});
|
||||
return o;
|
||||
}
|
||||
} else if (node.type === 'array') {
|
||||
let a: Array<any> = [];
|
||||
if (node.children && Array.isArray(node.children.data)) {
|
||||
node.children.data.forEach((item: any) => {
|
||||
if (item.config && item.config.max) {
|
||||
for (let i = 0; i < item.config.max; i++) {
|
||||
a.push(this.getConst(item, i));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
{
|
||||
if (node.children && this.isValid(node.children.data)) {
|
||||
a.push(this.getConst(node.children.data, index));
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
// 通用的处理表达式方法
|
||||
// 这里打破 JSON Schema 规范
|
||||
handleExpression(
|
||||
rootFormData: any,
|
||||
curNodePath: string,
|
||||
expression: string,
|
||||
fallBack: Function
|
||||
) {
|
||||
const regExpression = /{{(.*)}}/;
|
||||
// 未配置
|
||||
if (undefined === expression) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// 配置了 mustache 表达式
|
||||
const matchExpression = regExpression.exec(expression);
|
||||
regExpression.lastIndex = 0; // 重置索引
|
||||
if (matchExpression) {
|
||||
try {
|
||||
const code = matchExpression[1].trim();
|
||||
|
||||
// eslint-disable-next-line no-new-func
|
||||
const fn = new Function(
|
||||
'parentFormData',
|
||||
'rootFormData',
|
||||
`return ${code}`
|
||||
);
|
||||
|
||||
let result = fn(
|
||||
this.getPathVal(rootFormData, curNodePath, 1),
|
||||
rootFormData
|
||||
);
|
||||
|
||||
return result ? result : '';
|
||||
} catch (error) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// 回退
|
||||
return fallBack();
|
||||
}
|
||||
|
||||
// 获取当前path值
|
||||
getPathVal(obj: any, path: string, leftDeviation = 0) {
|
||||
const pathSeparator = '.';
|
||||
const pathArr = path.split(pathSeparator);
|
||||
|
||||
for (let i = 0; i < pathArr.length - leftDeviation; i += 1) {
|
||||
// 错误路径或者undefined中断查找
|
||||
if (obj === undefined) return undefined;
|
||||
obj = pathArr[i] === '' ? obj : obj[pathArr[i]];
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
buildSchema(node: any): any {
|
||||
let title = node.title;
|
||||
// while(title && title.match(/{{(.*?)}}/) && title.match(/{{(.*?)}}/).length === 2) {
|
||||
// title = title.replace(/({{(.*?)}})/, `${eval(title.match(/{{(.*?)}}/)[1])}`)
|
||||
// }
|
||||
if (node.type === 'string') {
|
||||
let defaultValue = this.getConst(node, 0);
|
||||
return Object.assign(
|
||||
{},
|
||||
{
|
||||
type: node.type,
|
||||
title: title,
|
||||
required: true,
|
||||
disabled: node.disabled,
|
||||
computed: node.computed,
|
||||
template: node.config ? node.config.template : '',
|
||||
|
||||
default: defaultValue ? defaultValue : '',
|
||||
}
|
||||
);
|
||||
} else if (node.type === 'object') {
|
||||
let nodeProperty = Object.assign({
|
||||
type: node.type,
|
||||
title: title,
|
||||
properties: {},
|
||||
});
|
||||
if (Array.isArray(node.children.data)) {
|
||||
node.children.data.forEach((item: any) => {
|
||||
if (this.isValid(item)) {
|
||||
nodeProperty.properties[item.name] = this.buildSchema(item);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (this.isValid(node.children.data)) {
|
||||
nodeProperty.properties[node.children.data.name] = this.buildSchema(
|
||||
node.children.data
|
||||
);
|
||||
}
|
||||
}
|
||||
return nodeProperty;
|
||||
} else if (node.type === 'array') {
|
||||
let defaultValue: Array<any> = [];
|
||||
if (node.config && node.config.min === node.config.max) {
|
||||
for (let i = 0; i < node.config.max; i++) {
|
||||
defaultValue.push(this.getConst(node.children.data, i));
|
||||
}
|
||||
} else if (node.config && node.config.min < node.config.max) {
|
||||
for (let i = 0; i < node.config.min; i++) {
|
||||
defaultValue.push(this.getConst(node.children.data, i));
|
||||
}
|
||||
}
|
||||
|
||||
return Object.assign({
|
||||
type: node.type,
|
||||
title: title,
|
||||
maxItems: node.config && node.config.max ? node.config.max : 100,
|
||||
minItems: node.config && node.config.min ? node.config.min : 0,
|
||||
default: defaultValue ? defaultValue : [],
|
||||
items: this.buildSchema(node.children.data),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
buildUISchema(properties: any) {
|
||||
if (!properties) {
|
||||
return [];
|
||||
}
|
||||
let uiSchema: any = {};
|
||||
Object.keys(properties).forEach((key) => {
|
||||
uiSchema[key] = {};
|
||||
if (properties[key].type === 'string') {
|
||||
uiSchema[key] = {
|
||||
'ui:disabled': true,
|
||||
};
|
||||
} else if (properties[key].type === 'object') {
|
||||
uiSchema[key] = this.buildUISchema(properties[key].properties);
|
||||
} else if (properties[key].type === 'array') {
|
||||
uiSchema[key] = {
|
||||
items: this.buildUISchema(properties[key].items.properties),
|
||||
'ui:options': {
|
||||
addable: true,
|
||||
removable: !(properties[key].minItems === properties[key].maxItems),
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
return uiSchema;
|
||||
}
|
||||
|
||||
computeData(properties: any, data: any) {
|
||||
if (!properties) {
|
||||
return null;
|
||||
}
|
||||
Object.keys(properties).forEach((key: any) => {
|
||||
if (properties[key] === undefined) {
|
||||
return data;
|
||||
}
|
||||
if (properties[key].type === 'string') {
|
||||
if (properties[key].computed === true) {
|
||||
data[key] = this.handleExpression(
|
||||
data,
|
||||
'',
|
||||
properties[key].template,
|
||||
() => {}
|
||||
);
|
||||
}
|
||||
} else if (properties[key].type === 'object') {
|
||||
data[key] = this.computeData(properties[key].properties, data[key]);
|
||||
} else if (properties[key].type === 'array') {
|
||||
data[key].forEach((subData: any, i: number) => {
|
||||
data[key][i] = this.computeData(
|
||||
properties[key].items.properties,
|
||||
subData
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
isValid(node: any) {
|
||||
if (_.has(node, 'type')) {
|
||||
if (node.type === 'string') {
|
||||
return _.has(node, 'name') && _.has(node, 'title');
|
||||
}
|
||||
if (node.type === 'object') {
|
||||
return (
|
||||
_.has(node, 'name') &&
|
||||
_.has(node, 'title') &&
|
||||
_.has(node, 'children.data') &&
|
||||
_.isArray(node.children.data)
|
||||
);
|
||||
}
|
||||
if (node.type === 'array') {
|
||||
return (
|
||||
_.has(node, 'name') &&
|
||||
_.has(node, 'title') &&
|
||||
_.has(node, 'children.data') &&
|
||||
_.has(node, 'children.data.type')
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { env, data, dataApi, onChange, className, min, max, step } =
|
||||
this.props;
|
||||
const { data_template, result_template, trial_data, trial_result } =
|
||||
this.state;
|
||||
let templateSchemaProperties = Object.assign({});
|
||||
let resultSchemaProperties = Object.assign({});
|
||||
let students = getVariable(data, 'items');
|
||||
let curIndex = this.state.student
|
||||
? students.findIndex((s) => s.student.code === this.state.student.code)
|
||||
: students.findIndex((s) => s.data && s.data.status === 'data_commit');
|
||||
|
||||
if (data_template && _.isArray(data_template.data)) {
|
||||
data_template.data.forEach((item: any) => {
|
||||
if (this.isValid(item)) {
|
||||
templateSchemaProperties[item.name] = this.buildSchema(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (result_template && _.isArray(result_template.data)) {
|
||||
result_template.data.forEach((item: any) => {
|
||||
if (this.isValid(item)) {
|
||||
resultSchemaProperties[item.name] = this.buildSchema(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
let dataSchema = {
|
||||
title: '原始数据',
|
||||
description: data_template && data_template.description,
|
||||
properties: templateSchemaProperties,
|
||||
};
|
||||
// let resultSchema = {
|
||||
// title: '结果数据',
|
||||
// description: result_template && result_template.description,
|
||||
// properties: resultSchemaProperties,
|
||||
// };
|
||||
let uiSchema = this.buildUISchema(dataSchema.properties);
|
||||
// let resultUiSchema = this.buildUISchema(resultSchema.properties);
|
||||
|
||||
const style = {
|
||||
// overflow: 'auto',
|
||||
// height: '700px',
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* <div className="">
|
||||
{`${this.state.student.name}(${this.state.student.code})`}
|
||||
</div> */}
|
||||
|
||||
{/* <Button
|
||||
level={'success'}
|
||||
disabled={curIndex === -1}
|
||||
onClick={() => {
|
||||
env
|
||||
.fetcher({
|
||||
method: 'patch',
|
||||
url: `rest/data/${this.state.data_id}`,
|
||||
data: {
|
||||
status: 'data_verified',
|
||||
},
|
||||
dataType: 'json',
|
||||
})
|
||||
.then((res) => {
|
||||
env.notify('success', '数据通过成功');
|
||||
});
|
||||
}}
|
||||
>
|
||||
通过
|
||||
</Button>
|
||||
<Button
|
||||
level={'danger'}
|
||||
disabled={curIndex === -1}
|
||||
onClick={() => {
|
||||
env
|
||||
.fetcher({
|
||||
method: 'patch',
|
||||
url: `rest/data/${this.state.data_id}`,
|
||||
data: {
|
||||
status: 'data_refused',
|
||||
},
|
||||
dataType: 'json',
|
||||
})
|
||||
.then((res) => {
|
||||
env.notify('success', '数据退回成功');
|
||||
});
|
||||
}}
|
||||
>
|
||||
退回
|
||||
</Button>
|
||||
<Button
|
||||
disabled={
|
||||
curIndex === -1 ||
|
||||
(this.dataTemplate && !this.dataTemplate.verification_required)
|
||||
}
|
||||
onClick={() => {
|
||||
env.fetcher({
|
||||
method: 'post',
|
||||
url: `/report-api/data/${this.state.data_id}/verify`,
|
||||
data: {
|
||||
id: this.state.data_id,
|
||||
},
|
||||
dataType: 'form',
|
||||
});
|
||||
}}
|
||||
>
|
||||
自动校验
|
||||
</Button>
|
||||
<Button
|
||||
disabled={curIndex === -1}
|
||||
onClick={() => {
|
||||
do {
|
||||
if (curIndex < students.length) {
|
||||
curIndex++;
|
||||
} else {
|
||||
curIndex = 0;
|
||||
}
|
||||
} while (
|
||||
!students[curIndex].data ||
|
||||
students[curIndex].data.length === 0 ||
|
||||
students[curIndex].data.status !== 'data_commit'
|
||||
);
|
||||
env
|
||||
.fetcher(dataApi, { user2project_id: students[curIndex].id })
|
||||
.then((payload: Payload) => {
|
||||
if (payload.data) {
|
||||
this.setState({
|
||||
trial_data: payload.data.data,
|
||||
trial_result: payload.data.result,
|
||||
student: payload.data.user2projects.users,
|
||||
});
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
下一个
|
||||
</Button> */}
|
||||
<div {...{ style }}>
|
||||
<Form
|
||||
schema={dataSchema}
|
||||
uiSchema={uiSchema}
|
||||
formData={trial_data}
|
||||
ArrayFieldTemplate={(props) => {
|
||||
let columns = Object.keys(
|
||||
props.items[0].children.props.uiSchema
|
||||
).map((o, index) => {
|
||||
return {
|
||||
name: o,
|
||||
label: props.schema.items['properties'][o].title,
|
||||
};
|
||||
});
|
||||
return render({
|
||||
type: 'table',
|
||||
title: props.title,
|
||||
items: props.formData,
|
||||
columns: columns,
|
||||
});
|
||||
}}
|
||||
onSubmit={() => {
|
||||
console.log('submit');
|
||||
}}
|
||||
onError={() => {
|
||||
console.log('errors');
|
||||
}}
|
||||
>
|
||||
<Fragment />
|
||||
</Form>
|
||||
{/* <Form
|
||||
schema={resultSchema}
|
||||
uiSchema={resultUiSchema}
|
||||
formData={trial_result}
|
||||
ArrayFieldTemplate={(props) => {
|
||||
return (
|
||||
<div>
|
||||
{props.items.map((element) => element.children)}
|
||||
{props.canAdd && (
|
||||
<button type="button" onClick={props.onAddClick}></button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
onSubmit={() => {
|
||||
console.log('submit');
|
||||
}}
|
||||
onError={() => {
|
||||
console.log('errors');
|
||||
}}
|
||||
>
|
||||
<Fragment />
|
||||
</Form> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
479
src/renderer/DataPreview1.tsx
Normal file
479
src/renderer/DataPreview1.tsx
Normal file
@@ -0,0 +1,479 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { Button, classnames, Modal, FormItem } from 'amis';
|
||||
import { FormControlProps } from 'amis/lib/renderers/Form/Item';
|
||||
import { Renderer, RendererProps } from 'amis/lib/factory';
|
||||
import { getVariable } from 'amis/lib/utils/helper';
|
||||
import Form from '@rjsf/fluent-ui';
|
||||
import _ from 'lodash';
|
||||
import { isEffectiveApi } from 'amis/lib/utils/api';
|
||||
import { Payload } from 'amis/lib/types';
|
||||
|
||||
@FormItem({
|
||||
test: /(^|\/)datapreview1$/,
|
||||
name: 'datapreview1',
|
||||
})
|
||||
export class DataPreview1Renderer extends React.Component<
|
||||
FormControlProps,
|
||||
{
|
||||
visible: boolean;
|
||||
data_template: any;
|
||||
result_template: any;
|
||||
trial_data: any;
|
||||
trial_result: any;
|
||||
}
|
||||
> {
|
||||
trialInterval: any;
|
||||
trial_data: any;
|
||||
trial_result: any;
|
||||
dataTemplate: any;
|
||||
resultTemplate: any;
|
||||
constructor(props: FormControlProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
visible: true,
|
||||
data_template: {},
|
||||
result_template: {},
|
||||
trial_data: {},
|
||||
trial_result: {},
|
||||
};
|
||||
}
|
||||
async componentDidMount() {
|
||||
const { env, api, dataApi, store, data } = this.props;
|
||||
const { data_template, result_template } = this.state;
|
||||
|
||||
let user2project_id = getVariable(data, 'id');
|
||||
let project_code = getVariable(data, 'projects.code');
|
||||
|
||||
if (isEffectiveApi(api)) {
|
||||
env
|
||||
.fetcher(api, { project_code: project_code })
|
||||
.then((payload: Payload) => {
|
||||
if (payload.data) {
|
||||
this.dataTemplate = payload.data.data;
|
||||
this.resultTemplate = payload.data.result;
|
||||
this.setState({
|
||||
data_template: payload.data.data,
|
||||
result_template: payload.data.result,
|
||||
});
|
||||
}
|
||||
});
|
||||
env.fetcher(dataApi, { user2project_id }).then((payload: Payload) => {
|
||||
if (payload.data) {
|
||||
this.setState({
|
||||
trial_data: payload.data.data,
|
||||
trial_result: payload.data.result,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.trialInterval) {
|
||||
clearInterval(this.trialInterval);
|
||||
}
|
||||
}
|
||||
|
||||
getConst(node: any, index: any) {
|
||||
let i = index;
|
||||
if (node.type === 'string') {
|
||||
let tpl_value = node.default ? node.default : '';
|
||||
while (
|
||||
tpl_value &&
|
||||
tpl_value.match(/{{(.*?)}}/) &&
|
||||
tpl_value.match(/{{(.*?)}}/).length === 2
|
||||
) {
|
||||
try {
|
||||
tpl_value = tpl_value.replace(
|
||||
/({{(.*?)}})/,
|
||||
`${eval(tpl_value.match(/{{(.*?)}}/)[1])}`
|
||||
);
|
||||
} catch (error) {
|
||||
tpl_value = '';
|
||||
}
|
||||
}
|
||||
return tpl_value;
|
||||
} else if (node.type === 'object') {
|
||||
if (node.children && Array.isArray(node.children.data)) {
|
||||
let o: any = {};
|
||||
|
||||
node.children.data.forEach((item: any) => {
|
||||
o[item.name] = this.getConst(item, index);
|
||||
});
|
||||
return o;
|
||||
}
|
||||
} else if (node.type === 'array') {
|
||||
let a: Array<any> = [];
|
||||
if (node.children && Array.isArray(node.children.data)) {
|
||||
node.children.data.forEach((item: any) => {
|
||||
if (item.config && item.config.max) {
|
||||
for (let i = 0; i < item.config.max; i++) {
|
||||
a.push(this.getConst(item, i));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
{
|
||||
if (node.children && this.isValid(node.children.data)) {
|
||||
a.push(this.getConst(node.children.data, index));
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
// 通用的处理表达式方法
|
||||
// 这里打破 JSON Schema 规范
|
||||
handleExpression(
|
||||
rootFormData: any,
|
||||
curNodePath: string,
|
||||
expression: string,
|
||||
fallBack: Function
|
||||
) {
|
||||
const regExpression = /{{(.*)}}/;
|
||||
// 未配置
|
||||
if (undefined === expression) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// 配置了 mustache 表达式
|
||||
const matchExpression = regExpression.exec(expression);
|
||||
regExpression.lastIndex = 0; // 重置索引
|
||||
if (matchExpression) {
|
||||
try {
|
||||
const code = matchExpression[1].trim();
|
||||
|
||||
// eslint-disable-next-line no-new-func
|
||||
const fn = new Function(
|
||||
'parentFormData',
|
||||
'rootFormData',
|
||||
`return ${code}`
|
||||
);
|
||||
|
||||
let result = fn(
|
||||
this.getPathVal(rootFormData, curNodePath, 1),
|
||||
rootFormData
|
||||
);
|
||||
|
||||
return result ? result : '';
|
||||
} catch (error) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// 回退
|
||||
return fallBack();
|
||||
}
|
||||
|
||||
// 获取当前path值
|
||||
getPathVal(obj: any, path: string, leftDeviation = 0) {
|
||||
const pathSeparator = '.';
|
||||
const pathArr = path.split(pathSeparator);
|
||||
|
||||
for (let i = 0; i < pathArr.length - leftDeviation; i += 1) {
|
||||
// 错误路径或者undefined中断查找
|
||||
if (obj === undefined) return undefined;
|
||||
obj = pathArr[i] === '' ? obj : obj[pathArr[i]];
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
buildSchema(node: any): any {
|
||||
let title = node.title;
|
||||
// while(title && title.match(/{{(.*?)}}/) && title.match(/{{(.*?)}}/).length === 2) {
|
||||
// title = title.replace(/({{(.*?)}})/, `${eval(title.match(/{{(.*?)}}/)[1])}`)
|
||||
// }
|
||||
if (node.type === 'string') {
|
||||
let defaultValue = this.getConst(node, 0);
|
||||
return Object.assign(
|
||||
{},
|
||||
{
|
||||
type: node.type,
|
||||
title: title,
|
||||
required: true,
|
||||
disabled: node.disabled,
|
||||
computed: node.computed,
|
||||
template: node.config ? node.config.template : '',
|
||||
|
||||
default: defaultValue ? defaultValue : '',
|
||||
}
|
||||
);
|
||||
} else if (node.type === 'object') {
|
||||
let nodeProperty = Object.assign({
|
||||
type: node.type,
|
||||
title: title,
|
||||
properties: {},
|
||||
});
|
||||
if (Array.isArray(node.children.data)) {
|
||||
node.children.data.forEach((item: any) => {
|
||||
if (this.isValid(item)) {
|
||||
nodeProperty.properties[item.name] = this.buildSchema(item);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (this.isValid(node.children.data)) {
|
||||
nodeProperty.properties[node.children.data.name] = this.buildSchema(
|
||||
node.children.data
|
||||
);
|
||||
}
|
||||
}
|
||||
return nodeProperty;
|
||||
} else if (node.type === 'array') {
|
||||
let defaultValue: Array<any> = [];
|
||||
if (node.config && node.config.min === node.config.max) {
|
||||
for (let i = 0; i < node.config.max; i++) {
|
||||
defaultValue.push(this.getConst(node.children.data, i));
|
||||
}
|
||||
} else if (node.config && node.config.min < node.config.max) {
|
||||
for (let i = 0; i < node.config.min; i++) {
|
||||
defaultValue.push(this.getConst(node.children.data, i));
|
||||
}
|
||||
}
|
||||
|
||||
return Object.assign({
|
||||
type: node.type,
|
||||
title: title,
|
||||
maxItems: node.config && node.config.max ? node.config.max : 100,
|
||||
minItems: node.config && node.config.min ? node.config.min : 0,
|
||||
default: defaultValue ? defaultValue : [],
|
||||
items: this.buildSchema(node.children.data),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
buildUISchema(properties: any) {
|
||||
if (!properties) {
|
||||
return [];
|
||||
}
|
||||
let uiSchema: any = {};
|
||||
Object.keys(properties).forEach((key) => {
|
||||
uiSchema[key] = {};
|
||||
if (properties[key].type === 'string') {
|
||||
uiSchema[key] = {
|
||||
'ui:disabled': true,
|
||||
};
|
||||
} else if (properties[key].type === 'object') {
|
||||
uiSchema[key] = this.buildUISchema(properties[key].properties);
|
||||
} else if (properties[key].type === 'array') {
|
||||
uiSchema[key] = {
|
||||
items: this.buildUISchema(properties[key].items.properties),
|
||||
'ui:options': {
|
||||
addable: true,
|
||||
removable: !(properties[key].minItems === properties[key].maxItems),
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
return uiSchema;
|
||||
}
|
||||
|
||||
computeData(properties: any, data: any) {
|
||||
if (!properties) {
|
||||
return null;
|
||||
}
|
||||
Object.keys(properties).forEach((key: any) => {
|
||||
if (properties[key] === undefined) {
|
||||
return data;
|
||||
}
|
||||
if (properties[key].type === 'string') {
|
||||
if (properties[key].computed === true) {
|
||||
data[key] = this.handleExpression(
|
||||
data,
|
||||
'',
|
||||
properties[key].template,
|
||||
() => {}
|
||||
);
|
||||
}
|
||||
} else if (properties[key].type === 'object') {
|
||||
data[key] = this.computeData(properties[key].properties, data[key]);
|
||||
} else if (properties[key].type === 'array') {
|
||||
data[key].forEach((subData: any, i: number) => {
|
||||
data[key][i] = this.computeData(
|
||||
properties[key].items.properties,
|
||||
subData
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
isValid(node: any) {
|
||||
if (_.has(node, 'type')) {
|
||||
if (node.type === 'string') {
|
||||
return _.has(node, 'name') && _.has(node, 'title');
|
||||
}
|
||||
if (node.type === 'object') {
|
||||
return (
|
||||
_.has(node, 'name') &&
|
||||
_.has(node, 'title') &&
|
||||
_.has(node, 'children.data') &&
|
||||
_.isArray(node.children.data)
|
||||
);
|
||||
}
|
||||
if (node.type === 'array') {
|
||||
return (
|
||||
_.has(node, 'name') &&
|
||||
_.has(node, 'title') &&
|
||||
_.has(node, 'children.data') &&
|
||||
_.has(node, 'children.data.type')
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
console.log(this.props.visable);
|
||||
const {
|
||||
env,
|
||||
data,
|
||||
onChange,
|
||||
className,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
label,
|
||||
level,
|
||||
size,
|
||||
block,
|
||||
overrideClassName,
|
||||
} = this.props;
|
||||
const { data_template, result_template, trial_data, trial_result } =
|
||||
this.state;
|
||||
let templateSchemaProperties = Object.assign({});
|
||||
let resultSchemaProperties = Object.assign({});
|
||||
|
||||
if (data_template && _.isArray(data_template.data)) {
|
||||
data_template.data.forEach((item: any) => {
|
||||
if (this.isValid(item)) {
|
||||
templateSchemaProperties[item.name] = this.buildSchema(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (result_template && _.isArray(result_template.data)) {
|
||||
result_template.data.forEach((item: any) => {
|
||||
if (this.isValid(item)) {
|
||||
resultSchemaProperties[item.name] = this.buildSchema(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
let dataSchema = {
|
||||
title: '原始数据',
|
||||
description: data_template && data_template.description,
|
||||
properties: templateSchemaProperties,
|
||||
};
|
||||
let resultSchema = {
|
||||
title: '结果数据',
|
||||
description: result_template && result_template.description,
|
||||
properties: resultSchemaProperties,
|
||||
};
|
||||
let uiSchema = this.buildUISchema(dataSchema.properties);
|
||||
let resultUiSchema = this.buildUISchema(resultSchema.properties);
|
||||
console.log(uiSchema);
|
||||
return (
|
||||
<>
|
||||
{this.props.visable ? (
|
||||
<Button
|
||||
level="primary"
|
||||
className={classnames(
|
||||
overrideClassName
|
||||
? ''
|
||||
: {
|
||||
Button: true,
|
||||
[`cxd-Button--${level}`]: level,
|
||||
[`cxd-Button--xs`]: size,
|
||||
[`cxd-Button--block`]: block,
|
||||
},
|
||||
className
|
||||
)}
|
||||
onClick={async () => {
|
||||
const { env, data } = this.props;
|
||||
const user2project_id = getVariable(data, 'id');
|
||||
const reports = getVariable(data, 'reports');
|
||||
const payload = await env.fetcher(
|
||||
`report-api/reports/${user2project_id}/url?type=submit`
|
||||
);
|
||||
let images = payload.data
|
||||
.replace('[', '')
|
||||
.replace(']', '')
|
||||
.split(',')
|
||||
.map((item: string) => {
|
||||
return {
|
||||
src: item,
|
||||
alt: '',
|
||||
};
|
||||
});
|
||||
let checkImages = [];
|
||||
if (
|
||||
Array.isArray(reports) &&
|
||||
reports.length > 0 &&
|
||||
reports[0].status === 'C'
|
||||
) {
|
||||
const payload2 = await env.fetcher(
|
||||
`report-api/reports/${user2project_id}/url?type=check`
|
||||
);
|
||||
checkImages = payload2.data
|
||||
.replace('[', '')
|
||||
.replace(']', '')
|
||||
.split(',')
|
||||
.map((item: string) => {
|
||||
return {
|
||||
src: item,
|
||||
alt: '',
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
visible: true,
|
||||
});
|
||||
this.setState({ visible: true });
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
<div className="device device-iphone-14-pro">
|
||||
<div className="device-frame">
|
||||
<Form
|
||||
schema={dataSchema}
|
||||
uiSchema={uiSchema}
|
||||
formData={trial_data}
|
||||
onSubmit={() => {
|
||||
console.log('submit');
|
||||
}}
|
||||
onError={() => {
|
||||
console.log('errors');
|
||||
}}
|
||||
>
|
||||
<Fragment />
|
||||
</Form>
|
||||
<Form
|
||||
schema={resultSchema}
|
||||
uiSchema={resultUiSchema}
|
||||
formData={trial_result}
|
||||
onSubmit={() => {
|
||||
console.log('submit');
|
||||
}}
|
||||
onError={() => {
|
||||
console.log('errors');
|
||||
}}
|
||||
>
|
||||
<Fragment />
|
||||
</Form>
|
||||
</div>
|
||||
<div className="device-stripe"></div>
|
||||
<div className="device-header"></div>
|
||||
<div className="device-sensors"></div>
|
||||
<div className="device-btns"></div>
|
||||
<div className="device-power"></div>
|
||||
<div className="device-home"></div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
590
src/renderer/DataSet.tsx
Normal file
590
src/renderer/DataSet.tsx
Normal file
@@ -0,0 +1,590 @@
|
||||
import React from 'react';
|
||||
import { Renderer, ScopedContext, Table, Button } from 'amis';
|
||||
import { RendererProps } from 'amis/lib/factory';
|
||||
|
||||
|
||||
@Renderer({
|
||||
test: /(^|\/)dataset\-renderer$/,
|
||||
name: 'dataset-renderer',
|
||||
})
|
||||
export class DataestRenderer extends React.Component<
|
||||
RendererProps,
|
||||
{
|
||||
source: any;
|
||||
breadcrumb: any;
|
||||
files: any;
|
||||
showAddFileDialog: any;
|
||||
showAddFolderDialog: any;
|
||||
showRenameDialog: any;
|
||||
isLoading: any;
|
||||
renameFile: string;
|
||||
}
|
||||
> {
|
||||
static contextType = ScopedContext;
|
||||
|
||||
constructor(props: RendererProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
source: [],
|
||||
breadcrumb: ['根目录'],
|
||||
files: [],
|
||||
showAddFileDialog: false,
|
||||
showAddFolderDialog: false,
|
||||
showRenameDialog: false,
|
||||
isLoading: false,
|
||||
renameFile: '',
|
||||
};
|
||||
}
|
||||
|
||||
fetchSource(prefix) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.props.env
|
||||
.fetcher({
|
||||
method: 'post',
|
||||
url: `/storage/v1/object/list/${this.props.bucket}`,
|
||||
data: {
|
||||
prefix: prefix ? prefix : '',
|
||||
sortBy: {
|
||||
column: 'name',
|
||||
order: 'asc',
|
||||
},
|
||||
},
|
||||
})
|
||||
.then((res) => resolve(res))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
}
|
||||
|
||||
async setSource(prefix) {
|
||||
let result: any = await this.fetchSource(prefix);
|
||||
let source = result.data
|
||||
.filter((item) => item.name.charAt(0) !== '.')
|
||||
.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
...item.metadata,
|
||||
mimetype: item.metadata
|
||||
? item.metadata.mimetype.split('/')[1]
|
||||
: '文件夹',
|
||||
size: item.metadata
|
||||
? '' + Math.floor(item.metadata.size / 1024) + 'KB'
|
||||
: null,
|
||||
updated_at: item.updated_at
|
||||
? item.updated_at.slice(0, 16).replace('T', ' ')
|
||||
: null,
|
||||
};
|
||||
});
|
||||
this.setState({ source });
|
||||
}
|
||||
|
||||
getPath(breadcrumb) {
|
||||
return breadcrumb.slice(1).toString().replaceAll(',', '/');
|
||||
}
|
||||
|
||||
async getContent(path, contents) {
|
||||
let res: any = await this.fetchSource(path);
|
||||
let actions = [];
|
||||
res.data.forEach((item) => {
|
||||
const subPath = path + '/' + item.name;
|
||||
|
||||
if (item.id) {
|
||||
contents.push(subPath);
|
||||
} else {
|
||||
actions.push(this.getContent(subPath, contents));
|
||||
}
|
||||
});
|
||||
await Promise.all(actions);
|
||||
return contents;
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const scoped = this.context;
|
||||
scoped.registerComponent(this);
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
this.setSource(null);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: any, prevState: any, snapshot: any) {
|
||||
if (
|
||||
JSON.stringify(prevState.breadcrumb) !==
|
||||
JSON.stringify(this.state.breadcrumb)
|
||||
) {
|
||||
this.reload();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const scoped = this.context;
|
||||
scoped.unRegisterComponent(this);
|
||||
}
|
||||
|
||||
reload() {
|
||||
this.setSource(this.getPath(this.state.breadcrumb));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span>
|
||||
{this.state.breadcrumb.map((item, index) => {
|
||||
if (index) {
|
||||
if (index === this.state.breadcrumb.length - 1) {
|
||||
return <span key={`${index}-${item}`}>/{item}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<span key={`${index}-${item}`}>
|
||||
/
|
||||
<a
|
||||
href="#"
|
||||
onClick={() => {
|
||||
let temp_breadcrumb = JSON.parse(
|
||||
JSON.stringify(this.state.breadcrumb)
|
||||
);
|
||||
temp_breadcrumb = temp_breadcrumb.slice(0, index + 1);
|
||||
this.setState({ breadcrumb: temp_breadcrumb });
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</a>
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
if (index === this.state.breadcrumb.length - 1) {
|
||||
return <span key={`${index}-${item}`}>{item}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
key={`${index}-${item}`}
|
||||
href="#"
|
||||
onClick={() => {
|
||||
let temp_breadcrumb = JSON.parse(
|
||||
JSON.stringify(this.state.breadcrumb)
|
||||
);
|
||||
temp_breadcrumb = temp_breadcrumb.slice(0, index + 1);
|
||||
this.setState({ breadcrumb: temp_breadcrumb });
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</span>
|
||||
<span>
|
||||
<Button
|
||||
level="primary"
|
||||
className={`fa fa-folder`}
|
||||
onClick={() => {
|
||||
const { addFolder } = this.props;
|
||||
const path = this.getPath(this.state.breadcrumb);
|
||||
this.props.onAction(
|
||||
null,
|
||||
addFolder,
|
||||
{ path },
|
||||
false,
|
||||
this.context
|
||||
);
|
||||
}}
|
||||
>
|
||||
新建文件夹
|
||||
</Button>
|
||||
<Button
|
||||
level="primary"
|
||||
className={`m-l-xs fa fa-upload`}
|
||||
onClick={() => this.setState({ showAddFileDialog: true })}
|
||||
>
|
||||
上传文件
|
||||
</Button>
|
||||
{/* <Button
|
||||
level="danger"
|
||||
className={`m-l-xs fa fa-trash`}
|
||||
tooltip="批量删除文件"
|
||||
onClick={() => {
|
||||
const { delFiles } = this.props;
|
||||
const path = this.getPath(this.state.breadcrumb);
|
||||
this.props.onAction(
|
||||
null,
|
||||
delFiles,
|
||||
{ path },
|
||||
false,
|
||||
this.context
|
||||
);
|
||||
}}
|
||||
>
|
||||
|
||||
</Button> */}
|
||||
</span>
|
||||
</div>
|
||||
<Table
|
||||
className={`m-t-xs`}
|
||||
dataSource={this.state.source}
|
||||
columns={[
|
||||
{
|
||||
title: '名称',
|
||||
render: (item, rowData, rowIndex, i) => {
|
||||
return {
|
||||
props: { rowSpan: 1, colSpan: 1 },
|
||||
children: (
|
||||
<div key={`${rowIndex}-${rowData.name}`}>
|
||||
{rowData.id ? (
|
||||
<Button
|
||||
level="link"
|
||||
size="sm"
|
||||
className={`m-l-xs`}
|
||||
onClick={() => {
|
||||
const path =
|
||||
this.getPath(this.state.breadcrumb) === ''
|
||||
? rowData.name
|
||||
: `${this.getPath(this.state.breadcrumb)}/${
|
||||
rowData.name
|
||||
}`;
|
||||
this.props.env
|
||||
.fetcher({
|
||||
method: 'post',
|
||||
url: `/storage/v1/object/sign/${this.props.bucket}`,
|
||||
data: {
|
||||
expiresIn: 60000,
|
||||
paths: [path],
|
||||
},
|
||||
headers: {
|
||||
post2rest: false,
|
||||
},
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (data && data.length > 0) {
|
||||
let presignedurl = `${location.origin}/api/storage/v1${data[0].signedURL}&download=${path}`;
|
||||
window.open(
|
||||
`${
|
||||
location.origin
|
||||
}/preview/onlinePreview?url=${encodeURIComponent(
|
||||
window.btoa(unescape(encodeURIComponent(presignedurl)))
|
||||
)}`
|
||||
);
|
||||
} else {
|
||||
this.props.env.notify(
|
||||
'error',
|
||||
'预览文件失败'
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((err) => console.log(err));
|
||||
}}
|
||||
>
|
||||
{rowData.name}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
level="link"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
let temp_breadcrumb = JSON.parse(
|
||||
JSON.stringify(this.state.breadcrumb)
|
||||
);
|
||||
temp_breadcrumb.push(rowData.name);
|
||||
this.setState({ breadcrumb: temp_breadcrumb });
|
||||
}}
|
||||
>
|
||||
{rowData.name}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
};
|
||||
},
|
||||
},
|
||||
{ key: 'mimetype', title: '类型' },
|
||||
{ key: 'size', title: '大小' },
|
||||
{ key: 'updated_at', title: '修改日期' },
|
||||
{
|
||||
title: '操作',
|
||||
render: (item, rowData, rowIndex, i) => {
|
||||
return {
|
||||
props: { rowSpan: 1, colSpan: 1 },
|
||||
children: (
|
||||
<div key={`${rowIndex}-${rowData.name}`}>
|
||||
<Button
|
||||
level="warning"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
const { moveFile } = this.props;
|
||||
const path = this.getPath(this.state.breadcrumb);
|
||||
const sourceKey = path
|
||||
? path + '/' + rowData.name
|
||||
: rowData.name;
|
||||
|
||||
if (rowData.id) {
|
||||
this.props.onAction(
|
||||
null,
|
||||
moveFile,
|
||||
{ path, sourceKey },
|
||||
false,
|
||||
this.context
|
||||
);
|
||||
} else {
|
||||
this.setState({
|
||||
showRenameDialog: true,
|
||||
renameFile: rowData.name,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
重命名
|
||||
</Button>
|
||||
<Button
|
||||
level="link"
|
||||
size="sm"
|
||||
className={`m-l-xs fa fa-trash text-danger`}
|
||||
tooltip="删除文件"
|
||||
onClick={async () => {
|
||||
const path = this.getPath(this.state.breadcrumb);
|
||||
const { delFile } = this.props;
|
||||
|
||||
if (rowData.id) {
|
||||
const prefixes = path
|
||||
? [path + '/' + rowData.name]
|
||||
: [rowData.name];
|
||||
|
||||
this.props.onAction(
|
||||
null,
|
||||
delFile,
|
||||
{ path, prefixes },
|
||||
false,
|
||||
this.context
|
||||
);
|
||||
} else {
|
||||
const prefixes = await this.getContent(
|
||||
path ? path + '/' + rowData.name : rowData.name,
|
||||
[]
|
||||
);
|
||||
|
||||
this.props.onAction(
|
||||
null,
|
||||
delFile,
|
||||
{ path, prefixes },
|
||||
false,
|
||||
this.context
|
||||
);
|
||||
}
|
||||
}}
|
||||
></Button>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
},
|
||||
},
|
||||
]}
|
||||
rowClassName={(item) => !item.id && 'font-bold text-primary'}
|
||||
/>
|
||||
{this.state.showAddFileDialog ? (
|
||||
<div
|
||||
id="addFileDialog"
|
||||
className="amis-dialog-widget cxd-Modal cxd-Modal--1th"
|
||||
>
|
||||
<div className="cxd-Modal-overlay in"></div>
|
||||
<div className="cxd-Modal-content in">
|
||||
<div className="cxd-Modal-header">
|
||||
<div className="cxd-Modal-title">上传文件</div>
|
||||
</div>
|
||||
<div className="cxd-Modal-body">
|
||||
<form method="dialog" className="cxd-Form cxd-Form--horizontal">
|
||||
<div className="cxd-Form-item cxd-Form-item--horizontal">
|
||||
<div className="cxd-Form-value">
|
||||
<div className="cxd-Form-control cxd-TextControl">
|
||||
<div>文件大小不能超过<strong>500MB</strong></div>
|
||||
<input
|
||||
id="addFile"
|
||||
type="file"
|
||||
multiple
|
||||
onChange={() => {
|
||||
const addFile: any =
|
||||
document.getElementById('addFile');
|
||||
|
||||
if (addFile.files.length)
|
||||
this.setState({
|
||||
files: Array.from(addFile.files),
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{this.state.files.length > 0 && (
|
||||
<ul>
|
||||
{this.state.files.map((file, index) => {
|
||||
return (
|
||||
<li key={`file-${index}-${file.name}`}>
|
||||
{file.name}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div className="cxd-Modal-footer">
|
||||
<Button
|
||||
onClick={() =>
|
||||
this.setState({ files: [], showAddFileDialog: false })
|
||||
}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
level="primary"
|
||||
onClick={async () => {
|
||||
const path = this.getPath(this.state.breadcrumb);
|
||||
this.setState({ isLoading: true });
|
||||
|
||||
if (this.state.files.length) {
|
||||
Promise.all(
|
||||
this.state.files.map((file: any) => {
|
||||
let url = `storage/v1/object/${this.props.bucket}/`;
|
||||
|
||||
if (path) url += path + '/' + file.name;
|
||||
else url += file.name;
|
||||
|
||||
return this.props.env.fetcher({
|
||||
method: 'post',
|
||||
url,
|
||||
data: { fileBody: file },
|
||||
});
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
this.reload();
|
||||
this.props.env.notify('success', '上传文件成功');
|
||||
this.setState({
|
||||
files: [],
|
||||
isLoading: false,
|
||||
showAddFileDialog: false,
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
this.props.env.notify('error', '上传文件失败');
|
||||
this.setState({
|
||||
files: [],
|
||||
isLoading: false,
|
||||
showAddFileDialog: false,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.props.env.notify('warning', '请上传文件');
|
||||
}
|
||||
}}
|
||||
>
|
||||
确认
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{this.state.showRenameDialog ? (
|
||||
<div
|
||||
id="renameDialog"
|
||||
className="amis-dialog-widget cxd-Modal cxd-Modal--1th"
|
||||
>
|
||||
<div className="cxd-Modal-overlay in"></div>
|
||||
<div className="cxd-Modal-content in">
|
||||
<div className="cxd-Modal-header">
|
||||
<div className="cxd-Modal-title">重命名</div>
|
||||
</div>
|
||||
<div className="cxd-Modal-body">
|
||||
<form method="dialog" className="cxd-Form cxd-Form--horizontal">
|
||||
<div className="cxd-Form-item cxd-Form-item--horizontal">
|
||||
<label
|
||||
htmlFor="rename"
|
||||
className="cxd-Form-label cxd-Form-itemColumn--normal"
|
||||
>
|
||||
<span>
|
||||
<span className="cxd-TplField">
|
||||
<span>名称</span>
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
<div className="cxd-Form-value">
|
||||
<div className="cxd-Form-control cxd-TextControl">
|
||||
<div className="cxd-TextControl-input">
|
||||
<input id="renameFile" type="text" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div className="cxd-Modal-footer">
|
||||
<Button
|
||||
onClick={() => this.setState({ showRenameDialog: false })}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
level="primary"
|
||||
onClick={async () => {
|
||||
const renameFile: any =
|
||||
document.getElementById('renameFile');
|
||||
const path = this.getPath(this.state.breadcrumb);
|
||||
const prefixes = await this.getContent(
|
||||
path
|
||||
? path + '/' + this.state.renameFile
|
||||
: this.state.renameFile,
|
||||
[]
|
||||
);
|
||||
|
||||
if (renameFile.value) {
|
||||
Promise.all(
|
||||
prefixes.map((item) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.props.env
|
||||
.fetcher({
|
||||
method: 'post',
|
||||
url: '/storage/v1/object/move',
|
||||
data: {
|
||||
bucketId: this.props.bucket,
|
||||
sourceKey: item,
|
||||
destinationKey: item.replace(
|
||||
this.state.renameFile,
|
||||
renameFile.value
|
||||
),
|
||||
},
|
||||
})
|
||||
.then((res) => resolve(res))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
this.setState({ showRenameDialog: false });
|
||||
this.reload();
|
||||
})
|
||||
.catch((err) => console.log(err));
|
||||
} else {
|
||||
this.props.env.notify('error', '请输入文件名');
|
||||
}
|
||||
}}
|
||||
>
|
||||
确认
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{this.state.isLoading ? (
|
||||
<div
|
||||
id="isLoading"
|
||||
className="amis-dialog-widget cxd-Modal cxd-Modal--1th"
|
||||
>
|
||||
<div className="cxd-Modal-overlay in"></div>
|
||||
<div className="cxd-Modal-content in">
|
||||
<h2>上传文件中...</h2>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
301
src/renderer/DataSetPicker.tsx
Normal file
301
src/renderer/DataSetPicker.tsx
Normal file
@@ -0,0 +1,301 @@
|
||||
import { Button, FormItem, ScopedContext, Table } from 'amis';
|
||||
import { FormControlProps } from 'amis/lib/renderers/Form/Item';
|
||||
import React from 'react';
|
||||
|
||||
@FormItem({
|
||||
test: /(^|\/)dataset\-picker$/,
|
||||
name: 'dataset-picker',
|
||||
})
|
||||
export class DataestRenderer extends React.Component<
|
||||
FormControlProps,
|
||||
{
|
||||
source: any;
|
||||
breadcrumb: any;
|
||||
hash: any;
|
||||
showDialog: any;
|
||||
}
|
||||
> {
|
||||
static contextType = ScopedContext;
|
||||
|
||||
constructor(props: FormControlProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
source: [],
|
||||
breadcrumb: ['根目录'],
|
||||
hash: '',
|
||||
showDialog: false,
|
||||
};
|
||||
}
|
||||
|
||||
fetchSource(prefix) {
|
||||
if (prefix === '/') {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.props.env
|
||||
.resFetcher({
|
||||
method: 'get',
|
||||
url: '/res/api/shares',
|
||||
})
|
||||
.then((res) => resolve(res))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.props.env
|
||||
.resFetcher({
|
||||
method: 'get',
|
||||
url: `/res/api/resources/${prefix}`,
|
||||
})
|
||||
.then((res) => resolve(res))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async setSource(prefix) {
|
||||
const result: any = await this.fetchSource(prefix);
|
||||
const source =
|
||||
prefix === '/'
|
||||
? result.data.data
|
||||
.filter((item) => !item.password_hash)
|
||||
.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
name: item.path.replaceAll(/^\/|\/$/g, ''),
|
||||
mimetype:
|
||||
item.path[item.path.length - 1] === '/' ? '文件夹' : null,
|
||||
};
|
||||
})
|
||||
: result.data.data.items.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
mimetype: item.isDir ? '文件夹' : item.type,
|
||||
updated_at: item.modified.slice(0, 16).replace('T', ' '),
|
||||
};
|
||||
});
|
||||
|
||||
this.setState({ source });
|
||||
}
|
||||
|
||||
getPath(breadcrumb, n) {
|
||||
return breadcrumb.slice(n).toString().replaceAll(',', '/');
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const scoped = this.context;
|
||||
scoped.registerComponent(this);
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
this.setSource('/');
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: any, prevState: any, snapshot: any) {
|
||||
if (
|
||||
JSON.stringify(prevState.breadcrumb) !==
|
||||
JSON.stringify(this.state.breadcrumb)
|
||||
) {
|
||||
this.reload();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const scoped = this.context;
|
||||
scoped.unRegisterComponent(this);
|
||||
}
|
||||
|
||||
reload() {
|
||||
this.setSource(`${this.getPath(this.state.breadcrumb, 1)}/`);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
level="warning"
|
||||
size="sm"
|
||||
onClick={() => this.setState({ showDialog: true })}
|
||||
>
|
||||
目标选取
|
||||
</Button>
|
||||
{this.state.showDialog ? (
|
||||
<div
|
||||
id="dialog"
|
||||
className="amis-dialog-widget cxd-Modal cxd-Modal--1th"
|
||||
>
|
||||
<div className="cxd-Modal-overlay in"></div>
|
||||
<div className="cxd-Modal-content in">
|
||||
<div className="cxd-Modal-header">
|
||||
<div className="cxd-Modal-title">目标选取</div>
|
||||
</div>
|
||||
<div className="cxd-Modal-body">
|
||||
<div
|
||||
style={{ display: 'flex', justifyContent: 'space-between' }}
|
||||
>
|
||||
<span>
|
||||
{this.state.breadcrumb.map((item, index) => {
|
||||
if (index) {
|
||||
if (index === this.state.breadcrumb.length - 1) {
|
||||
return <span key={`${index}-${item}`}>/{item}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<span key={`${index}-${item}`}>
|
||||
/
|
||||
<a
|
||||
onClick={() => {
|
||||
let temp_breadcrumb = JSON.parse(
|
||||
JSON.stringify(this.state.breadcrumb)
|
||||
);
|
||||
temp_breadcrumb = temp_breadcrumb.slice(
|
||||
0,
|
||||
index + 1
|
||||
);
|
||||
this.setState({ breadcrumb: temp_breadcrumb });
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</a>
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
if (index === this.state.breadcrumb.length - 1) {
|
||||
return <span key={`${index}-${item}`}>{item}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
key={`${index}-${item}`}
|
||||
onClick={() => {
|
||||
let temp_breadcrumb = JSON.parse(
|
||||
JSON.stringify(this.state.breadcrumb)
|
||||
);
|
||||
temp_breadcrumb = temp_breadcrumb.slice(
|
||||
0,
|
||||
index + 1
|
||||
);
|
||||
this.setState({ breadcrumb: temp_breadcrumb });
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
<Table
|
||||
className={`m-t-xs`}
|
||||
dataSource={this.state.source}
|
||||
columns={[
|
||||
{
|
||||
title: '名称',
|
||||
render: (item, rowData, rowIndex, i) => {
|
||||
return {
|
||||
props: { rowSpan: 1, colSpan: 1 },
|
||||
children: (
|
||||
<div key={`${rowIndex}-${rowData.name}`}>
|
||||
{rowData.isDir ||
|
||||
rowData.mimetype === '文件夹' ? (
|
||||
<Button
|
||||
level="link"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
let temp_breadcrumb = JSON.parse(
|
||||
JSON.stringify(this.state.breadcrumb)
|
||||
);
|
||||
|
||||
temp_breadcrumb.push(rowData.name);
|
||||
this.setState({
|
||||
breadcrumb: temp_breadcrumb,
|
||||
});
|
||||
|
||||
if (rowData.hash)
|
||||
this.setState({
|
||||
hash: rowData.hash,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{rowData.name}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
level="link"
|
||||
size="sm"
|
||||
className={`m-l-xs`}
|
||||
onClick={() => {
|
||||
const prefix = this.getPath(
|
||||
this.state.breadcrumb,
|
||||
2
|
||||
);
|
||||
const path = rowData.hash
|
||||
? `${rowData.hash}`
|
||||
: prefix
|
||||
? `${this.state.hash}/${prefix}/${rowData.name}`
|
||||
: `${this.state.hash}/${rowData.name}`;
|
||||
this.props.onChange(path);
|
||||
this.setState({ showDialog: false });
|
||||
}}
|
||||
>
|
||||
{rowData.name}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
render: (item, rowData, rowIndex, i) => {
|
||||
return {
|
||||
props: { rowSpan: 1, colSpan: 1 },
|
||||
children: (
|
||||
<div key={`${rowIndex}-${rowData.mimetype}`}>
|
||||
{rowData.mimetype}
|
||||
</div>
|
||||
),
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '大小',
|
||||
render: (item, rowData, rowIndex, i) => {
|
||||
return {
|
||||
props: { rowSpan: 1, colSpan: 1 },
|
||||
children: (
|
||||
<div key={`${rowIndex}-${rowData.size}`}>
|
||||
{rowData.size}
|
||||
</div>
|
||||
),
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '修改日期',
|
||||
render: (item, rowData, rowIndex, i) => {
|
||||
return {
|
||||
props: { rowSpan: 1, colSpan: 1 },
|
||||
children: (
|
||||
<div key={`${rowIndex}-${rowData.updated_at}`}>
|
||||
{rowData.updated_at}
|
||||
</div>
|
||||
),
|
||||
};
|
||||
},
|
||||
},
|
||||
]}
|
||||
rowClassName={(item) => !item.id && 'font-bold text-primary'}
|
||||
/>
|
||||
</div>
|
||||
<div className="cxd-Modal-footer">
|
||||
<Button onClick={() => this.setState({ showDialog: false })}>
|
||||
取消
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
453
src/renderer/DataTemplatePreview.tsx
Normal file
453
src/renderer/DataTemplatePreview.tsx
Normal file
@@ -0,0 +1,453 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { Button, FormItem, render, Table } from 'amis';
|
||||
import { FormControlProps } from 'amis/lib/renderers/Form/Item';
|
||||
import Form from '@rjsf/fluent-ui';
|
||||
import _ from 'lodash';
|
||||
import { isEffectiveApi } from 'amis/lib/utils/api';
|
||||
import { Payload } from 'amis/lib/types';
|
||||
import { getVariable, setVariable } from 'amis/lib/utils/helper';
|
||||
import { Badge } from 'amis/lib/components/Badge';
|
||||
|
||||
@FormItem({
|
||||
test: /(^|\/)datatemplatepreview$/,
|
||||
name: 'datatemplatepreview',
|
||||
})
|
||||
export class DataTemplatePreviewRenderer extends React.Component<
|
||||
FormControlProps,
|
||||
{
|
||||
data_template: any;
|
||||
result_template: any;
|
||||
trial_data: any;
|
||||
trial_result: any;
|
||||
data: any;
|
||||
canNext: boolean;
|
||||
}
|
||||
> {
|
||||
trialInterval: any;
|
||||
trial_data: any;
|
||||
dataTemplate: any;
|
||||
resultTemplate: any;
|
||||
constructor(props: FormControlProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
data_template: {},
|
||||
result_template: {},
|
||||
trial_data: {},
|
||||
trial_result: {},
|
||||
data: {},
|
||||
canNext: true,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { store, data, env, onChange } = this.props;
|
||||
let template_id = getVariable(data, 'template_id');
|
||||
let sample_data = {};
|
||||
let sample_result = {};
|
||||
env
|
||||
.fetcher({
|
||||
url: `rest/data_templates/${template_id}`,
|
||||
})
|
||||
.then((payload) => {
|
||||
if (payload.data.sample_data !== null) {
|
||||
sample_data = payload.data.sample_data;
|
||||
}
|
||||
if (payload.data.sample_result !== null) {
|
||||
sample_result = payload.data.sample_result;
|
||||
}
|
||||
this.setState({
|
||||
trial_data: sample_data,
|
||||
trial_result: sample_result,
|
||||
});
|
||||
|
||||
onChange({
|
||||
trial_data: sample_data,
|
||||
trial_result: sample_result,
|
||||
});
|
||||
});
|
||||
|
||||
this.trialInterval = setInterval(() => {
|
||||
if (
|
||||
store?.data &&
|
||||
(!_.isEqual(this.dataTemplate, store?.data.data) ||
|
||||
!_.isEqual(this.resultTemplate, store?.data.result))
|
||||
) {
|
||||
this.dataTemplate = store?.data.data;
|
||||
this.resultTemplate = store?.data.result;
|
||||
this.setState({
|
||||
data_template: store?.data.data,
|
||||
result_template: store?.data.result,
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.trialInterval) {
|
||||
clearInterval(this.trialInterval);
|
||||
}
|
||||
}
|
||||
|
||||
fetchData() {
|
||||
const { env, api, dataApi, data } = this.props;
|
||||
|
||||
let project_code = getVariable(data, 'projects.code');
|
||||
|
||||
if (isEffectiveApi(api)) {
|
||||
env
|
||||
.fetcher(api, { project_code: project_code })
|
||||
.then((payload: Payload) => {
|
||||
if (payload.data) {
|
||||
this.dataTemplate = payload.data.data;
|
||||
this.resultTemplate = payload.data.result;
|
||||
this.setState({
|
||||
data_template: payload.data.data,
|
||||
result_template: payload.data.result,
|
||||
});
|
||||
}
|
||||
});
|
||||
// env
|
||||
// .fetcher(dataApi, { user2project_id: })
|
||||
// .then((payload: Payload) => {
|
||||
// if (payload.data) {
|
||||
// this.setState({
|
||||
// data: payload.data,
|
||||
// trial_data: payload.data.data,
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
getConst(node: any, index: any) {
|
||||
let i = index;
|
||||
if (node.type === 'string') {
|
||||
let tpl_value = node.default ? node.default : '';
|
||||
while (
|
||||
tpl_value &&
|
||||
tpl_value.match(/{{(.*?)}}/) &&
|
||||
tpl_value.match(/{{(.*?)}}/).length === 2
|
||||
) {
|
||||
try {
|
||||
tpl_value = tpl_value.replace(
|
||||
/({{(.*?)}})/,
|
||||
`${eval(tpl_value.match(/{{(.*?)}}/)[1])}`
|
||||
);
|
||||
} catch (error) {
|
||||
tpl_value = '';
|
||||
}
|
||||
}
|
||||
return tpl_value;
|
||||
} else if (node.type === 'object') {
|
||||
if (node.children && Array.isArray(node.children.data)) {
|
||||
let o: any = {};
|
||||
|
||||
node.children.data.forEach((item: any) => {
|
||||
o[item.name] = this.getConst(item, index);
|
||||
});
|
||||
return o;
|
||||
}
|
||||
} else if (node.type === 'array') {
|
||||
let a: Array<any> = [];
|
||||
if (node.children && Array.isArray(node.children.data)) {
|
||||
node.children.data.forEach((item: any) => {
|
||||
if (item.config && item.config.max) {
|
||||
for (let i = 0; i < item.config.max; i++) {
|
||||
a.push(this.getConst(item, i));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
{
|
||||
if (node.children && this.isValid(node.children.data)) {
|
||||
a.push(this.getConst(node.children.data, index));
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
// 通用的处理表达式方法
|
||||
// 这里打破 JSON Schema 规范
|
||||
handleExpression(
|
||||
rootFormData: any,
|
||||
curNodePath: string,
|
||||
expression: string,
|
||||
fallBack: Function
|
||||
) {
|
||||
const regExpression = /{{(.*)}}/;
|
||||
// 未配置
|
||||
if (undefined === expression) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// 配置了 mustache 表达式
|
||||
const matchExpression = regExpression.exec(expression);
|
||||
regExpression.lastIndex = 0; // 重置索引
|
||||
if (matchExpression) {
|
||||
try {
|
||||
const code = matchExpression[1].trim();
|
||||
|
||||
// eslint-disable-next-line no-new-func
|
||||
const fn = new Function(
|
||||
'parentFormData',
|
||||
'rootFormData',
|
||||
`return ${code}`
|
||||
);
|
||||
|
||||
let result = fn(
|
||||
this.getPathVal(rootFormData, curNodePath, 1),
|
||||
rootFormData
|
||||
);
|
||||
|
||||
return result ? result : '';
|
||||
} catch (error) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// 回退
|
||||
return fallBack();
|
||||
}
|
||||
|
||||
// 获取当前path值
|
||||
getPathVal(obj: any, path: string, leftDeviation = 0) {
|
||||
const pathSeparator = '.';
|
||||
const pathArr = path.split(pathSeparator);
|
||||
|
||||
for (let i = 0; i < pathArr.length - leftDeviation; i += 1) {
|
||||
// 错误路径或者undefined中断查找
|
||||
if (obj === undefined) return undefined;
|
||||
obj = pathArr[i] === '' ? obj : obj[pathArr[i]];
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
buildSchema(node: any): any {
|
||||
let title = node.title;
|
||||
// while(title && title.match(/{{(.*?)}}/) && title.match(/{{(.*?)}}/).length === 2) {
|
||||
// title = title.replace(/({{(.*?)}})/, `${eval(title.match(/{{(.*?)}}/)[1])}`)
|
||||
// }
|
||||
if (node.type === 'string') {
|
||||
let defaultValue = this.getConst(node, 0);
|
||||
return Object.assign(
|
||||
{},
|
||||
{
|
||||
type: node.type,
|
||||
title: title,
|
||||
required: true,
|
||||
disabled: node.disabled,
|
||||
computed: node.computed,
|
||||
template: node.config ? node.config.template : '',
|
||||
|
||||
default: defaultValue ? defaultValue : '',
|
||||
}
|
||||
);
|
||||
} else if (node.type === 'object') {
|
||||
let nodeProperty = Object.assign({
|
||||
type: node.type,
|
||||
title: title,
|
||||
properties: {},
|
||||
});
|
||||
if (Array.isArray(node.children.data)) {
|
||||
node.children.data.forEach((item: any) => {
|
||||
if (this.isValid(item)) {
|
||||
nodeProperty.properties[item.name] = this.buildSchema(item);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (this.isValid(node.children.data)) {
|
||||
nodeProperty.properties[node.children.data.name] = this.buildSchema(
|
||||
node.children.data
|
||||
);
|
||||
}
|
||||
}
|
||||
return nodeProperty;
|
||||
} else if (node.type === 'array') {
|
||||
let defaultValue: Array<any> = [];
|
||||
if (node.config && node.config.min === node.config.max) {
|
||||
for (let i = 0; i < node.config.max; i++) {
|
||||
defaultValue.push(this.getConst(node.children.data, i));
|
||||
}
|
||||
} else if (node.config && node.config.min < node.config.max) {
|
||||
for (let i = 0; i < node.config.min; i++) {
|
||||
defaultValue.push(this.getConst(node.children.data, i));
|
||||
}
|
||||
}
|
||||
|
||||
return Object.assign({
|
||||
type: node.type,
|
||||
title: title,
|
||||
maxItems: node.config && node.config.max ? node.config.max : 100,
|
||||
minItems: node.config && node.config.min ? node.config.min : 0,
|
||||
default: defaultValue ? defaultValue : [],
|
||||
items: this.buildSchema(node.children.data),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
buildUISchema(properties: any) {
|
||||
if (!properties) {
|
||||
return [];
|
||||
}
|
||||
let uiSchema: any = {};
|
||||
Object.keys(properties).forEach((key) => {
|
||||
uiSchema[key] = {};
|
||||
if (properties[key].type === 'string') {
|
||||
uiSchema[key] = {
|
||||
'ui:disabled': true,
|
||||
};
|
||||
} else if (properties[key].type === 'object') {
|
||||
uiSchema[key] = this.buildUISchema(properties[key].properties);
|
||||
} else if (properties[key].type === 'array') {
|
||||
uiSchema[key] = {
|
||||
items: this.buildUISchema(properties[key].items.properties),
|
||||
'ui:options': {
|
||||
addable: true,
|
||||
removable: !(properties[key].minItems === properties[key].maxItems),
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
return uiSchema;
|
||||
}
|
||||
|
||||
computeData(properties: any, data: any) {
|
||||
if (!properties) {
|
||||
return null;
|
||||
}
|
||||
Object.keys(properties).forEach((key: any) => {
|
||||
if (properties[key] === undefined) {
|
||||
return data;
|
||||
}
|
||||
if (properties[key].type === 'string') {
|
||||
if (properties[key].computed === true) {
|
||||
data[key] = this.handleExpression(
|
||||
data,
|
||||
'',
|
||||
properties[key].template,
|
||||
() => {}
|
||||
);
|
||||
}
|
||||
} else if (properties[key].type === 'object') {
|
||||
data[key] = this.computeData(properties[key].properties, data[key]);
|
||||
} else if (properties[key].type === 'array') {
|
||||
data[key].forEach((subData: any, i: number) => {
|
||||
data[key][i] = this.computeData(
|
||||
properties[key].items.properties,
|
||||
subData
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
isValid(node: any) {
|
||||
if (_.has(node, 'type')) {
|
||||
if (node.type === 'string') {
|
||||
return _.has(node, 'name') && _.has(node, 'title');
|
||||
}
|
||||
if (node.type === 'object') {
|
||||
return (
|
||||
_.has(node, 'name') &&
|
||||
_.has(node, 'title') &&
|
||||
_.has(node, 'children.data') &&
|
||||
_.isArray(node.children.data)
|
||||
);
|
||||
}
|
||||
if (node.type === 'array') {
|
||||
return (
|
||||
_.has(node, 'name') &&
|
||||
_.has(node, 'title') &&
|
||||
_.has(node, 'children.data') &&
|
||||
_.has(node, 'children.data.type')
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { env, data, dataApi, onChange, className, min, max, step } =
|
||||
this.props;
|
||||
const { data_template, result_template, trial_data } =
|
||||
this.state;
|
||||
let templateSchemaProperties = Object.assign({});
|
||||
let resultSchemaProperties = Object.assign({});
|
||||
|
||||
|
||||
|
||||
if (data_template && _.isArray(data_template.data)) {
|
||||
data_template.data.forEach((item: any) => {
|
||||
if (this.isValid(item)) {
|
||||
templateSchemaProperties[item.name] = this.buildSchema(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (result_template && _.isArray(result_template.data)) {
|
||||
result_template.data.forEach((item: any) => {
|
||||
if (this.isValid(item)) {
|
||||
resultSchemaProperties[item.name] = this.buildSchema(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
let dataSchema = {
|
||||
title: '原始数据',
|
||||
description: data_template && data_template.description,
|
||||
properties: templateSchemaProperties,
|
||||
};
|
||||
// let resultSchema = {
|
||||
// title: '结果数据',
|
||||
// description: result_template && result_template.description,
|
||||
// properties: resultSchemaProperties,
|
||||
// };
|
||||
let uiSchema = this.buildUISchema(dataSchema.properties);
|
||||
// let resultUiSchema = this.buildUISchema(resultSchema.properties);
|
||||
|
||||
const style = {
|
||||
// overflow: 'auto',
|
||||
// height: '700px',
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
<div {...{ style }}>
|
||||
<Form
|
||||
schema={dataSchema}
|
||||
uiSchema={uiSchema}
|
||||
formData={trial_data}
|
||||
ArrayFieldTemplate={(props) => {
|
||||
let columns = Object.keys(
|
||||
props.items[0].children.props.uiSchema
|
||||
).map((o, index) => {
|
||||
return {
|
||||
name: o,
|
||||
label: props.schema.items['properties'][o].title,
|
||||
};
|
||||
});
|
||||
return render({
|
||||
type: 'table',
|
||||
title: props.title,
|
||||
items: props.formData,
|
||||
columns: columns,
|
||||
});
|
||||
}}
|
||||
onSubmit={() => {
|
||||
console.log('submit');
|
||||
}}
|
||||
onError={() => {
|
||||
console.log('errors');
|
||||
}}
|
||||
>
|
||||
<Fragment />
|
||||
</Form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
530
src/renderer/DataTrial.tsx
Normal file
530
src/renderer/DataTrial.tsx
Normal file
@@ -0,0 +1,530 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { Button, FormItem, FormControlProps, getVariable } from 'amis';
|
||||
import Form from '@rjsf/fluent-ui';
|
||||
import _ from 'lodash';
|
||||
|
||||
@FormItem({
|
||||
test: /(^|\/)datatrial$/,
|
||||
name: 'datatrial',
|
||||
})
|
||||
export class DataTrialRenderer extends React.Component<
|
||||
FormControlProps,
|
||||
{
|
||||
data_template: any;
|
||||
result_template: any;
|
||||
trial_data: any;
|
||||
trial_result: any;
|
||||
}
|
||||
> {
|
||||
trialInterval: any;
|
||||
trial_data: any;
|
||||
trial_result: any;
|
||||
dataTemplate: any;
|
||||
resultTemplate: any;
|
||||
constructor(props: FormControlProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
data_template: {},
|
||||
result_template: {},
|
||||
trial_data: {},
|
||||
trial_result: {},
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { store, data, env, onChange } = this.props;
|
||||
let template_id = getVariable(data, 'template_id');
|
||||
let sample_data = {};
|
||||
let sample_result = {};
|
||||
env
|
||||
.fetcher({
|
||||
url: `rest/data_templates/${template_id}`,
|
||||
})
|
||||
.then((payload) => {
|
||||
if (payload.data.sample_data !== null) {
|
||||
sample_data = payload.data.sample_data;
|
||||
}
|
||||
if (payload.data.sample_result !== null) {
|
||||
sample_result = payload.data.sample_result;
|
||||
}
|
||||
this.setState({
|
||||
trial_data: sample_data,
|
||||
trial_result: sample_result,
|
||||
});
|
||||
|
||||
onChange({
|
||||
trial_data: sample_data,
|
||||
trial_result: sample_result,
|
||||
});
|
||||
});
|
||||
|
||||
this.trialInterval = setInterval(() => {
|
||||
if (
|
||||
store?.data &&
|
||||
(!_.isEqual(this.dataTemplate, store?.data.data) ||
|
||||
!_.isEqual(this.resultTemplate, store?.data.result))
|
||||
) {
|
||||
this.dataTemplate = store?.data.data;
|
||||
this.resultTemplate = store?.data.result;
|
||||
this.setState({
|
||||
data_template: store?.data.data,
|
||||
result_template: store?.data.result,
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.trialInterval) {
|
||||
clearInterval(this.trialInterval);
|
||||
}
|
||||
}
|
||||
|
||||
getConst(node: any, index: any) {
|
||||
let i = index;
|
||||
if (node.type === 'string') {
|
||||
let tpl_value = node.default ? node.default : '';
|
||||
while (
|
||||
tpl_value &&
|
||||
tpl_value.match(/{{(.*?)}}/) &&
|
||||
tpl_value.match(/{{(.*?)}}/).length === 2
|
||||
) {
|
||||
try {
|
||||
tpl_value = tpl_value.replace(
|
||||
/({{(.*?)}})/,
|
||||
`${eval(tpl_value.match(/{{(.*?)}}/)[1])}`
|
||||
);
|
||||
} catch (error) {
|
||||
tpl_value = '';
|
||||
}
|
||||
}
|
||||
return tpl_value;
|
||||
} else if (node.type === 'object') {
|
||||
if (node.children && Array.isArray(node.children.data)) {
|
||||
let o: any = {};
|
||||
|
||||
node.children.data.forEach((item: any) => {
|
||||
o[item.name] = this.getConst(item, index);
|
||||
});
|
||||
return o;
|
||||
}
|
||||
} else if (node.type === 'array') {
|
||||
let a: Array<any> = [];
|
||||
if (node.children && Array.isArray(node.children.data)) {
|
||||
node.children.data.forEach((item: any) => {
|
||||
if (item.config && item.config.max) {
|
||||
for (let i = 0; i < item.config.max; i++) {
|
||||
a.push(this.getConst(item, i));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
{
|
||||
if (node.children && this.isValid(node.children.data)) {
|
||||
a.push(this.getConst(node.children.data, index));
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
// 通用的处理表达式方法
|
||||
// 这里打破 JSON Schema 规范
|
||||
handleExpression(
|
||||
rootFormData: any,
|
||||
curNodePath: string,
|
||||
expression: string,
|
||||
fallBack: Function
|
||||
) {
|
||||
const regExpression = /{{(.*)}}/;
|
||||
// 未配置
|
||||
if (undefined === expression) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// 配置了 mustache 表达式
|
||||
const matchExpression = regExpression.exec(expression);
|
||||
regExpression.lastIndex = 0; // 重置索引
|
||||
if (matchExpression) {
|
||||
try {
|
||||
const code = matchExpression[1].trim();
|
||||
|
||||
// eslint-disable-next-line no-new-func
|
||||
const fn = new Function(
|
||||
'parentFormData',
|
||||
'rootFormData',
|
||||
`return ${code}`
|
||||
);
|
||||
|
||||
let result = fn(
|
||||
this.getPathVal(rootFormData, curNodePath, 1),
|
||||
rootFormData
|
||||
);
|
||||
|
||||
return result ? result : '';
|
||||
} catch (error) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// 回退
|
||||
return fallBack();
|
||||
}
|
||||
|
||||
// 获取当前path值
|
||||
getPathVal(obj: any, path: string, leftDeviation = 0) {
|
||||
const pathSeparator = '.';
|
||||
const pathArr = path.split(pathSeparator);
|
||||
|
||||
for (let i = 0; i < pathArr.length - leftDeviation; i += 1) {
|
||||
// 错误路径或者undefined中断查找
|
||||
if (obj === undefined) return undefined;
|
||||
obj = pathArr[i] === '' ? obj : obj[pathArr[i]];
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
buildSchema(node: any): any {
|
||||
let title = node.title;
|
||||
// while(title && title.match(/{{(.*?)}}/) && title.match(/{{(.*?)}}/).length === 2) {
|
||||
// title = title.replace(/({{(.*?)}})/, `${eval(title.match(/{{(.*?)}}/)[1])}`)
|
||||
// }
|
||||
if (node.type === 'string') {
|
||||
let defaultValue = this.getConst(node, 0);
|
||||
return Object.assign(
|
||||
{},
|
||||
{
|
||||
type: node.type,
|
||||
title: title,
|
||||
required: true,
|
||||
disabled: node.disabled,
|
||||
computed: node.computed,
|
||||
template: node.config ? node.config.template : '',
|
||||
|
||||
default: defaultValue ? defaultValue : '',
|
||||
}
|
||||
);
|
||||
} else if (node.type === 'object') {
|
||||
let nodeProperty = Object.assign({
|
||||
type: node.type,
|
||||
title: title,
|
||||
properties: {},
|
||||
});
|
||||
if (Array.isArray(node.children.data)) {
|
||||
node.children.data.forEach((item: any) => {
|
||||
if (this.isValid(item)) {
|
||||
nodeProperty.properties[item.name] = this.buildSchema(item);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (this.isValid(node.children.data)) {
|
||||
nodeProperty.properties[node.children.data.name] = this.buildSchema(
|
||||
node.children.data
|
||||
);
|
||||
}
|
||||
}
|
||||
return nodeProperty;
|
||||
} else if (node.type === 'array') {
|
||||
let defaultValue: Array<any> = [];
|
||||
if (node.config && node.config.min === node.config.max) {
|
||||
for (let i = 0; i < node.config.max; i++) {
|
||||
defaultValue.push(this.getConst(node.children.data, i));
|
||||
}
|
||||
} else if (node.config && node.config.min < node.config.max) {
|
||||
for (let i = 0; i < node.config.min; i++) {
|
||||
defaultValue.push(this.getConst(node.children.data, i));
|
||||
}
|
||||
}
|
||||
|
||||
return Object.assign({
|
||||
type: node.type,
|
||||
title: title,
|
||||
maxItems: node.config && node.config.max ? node.config.max : 100,
|
||||
minItems: node.config && node.config.min ? node.config.min : 0,
|
||||
default: defaultValue ? defaultValue : [],
|
||||
items: this.buildSchema(node.children.data),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
buildUISchema(properties: any) {
|
||||
if (!properties) {
|
||||
return [];
|
||||
}
|
||||
let uiSchema: any = {};
|
||||
Object.keys(properties).forEach((key) => {
|
||||
uiSchema[key] = {};
|
||||
if (properties[key].type === 'string') {
|
||||
uiSchema[key] = {
|
||||
'ui:disabled': properties[key].computed === true,
|
||||
};
|
||||
} else if (properties[key].type === 'object') {
|
||||
uiSchema[key] = this.buildUISchema(properties[key].properties);
|
||||
} else if (properties[key].type === 'array') {
|
||||
uiSchema[key] = {
|
||||
items: this.buildUISchema(properties[key].items.properties),
|
||||
'ui:options': {
|
||||
addable: true,
|
||||
removable: !(properties[key].minItems === properties[key].maxItems),
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
return uiSchema;
|
||||
}
|
||||
|
||||
computeData(properties: any, data: any) {
|
||||
if (!properties) {
|
||||
return null;
|
||||
}
|
||||
Object.keys(properties).forEach((key: any) => {
|
||||
if (properties[key] === undefined) {
|
||||
return data;
|
||||
}
|
||||
if (properties[key].type === 'string') {
|
||||
if (properties[key].computed === true) {
|
||||
data[key] = this.handleExpression(
|
||||
data,
|
||||
'',
|
||||
properties[key].template,
|
||||
() => {}
|
||||
);
|
||||
}
|
||||
} else if (properties[key].type === 'object') {
|
||||
data[key] = this.computeData(properties[key].properties, data[key]);
|
||||
} else if (properties[key].type === 'array') {
|
||||
data[key].forEach((subData: any, i: number) => {
|
||||
data[key][i] = this.computeData(
|
||||
properties[key].items.properties,
|
||||
subData
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
isValid(node: any) {
|
||||
if (_.has(node, 'type')) {
|
||||
if (node.type === 'string') {
|
||||
return _.has(node, 'name') && _.has(node, 'title');
|
||||
}
|
||||
if (node.type === 'object') {
|
||||
return (
|
||||
_.has(node, 'name') &&
|
||||
_.has(node, 'title') &&
|
||||
_.has(node, 'children.data') &&
|
||||
_.isArray(node.children.data)
|
||||
);
|
||||
}
|
||||
if (node.type === 'array') {
|
||||
return (
|
||||
_.has(node, 'name') &&
|
||||
_.has(node, 'children.data') &&
|
||||
_.has(node, 'children.data.type')
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// handleTrialData({ formData }: any) {
|
||||
// console.log(formData)
|
||||
// let newData = _.cloneDeep(formData);
|
||||
// let newFormData = this.computeData(this.dataSchema.properties, newData);
|
||||
// console.log(newFormData)
|
||||
// this.setState({
|
||||
// trial_data: newData,
|
||||
// });
|
||||
// this.trial_data = newFormData;
|
||||
// this.props.onChange({
|
||||
// trial_data: newFormData,
|
||||
// trial_result: this.trial_result,
|
||||
// });
|
||||
// }
|
||||
// handleTrialResult({ formData }: any) {
|
||||
// let newData = _.cloneDeep(formData);
|
||||
// let newFormData = this.computeData(this.resultSchema.properties, newData);
|
||||
|
||||
// // this.setState({
|
||||
// // trial_result: newFormData,
|
||||
// // });
|
||||
// this.trial_result = newFormData
|
||||
// this.props.onChange({
|
||||
// trial_data: this.trial_data,
|
||||
// trial_result: newFormData,
|
||||
// });
|
||||
// }
|
||||
|
||||
render() {
|
||||
const { env, data, onChange, className, min, max, step } = this.props;
|
||||
const { data_template, result_template, trial_data, trial_result } =
|
||||
this.state;
|
||||
let templateSchemaProperties = Object.assign({});
|
||||
let resultSchemaProperties = Object.assign({});
|
||||
|
||||
if (data_template && _.isArray(data_template.data)) {
|
||||
data_template.data.forEach((item: any) => {
|
||||
if (this.isValid(item)) {
|
||||
templateSchemaProperties[item.name] = this.buildSchema(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (result_template && _.isArray(result_template.data)) {
|
||||
result_template.data.forEach((item: any) => {
|
||||
if (this.isValid(item)) {
|
||||
resultSchemaProperties[item.name] = this.buildSchema(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
let dataSchema = {
|
||||
title: '原始数据示例',
|
||||
description: data_template && data_template.description,
|
||||
properties: templateSchemaProperties,
|
||||
};
|
||||
let resultSchema = {
|
||||
title: '结果数据示例',
|
||||
description: result_template && result_template.description,
|
||||
properties: resultSchemaProperties,
|
||||
};
|
||||
let uiSchema = this.buildUISchema(dataSchema.properties);
|
||||
let calculate_enabled = getVariable(data, 'calculate_enabled');
|
||||
return (
|
||||
<div className={``}>
|
||||
<Form
|
||||
schema={dataSchema}
|
||||
uiSchema={uiSchema}
|
||||
formData={trial_data}
|
||||
onChange={({ formData }) => {
|
||||
this.setState({
|
||||
trial_data: formData,
|
||||
});
|
||||
onChange({
|
||||
trial_data: formData,
|
||||
trial_result,
|
||||
});
|
||||
}}
|
||||
onSubmit={() => {
|
||||
console.log('submit');
|
||||
}}
|
||||
onError={() => {
|
||||
console.log('errors');
|
||||
}}
|
||||
>
|
||||
<Fragment />
|
||||
</Form>
|
||||
<Button
|
||||
level="primary"
|
||||
onClick={async () => {
|
||||
const { env, data } = this.props;
|
||||
const template_id = getVariable(data, 'template_id');
|
||||
const payload = await env.fetcher({
|
||||
method: 'patch',
|
||||
url: `rest/data_templates/${template_id}`,
|
||||
data: {
|
||||
sample_data: this.state.trial_data,
|
||||
},
|
||||
});
|
||||
|
||||
if (payload.ok) {
|
||||
env.notify('success', `测试数据已保存`);
|
||||
}
|
||||
}}
|
||||
>
|
||||
保存
|
||||
</Button>
|
||||
{calculate_enabled ? (
|
||||
<Button
|
||||
className="m-l-sm"
|
||||
level="primary"
|
||||
onClick={async () => {
|
||||
const { env, data } = this.props;
|
||||
const calculate_rule = getVariable(data, 'calculate_rule');
|
||||
const orderNumber = getVariable(data, 'orderNumber');
|
||||
const payload = await env.fetcher(
|
||||
{
|
||||
method: 'post',
|
||||
url: `/report-api/data/rule/test`,
|
||||
dataType: 'form',
|
||||
},
|
||||
{
|
||||
data: JSON.stringify(this.state.trial_data),
|
||||
rule: calculate_rule,
|
||||
ref: JSON.stringify({ _orderNumber_: orderNumber }),
|
||||
}
|
||||
);
|
||||
|
||||
if (payload.data.result) {
|
||||
this.setState({
|
||||
trial_data: payload.data.data,
|
||||
});
|
||||
onChange({
|
||||
trial_data: payload.data.data,
|
||||
trial_result,
|
||||
});
|
||||
} else {
|
||||
env.notify(
|
||||
'error',
|
||||
`${payload.data.result}: ${payload.data.message}`
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
计算
|
||||
</Button>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<Form
|
||||
schema={resultSchema}
|
||||
formData={trial_result}
|
||||
onChange={({ formData }) => {
|
||||
let newData = _.cloneDeep(formData);
|
||||
let newFormData = this.computeData(
|
||||
resultSchema.properties,
|
||||
newData
|
||||
);
|
||||
|
||||
this.setState({
|
||||
trial_result: newFormData,
|
||||
});
|
||||
onChange({
|
||||
trial_data,
|
||||
trial_result: newFormData,
|
||||
});
|
||||
}}
|
||||
onSubmit={() => {
|
||||
console.log('submit');
|
||||
}}
|
||||
onError={() => {
|
||||
console.log('errors');
|
||||
}}
|
||||
>
|
||||
<Fragment />
|
||||
</Form>
|
||||
<Button
|
||||
level="primary"
|
||||
onClick={async () => {
|
||||
const { env, data } = this.props;
|
||||
const template_id = getVariable(data, 'template_id');
|
||||
const payload = await env.fetcher({
|
||||
method: 'patch',
|
||||
url: `rest/data_templates/${template_id}`,
|
||||
data: {
|
||||
sample_result: this.state.trial_result,
|
||||
},
|
||||
});
|
||||
|
||||
if (payload.ok) {
|
||||
env.notify('success', `测试结果已保存`);
|
||||
}
|
||||
}}
|
||||
>
|
||||
保存
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
238
src/renderer/DestPicker.tsx
Normal file
238
src/renderer/DestPicker.tsx
Normal file
@@ -0,0 +1,238 @@
|
||||
import React from 'react'
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Table, Button } from 'amis';
|
||||
|
||||
export default function DestPicker({
|
||||
isRichtext,
|
||||
fetcher,
|
||||
breadcrumb,
|
||||
setBreadcrumb,
|
||||
bucket,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
}) {
|
||||
const [source, setSource] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchSource(null);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
reload();
|
||||
}, [breadcrumb]);
|
||||
|
||||
async function fetchSource(prefix) {
|
||||
const result = await fetcher({
|
||||
method: 'post',
|
||||
url: `/storage/v1/object/list/${bucket}`,
|
||||
data: {
|
||||
prefix: prefix ? prefix : '',
|
||||
sortBy: {
|
||||
column: 'name',
|
||||
order: 'asc',
|
||||
},
|
||||
},
|
||||
});
|
||||
let source = result.data
|
||||
.filter((item) => item.name.charAt(0) !== '.')
|
||||
.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
...item.metadata,
|
||||
mimetype: item.metadata
|
||||
? item.metadata.mimetype.split('/')[1]
|
||||
: '文件夹',
|
||||
size: item.metadata
|
||||
? '' + Math.floor(item.metadata.size / 1024) + 'KB'
|
||||
: null,
|
||||
updated_at: item.updated_at
|
||||
? item.updated_at.slice(0, 16).replace('T', ' ')
|
||||
: null,
|
||||
};
|
||||
});
|
||||
setSource(source);
|
||||
}
|
||||
|
||||
function getPath(breadcrumb) {
|
||||
return breadcrumb.slice(1).toString().replaceAll(',', '/');
|
||||
}
|
||||
|
||||
function reload() {
|
||||
fetchSource(getPath(breadcrumb));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
id="dialog"
|
||||
className="amis-dialog-widget cxd-Modal cxd-Modal--xl cxd-Modal--1th"
|
||||
>
|
||||
<div className="cxd-Modal-overlay in"></div>
|
||||
<div className="cxd-Modal-content in">
|
||||
<div className="cxd-Modal-header">
|
||||
<div className="cxd-Modal-title">选取目标目录</div>
|
||||
</div>
|
||||
<div className="cxd-Modal-body">
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span>
|
||||
{breadcrumb.map((item, index) => {
|
||||
if (index) {
|
||||
if (index === breadcrumb.length - 1) {
|
||||
return <span key={`${index}-${item}`}>/{item}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<span key={`${index}-${item}`}>
|
||||
/
|
||||
<a
|
||||
href="#"
|
||||
onClick={() => {
|
||||
let temp_breadcrumb = JSON.parse(
|
||||
JSON.stringify(breadcrumb)
|
||||
);
|
||||
temp_breadcrumb = temp_breadcrumb.slice(
|
||||
0,
|
||||
index + 1
|
||||
);
|
||||
setBreadcrumb(temp_breadcrumb);
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</a>
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
if (index === breadcrumb.length - 1) {
|
||||
return <span key={`${index}-${item}`}>{item}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
href="#"
|
||||
key={`${index}-${item}`}
|
||||
onClick={() => {
|
||||
let temp_breadcrumb = JSON.parse(
|
||||
JSON.stringify(breadcrumb)
|
||||
);
|
||||
temp_breadcrumb = temp_breadcrumb.slice(0, index + 1);
|
||||
setBreadcrumb(temp_breadcrumb);
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
<Table
|
||||
className={`m-t-xs`}
|
||||
dataSource={source}
|
||||
columns={[
|
||||
{
|
||||
title: '名称',
|
||||
render: (item, rowData, rowIndex, i) => {
|
||||
return {
|
||||
props: { rowSpan: 1, colSpan: 1 },
|
||||
children: (
|
||||
<div key={`${rowIndex}-${rowData.name}`}>
|
||||
{rowData.id ? (
|
||||
isRichtext ? (
|
||||
<Button
|
||||
level="link"
|
||||
size="sm"
|
||||
className={`m-l-xs`}
|
||||
onClick={() => {
|
||||
const path = getPath(breadcrumb)
|
||||
const filePath = path ? `${path}/${rowData.name}` : `${rowData.name}`
|
||||
const src = path ? `/api/storage/v1/object/public/mooc/${path}/${rowData.name}` : `/api/storage/v1/object/public/mooc/${rowData.name}`
|
||||
const type =
|
||||
rowData.metadata.mimetype.split('/')[0];
|
||||
console.log(src)
|
||||
|
||||
switch (type) {
|
||||
case 'image':
|
||||
onConfirm(`<img src="${src}" />`);
|
||||
break;
|
||||
case 'video':
|
||||
onConfirm(
|
||||
`<video controls src="${src}" />`
|
||||
);
|
||||
break;
|
||||
default:
|
||||
fetcher({
|
||||
method: 'post',
|
||||
url: `/storage/v1/object/sign/${bucket}`,
|
||||
data: {
|
||||
expiresIn: 60000,
|
||||
paths: [filePath],
|
||||
},
|
||||
headers: {
|
||||
post2rest: false,
|
||||
},
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (data && data.length > 0) {
|
||||
const presignedurl = `${location.origin}/api/storage/v1${data[0].signedURL}&download=${filePath}`;
|
||||
const href = `${
|
||||
location.origin
|
||||
}/preview/onlinePreview?url=${encodeURIComponent(
|
||||
window.btoa(unescape(encodeURIComponent(presignedurl)))
|
||||
)}`;
|
||||
onConfirm(`<a href=${href}>${rowData.name}</a>`);
|
||||
} else {
|
||||
this.props.env.notify(
|
||||
'error',
|
||||
'选取文件失败'
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((err) => console.log(err));
|
||||
}
|
||||
}}
|
||||
>
|
||||
{rowData.name}
|
||||
</Button>
|
||||
) : (
|
||||
<span>{rowData.name}</span>
|
||||
)
|
||||
) : (
|
||||
<Button
|
||||
level="link"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
let temp_breadcrumb = JSON.parse(
|
||||
JSON.stringify(breadcrumb)
|
||||
);
|
||||
temp_breadcrumb.push(rowData.name);
|
||||
setBreadcrumb(temp_breadcrumb);
|
||||
}}
|
||||
>
|
||||
{rowData.name}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
};
|
||||
},
|
||||
},
|
||||
{ key: 'mimetype', title: '类型' },
|
||||
{ key: 'size', title: '大小' },
|
||||
{ key: 'updated_at', title: '修改日期' },
|
||||
]}
|
||||
rowClassName={(item) => !item.id && 'font-bold text-primary'}
|
||||
/>
|
||||
</div>
|
||||
<div className="cxd-Modal-footer">
|
||||
<Button onClick={onCancel}>取消</Button>
|
||||
{!isRichtext && (
|
||||
<Button level="primary" onClick={onConfirm}>
|
||||
确认
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
78
src/renderer/DownloadReport.tsx
Normal file
78
src/renderer/DownloadReport.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import { Button, Renderer } from 'amis';
|
||||
import { RendererProps } from 'amis/lib/factory';
|
||||
import React from 'react';
|
||||
|
||||
@Renderer({
|
||||
test: /(^|\/)download-report$/,
|
||||
name: 'download-report',
|
||||
})
|
||||
export class DownloadReport extends React.Component<RendererProps> {
|
||||
constructor(props: RendererProps) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { data, env, way } = this.props;
|
||||
let selectedU2cs = [];
|
||||
let selectedU2ps = [];
|
||||
let selectedSchedules = [];
|
||||
|
||||
if (way === 'stu') {
|
||||
data.items.forEach((item) => {
|
||||
if (item.user2courses.length)
|
||||
selectedU2cs = selectedU2cs.concat(
|
||||
item.user2courses.map((item) => item.id)
|
||||
);
|
||||
});
|
||||
} else if (way === 'report') {
|
||||
data.items.forEach((item) => {
|
||||
if (
|
||||
item.reports.length &&
|
||||
(item.reports[0].status === 'C' || item.reports[0].status === 'Y')
|
||||
)
|
||||
selectedU2ps.push(item);
|
||||
});
|
||||
} else if (way === 'schedule') {
|
||||
selectedSchedules = data.items.map((item) => item.id);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
level="primary"
|
||||
onClick={() => {
|
||||
if (data.items.length) {
|
||||
if (selectedU2cs.length) {
|
||||
window.open(
|
||||
`/api/report-api/reports/zip/${selectedU2cs}/personal`
|
||||
);
|
||||
} else if (selectedU2ps.length) {
|
||||
const json = JSON.stringify([
|
||||
{
|
||||
name:
|
||||
selectedU2ps[0].user_code + '_' + selectedU2ps[0].user_name,
|
||||
report: selectedU2ps.map((item) => ({
|
||||
id: item.id.toString(),
|
||||
name: item.courses.name + '_' + item.projects.name,
|
||||
})),
|
||||
},
|
||||
]);
|
||||
window.open(`/api/report-api/reports/zip?json=${json}`);
|
||||
} else if (selectedSchedules.length) {
|
||||
window.open(
|
||||
`/api/report-api/reports/zip/${selectedSchedules}/schedule`
|
||||
);
|
||||
} else {
|
||||
env.notify('error', '没有可以导出的报告!');
|
||||
}
|
||||
} else {
|
||||
if (way === 'stu') env.notify('error', '请选择学生!');
|
||||
else if (way === 'report' || way === 'schedule')
|
||||
env.notify('error', '请选择场次!');
|
||||
}
|
||||
}}
|
||||
>
|
||||
{this.props.label}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
47
src/renderer/DownloadReportSchedule.tsx
Normal file
47
src/renderer/DownloadReportSchedule.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Button, Renderer, RendererProps, getVariable } from 'amis';
|
||||
import React from 'react';
|
||||
|
||||
@Renderer({
|
||||
test: /(^|\/)download-report-schedule$/,
|
||||
name: 'download-report-schedule',
|
||||
})
|
||||
export class DownloadReportSchedule extends React.Component<RendererProps> {
|
||||
constructor(props: RendererProps) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
async fetchIDs() {
|
||||
const { data } = this.props;
|
||||
const semester_id = getVariable(data, 'semesterSelect');
|
||||
const id = getVariable(data, 'id');
|
||||
const remote_u2p = await this.props.env.fetcher(
|
||||
`rest/user2projects?schedule_id=eq.${id}&semester_id=eq.${semester_id}`
|
||||
);
|
||||
|
||||
return {
|
||||
u2p: remote_u2p.data.items,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Button
|
||||
level="primary"
|
||||
size="xs"
|
||||
onClick={async () => {
|
||||
const { u2p } = await this.fetchIDs();
|
||||
|
||||
if (u2p.length) {
|
||||
window.open(
|
||||
`/api/report-api/report_paper/zip/${u2p
|
||||
.map((item) => item.id)
|
||||
.toString()}`
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{this.props.label}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
269
src/renderer/ExamImport.tsx
Normal file
269
src/renderer/ExamImport.tsx
Normal file
@@ -0,0 +1,269 @@
|
||||
import { FormControlProps, FormItem, getVariable } from 'amis';
|
||||
import mammoth from 'mammoth/mammoth.browser';
|
||||
import React from 'react';
|
||||
|
||||
@FormItem({
|
||||
test: /(^|\/)examimport$/,
|
||||
name: 'examimport',
|
||||
})
|
||||
export class ExamImportRenderer extends React.Component<
|
||||
FormControlProps,
|
||||
{
|
||||
exam: Array<any>;
|
||||
file: any;
|
||||
}
|
||||
> {
|
||||
constructor(props: FormControlProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
exam: [],
|
||||
file: '',
|
||||
};
|
||||
}
|
||||
|
||||
// word 文件解析方法
|
||||
parseWord(event: any, organization_id: any, category: any, onChange: any) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e: any) => {
|
||||
const arrayBuffer = e.target.result;
|
||||
const options = {
|
||||
convertImage: mammoth.images.imgElement((image: any) => {
|
||||
return image.read().then((res: any) => {
|
||||
let file = new Blob([res], {
|
||||
type: image.contentType,
|
||||
});
|
||||
let formData = new FormData();
|
||||
formData.set('file', file);
|
||||
|
||||
return this.props.env
|
||||
.fetcher({
|
||||
url: 'storage/v1/object/exam',
|
||||
method: 'post',
|
||||
dataType: 'form-data',
|
||||
data: formData,
|
||||
})
|
||||
.then((res) => {
|
||||
return {
|
||||
src: res.data.data.value,
|
||||
};
|
||||
});
|
||||
});
|
||||
}),
|
||||
ignoreEmptyParagraphs: false,
|
||||
};
|
||||
|
||||
mammoth
|
||||
.convertToHtml({ arrayBuffer: arrayBuffer }, options)
|
||||
.then((resultObject: any) => {
|
||||
let temp: any[] = [];
|
||||
let local: any[] = [];
|
||||
|
||||
// 清除粗体、软回车,按空行或题号分割题目,并做 forEach 循环
|
||||
resultObject.value
|
||||
.replace(/<strong>|<\/strong>/g, '')
|
||||
.replace(/<br \/><\/p>/g, '</p>')
|
||||
.replace(/<br \/>/g, '</p><p>')
|
||||
.split(/<p>\s*<\/p>|(?<!^|<p>\s*<\/p>)<p>\d+/g)
|
||||
.forEach((item: any, q_index: any) => {
|
||||
if (item) {
|
||||
// 清除开头的 . 或 、
|
||||
if (!item.match(/^<p>/g)) {
|
||||
item = item.replace(/^(\.|、)/g, '<p>');
|
||||
}
|
||||
|
||||
// 建立当前题目的分割序列
|
||||
let split_series: any[] = [];
|
||||
|
||||
if (item.search(/<p>答案:|<p>答案:/g) > 0) {
|
||||
split_series.push({
|
||||
pattern: /(<p>答案:|<p>答案:)/g,
|
||||
num: item.search(/<p>答案:|<p>答案:/g),
|
||||
dict: 'answer',
|
||||
});
|
||||
}
|
||||
if (item.search(/<p>答案解析:|<p>答案解析:/g) > 0) {
|
||||
split_series.push({
|
||||
pattern: /(<p>答案解析:|<p>答案解析:)/g,
|
||||
num: item.search(/<p>答案解析:|<p>答案解析:/g),
|
||||
dict: 'analysis',
|
||||
});
|
||||
}
|
||||
if (item.search(/<p>难易程度:|<p>难易程度:/g) > 0) {
|
||||
split_series.push({
|
||||
pattern: /(<p>难易程度:|<p>难易程度:)/g,
|
||||
num: item.search(/<p>难易程度:|<p>难易程度:/g),
|
||||
dict: 'difficulty',
|
||||
});
|
||||
}
|
||||
if (item.search(/<p>题型:|<p>题型:/g) > 0) {
|
||||
split_series.push({
|
||||
pattern: /(<p>题型:|<p>题型:)/g,
|
||||
num: item.search(/<p>题型:|<p>题型:/g),
|
||||
dict: 'type',
|
||||
});
|
||||
}
|
||||
|
||||
// 按分割序列各项在题目中的位置排序,从前到后
|
||||
split_series = split_series.sort((a, b) => a.num - b.num);
|
||||
|
||||
let exam = item;
|
||||
let data_item: any = {};
|
||||
let last_split_item: any = {};
|
||||
|
||||
// 按分割序列逐项分割当前题目
|
||||
split_series.forEach((split_item, index) => {
|
||||
let result = exam.split(split_item.pattern);
|
||||
|
||||
// 如果是第一项,必定是题干和选项(如有),其他项则为其他信息
|
||||
if (!index) {
|
||||
// 提取选项
|
||||
let options = result[0].match(
|
||||
/<p>[A-Za-z](\.|、).+?<\/p>(?=<p>[A-Za-z](\.|、)|$)/g
|
||||
);
|
||||
|
||||
// 如果有选项则继续分割题干和选项,否则即为题干
|
||||
if (options) {
|
||||
let title = result[0]
|
||||
.replace(/\n/g, '')
|
||||
.match(/^.+?(?=<p>[A-Za-z](\.|、))/g)[0]
|
||||
.replace(
|
||||
/(<p>\d+(\.|、)\{<\/p>)|(?<=<p>)\d+(\.|、)|(<p>}<\/p>)/g,
|
||||
''
|
||||
)
|
||||
.replace(/(?<=<p>)\s+|\s+(?=<\/p>)/g, '');
|
||||
|
||||
options = options.map((item: any) => {
|
||||
return {
|
||||
key: item.match(/(?<=<p>)[A-Za-z]/g)[0],
|
||||
label: '<p>'
|
||||
.concat(
|
||||
item
|
||||
.match(/(?<=\.|、).+<\/p>/g)[0]
|
||||
.replace(/(<p>{<\/p>)|(<p>}<\/p>)/g, '')
|
||||
)
|
||||
.replace(/(?<=<p>)\s+|\s+(?=<\/p>)/g, ''),
|
||||
is_fixed: false,
|
||||
};
|
||||
});
|
||||
|
||||
// 判断是否有相同的选项
|
||||
let key_set = new Set();
|
||||
let key_count = 0;
|
||||
options.forEach((item: any) => {
|
||||
key_set.add(item.key);
|
||||
key_count++;
|
||||
});
|
||||
if (key_set.size !== key_count) local.push(q_index + 1);
|
||||
|
||||
data_item.options = options;
|
||||
data_item.title = title;
|
||||
} else {
|
||||
let title = result[0]
|
||||
.replace(
|
||||
/(<p>\d+(\.|、)\{<\/p>)|(?<=<p>)\d+(\.|、)|(<p>}<\/p>)/g,
|
||||
''
|
||||
)
|
||||
.replace(/(?<=<p>)\s+|\s+(?=<\/p>)/g, '');
|
||||
|
||||
data_item.title = title;
|
||||
}
|
||||
} else {
|
||||
// 清除 { 、} 和 分割项
|
||||
data_item[last_split_item.dict] = result[0]
|
||||
.replace(/(<p>{<\/p>)|(<p>}<\/p>)/g, '')
|
||||
.replace(last_split_item.pattern, '<p>')
|
||||
.replace(/(?<=<p>)\s+|\s+(?=<\/p>)/g, '');
|
||||
|
||||
// 如果是答案、题型、难易程度,则清除 DOM 标签,并清除空格
|
||||
if (
|
||||
last_split_item.dict === 'answer' ||
|
||||
last_split_item.dict === 'type' ||
|
||||
last_split_item.dict === 'difficulty'
|
||||
) {
|
||||
data_item[last_split_item.dict] = data_item[
|
||||
last_split_item.dict
|
||||
]
|
||||
.replace(/<p>|<\/p>/g, '')
|
||||
.replace(/(?<=<p>)\s+|\s+(?=<\/p>)/g, '');
|
||||
}
|
||||
}
|
||||
|
||||
// 最后一项
|
||||
if (index + 1 === split_series.length) {
|
||||
data_item[split_item.dict] = result[1]
|
||||
.concat(result[2])
|
||||
.replace(/(<p>{<\/p>)|(<p>}<\/p>)/g, '')
|
||||
.replace(split_item.pattern, '<p>')
|
||||
.replace(/(?<=<p>)\s+|\s+(?=<\/p>)/g, '');
|
||||
|
||||
// 如果是答案、题型、难易程度,则清除 DOM 标签,并清除空格
|
||||
if (
|
||||
split_item.dict === 'answer' ||
|
||||
split_item.dict === 'type' ||
|
||||
split_item.dict === 'difficulty'
|
||||
) {
|
||||
data_item[split_item.dict] = data_item[split_item.dict]
|
||||
.replace(/<p>|<\/p>/g, '')
|
||||
.replace(/(?<=<p>)\s+|\s+(?=<\/p>)/g, '');
|
||||
}
|
||||
}
|
||||
|
||||
// 重组分割项和剩余项
|
||||
exam = result[1].concat(result[2]);
|
||||
|
||||
// 保留上一次分割的分割项
|
||||
last_split_item = split_item;
|
||||
});
|
||||
|
||||
if (data_item.type === '判断题') {
|
||||
data_item.options = [
|
||||
{ key: '正确', label: '正确', is_fixed: false },
|
||||
{ key: '错误', label: '错误', is_fixed: false },
|
||||
];
|
||||
}
|
||||
|
||||
data_item.organization_id = organization_id;
|
||||
data_item.category = category;
|
||||
temp.push(data_item);
|
||||
}
|
||||
});
|
||||
this.setState({ exam: temp, file: '' });
|
||||
if (!local.length) {
|
||||
onChange(this.state.exam);
|
||||
} else {
|
||||
this.props.env.notify(
|
||||
'error',
|
||||
`第 ${local.toString()} 题中有重复选项,请修改后重新上传`
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
if (event.target.files.length !== 0) {
|
||||
reader.readAsArrayBuffer(event.target.files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
updateValue(event: any) {
|
||||
this.setState({ file: event.target.value });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { data, onChange } = this.props;
|
||||
const { exam } = this.state;
|
||||
const organization_id = getVariable(data, 'centre_id');
|
||||
const category = getVariable(data, 'tree');
|
||||
return (
|
||||
<form action="">
|
||||
<input
|
||||
type="file"
|
||||
accept=".docx"
|
||||
value={this.state.file}
|
||||
onChange={(event) => {
|
||||
this.parseWord(event, organization_id, category, onChange);
|
||||
this.updateValue(event);
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
298
src/renderer/ExamPaperRule.tsx
Normal file
298
src/renderer/ExamPaperRule.tsx
Normal file
@@ -0,0 +1,298 @@
|
||||
import React from 'react';
|
||||
import { FormItem, FormControlProps, getVariable, isEffectiveApi, Payload, NumberInput } from 'amis';
|
||||
import cx from 'classnames';
|
||||
import { isNumber } from 'lodash';
|
||||
|
||||
@FormItem({
|
||||
test: /(^|\/)custom\-exampaperrule$/,
|
||||
name: 'custom-exampaperrule',
|
||||
})
|
||||
export class ExamPaperRuleRenderer extends React.Component<
|
||||
FormControlProps,
|
||||
{ scopeData: Array<any>; scopes: Array<any>; questionTypes: Array<any> }
|
||||
> {
|
||||
constructor(props: FormControlProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
scopeData: [],
|
||||
scopes: [],
|
||||
questionTypes: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { env, api, scopeApi, onChange } = this.props;
|
||||
|
||||
if (isEffectiveApi(api)) {
|
||||
env.fetcher(api).then((payload: Payload) => {
|
||||
let items = payload.data.items.map((item: any) => {
|
||||
return {
|
||||
type: item.value,
|
||||
name: item.label,
|
||||
number: 0,
|
||||
};
|
||||
});
|
||||
|
||||
this.setState({
|
||||
questionTypes: items,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (isEffectiveApi(scopeApi)) {
|
||||
env.fetcher(scopeApi).then((payload: Payload) => {
|
||||
this.setState({
|
||||
scopeData: payload.data.items,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { data, onChange, className } = this.props;
|
||||
const { scopeData, questionTypes } = this.state;
|
||||
let inputScope = getVariable(data, 'scope');
|
||||
let range = getVariable(data, 'rules.range');
|
||||
|
||||
if (range && range.length > 0) {
|
||||
range = range.filter((x: any) => x !== null && x !== undefined);
|
||||
}
|
||||
if (inputScope && inputScope.length) {
|
||||
inputScope = inputScope.filter(
|
||||
(x: any) => x !== null && x !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
let scopes: Array<any> = [];
|
||||
if (scopeData.length > 0) {
|
||||
if (range && range.length > 0) {
|
||||
// 未设置题目数量
|
||||
if (isNumber(range[0])) {
|
||||
range = range.map((x: any, index: number) => {
|
||||
let category = scopeData.find(
|
||||
(s) => s.id.toString() === x.toString()
|
||||
);
|
||||
return {
|
||||
id: x,
|
||||
name: category?.name,
|
||||
orderNumber: index + 1,
|
||||
category_questions: category?.exam_questions,
|
||||
questions: questionTypes.map(
|
||||
(x: any, qindex: number) => {
|
||||
return {
|
||||
name: x.name,
|
||||
type: x.type,
|
||||
number: 0,
|
||||
};
|
||||
}
|
||||
),
|
||||
};
|
||||
});
|
||||
}
|
||||
scopes = range.map((x: any) => {
|
||||
let category = scopeData.find(
|
||||
(s) => s.id.toString() === x?.id.toString()
|
||||
);
|
||||
return {
|
||||
...x,
|
||||
category_questions: category?.exam_questions,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
scopes = inputScope
|
||||
? inputScope.map((id: any, index: number) => {
|
||||
let q = range?.find((x: any) => x.id === id);
|
||||
let category = scopeData.find(
|
||||
(x) => x.id.toString() === id.toString()
|
||||
);
|
||||
return {
|
||||
id: id,
|
||||
name: category?.name,
|
||||
orderNumber: index + 1,
|
||||
category_questions: category?.exam_questions,
|
||||
questions: questionTypes.map(
|
||||
(x: any, qindex: number) => {
|
||||
let scope = this.state.scopes.find(
|
||||
(x: any) => x.id === id
|
||||
);
|
||||
return {
|
||||
name: x.name,
|
||||
type: x.type,
|
||||
number: q
|
||||
? q.questions[qindex].number
|
||||
: scope
|
||||
? scope.questions[qindex].number
|
||||
: 0,
|
||||
};
|
||||
}
|
||||
),
|
||||
};
|
||||
})
|
||||
: [];
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={``}>
|
||||
<h2>
|
||||
总
|
||||
{scopes.reduce(
|
||||
(acc, cur) =>
|
||||
acc +
|
||||
cur.questions.reduce(
|
||||
(acc: any, cur: any) => acc + cur.number,
|
||||
0
|
||||
),
|
||||
0
|
||||
)}
|
||||
题,共
|
||||
{scopes.reduce(
|
||||
(acc, cur) =>
|
||||
acc +
|
||||
cur.questions
|
||||
.filter((x: any) => isNumber(x.score))
|
||||
.reduce(
|
||||
(acc: any, cur: any) =>
|
||||
acc + cur.number * cur.score,
|
||||
0
|
||||
),
|
||||
0
|
||||
)}
|
||||
分
|
||||
</h2>
|
||||
{scopes.map((scope: any, scopeIndex: number) => (
|
||||
<div className="cxd-Form-item cxd-Form-item-normal mt-2">
|
||||
<label className="cxd-Form-label cxd-Form-item">
|
||||
<span>{`${scope.name}`}</span>
|
||||
</label>
|
||||
<div
|
||||
className={cx(
|
||||
`cxd-NumberControl cxd-Form-control w-full`,
|
||||
className
|
||||
)}
|
||||
>
|
||||
{questionTypes.map((item, index) => {
|
||||
let type_number = scope.category_questions
|
||||
? scope.category_questions.filter(
|
||||
(x: any) => x.type === item.type
|
||||
).length
|
||||
: 0;
|
||||
let question_number = scope.questions[index]
|
||||
? scope.questions[index].number
|
||||
: 0;
|
||||
if (
|
||||
type_number === 0 &&
|
||||
question_number === 0
|
||||
) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<div className="inline mt-2">
|
||||
<label className="m-l cxd-Form-label cxd-Form-itemColumn--2">
|
||||
<span>{`${item.name}`}</span>
|
||||
</label>
|
||||
共({type_number})题, 抽
|
||||
<NumberInput
|
||||
className="w-xs is-inline"
|
||||
value={question_number}
|
||||
step={1}
|
||||
max={type_number}
|
||||
min={0}
|
||||
onChange={(value: number) => {
|
||||
scopes.find(
|
||||
(x: any) =>
|
||||
x.id === scope.id
|
||||
).questions[index].number =
|
||||
value;
|
||||
this.setState({
|
||||
scopes: scopes,
|
||||
questionTypes,
|
||||
});
|
||||
onChange(
|
||||
scopes.map(
|
||||
(
|
||||
x: any,
|
||||
index: number
|
||||
) => {
|
||||
return {
|
||||
id: x.id,
|
||||
name: x.name,
|
||||
orderNumber:
|
||||
index + 1,
|
||||
questions:
|
||||
x.questions,
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{type_number > 0 ? (
|
||||
<>
|
||||
, 每题
|
||||
<NumberInput
|
||||
className="w-xs is-inline"
|
||||
value={
|
||||
scope.questions[index]
|
||||
? scope.questions[
|
||||
index
|
||||
].score
|
||||
: 0
|
||||
}
|
||||
step={1}
|
||||
max={
|
||||
type_number === 0
|
||||
? 0
|
||||
: 100
|
||||
}
|
||||
min={0}
|
||||
onChange={(
|
||||
value: number
|
||||
) => {
|
||||
scopes.find(
|
||||
(x: any) =>
|
||||
x.id ===
|
||||
scope.id
|
||||
).questions[
|
||||
index
|
||||
].score = value;
|
||||
this.setState({
|
||||
scopes: scopes,
|
||||
questionTypes,
|
||||
});
|
||||
onChange(
|
||||
scopes.map(
|
||||
(
|
||||
x: any,
|
||||
index: number
|
||||
) => {
|
||||
return {
|
||||
id: x.id,
|
||||
name: x.name,
|
||||
orderNumber:
|
||||
index +
|
||||
1,
|
||||
questions:
|
||||
x.questions,
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
分
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<br />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user