mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 07:06:06 +00:00
Merge branch 'main' into chore/kanban
This commit is contained in:
commit
46d8c00f76
@ -13,6 +13,7 @@ const formatData = (
|
|||||||
hideChildren: boolean = false,
|
hideChildren: boolean = false,
|
||||||
) => {
|
) => {
|
||||||
data.forEach((item: any) => {
|
data.forEach((item: any) => {
|
||||||
|
const percent=item[fieldNames.progress] * 100;
|
||||||
if (item.children && item.children.length) {
|
if (item.children && item.children.length) {
|
||||||
tasks.push({
|
tasks.push({
|
||||||
start: new Date(item[fieldNames.start]),
|
start: new Date(item[fieldNames.start]),
|
||||||
@ -20,7 +21,7 @@ const formatData = (
|
|||||||
name: item[fieldNames.title],
|
name: item[fieldNames.title],
|
||||||
id: item.id + '',
|
id: item.id + '',
|
||||||
type: 'project',
|
type: 'project',
|
||||||
progress: item[fieldNames.progress] * 100 || 0,
|
progress: percent>100?100:percent || 0,
|
||||||
hideChildren: hideChildren,
|
hideChildren: hideChildren,
|
||||||
project: projectId,
|
project: projectId,
|
||||||
color: item.color,
|
color: item.color,
|
||||||
@ -33,7 +34,7 @@ const formatData = (
|
|||||||
name: item[fieldNames.title],
|
name: item[fieldNames.title],
|
||||||
id: item.id + '',
|
id: item.id + '',
|
||||||
type: fieldNames.end ? 'task' : 'milestone',
|
type: fieldNames.end ? 'task' : 'milestone',
|
||||||
progress: item[fieldNames.progress] * 100 || 0,
|
progress: percent>100?100:percent || 0,
|
||||||
project: projectId,
|
project: projectId,
|
||||||
color: item.color,
|
color: item.color,
|
||||||
});
|
});
|
||||||
|
@ -718,7 +718,17 @@ export const useDestroyActionProps = () => {
|
|||||||
await resource.destroy({
|
await resource.destroy({
|
||||||
filterByTk,
|
filterByTk,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { count = 0, page = 0, pageSize = 0 } = service?.data?.meta || {};
|
||||||
|
if (count % pageSize === 1) {
|
||||||
|
service.run({
|
||||||
|
...service?.params?.[0],
|
||||||
|
page: page - 1,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
service?.refresh?.();
|
service?.refresh?.();
|
||||||
|
}
|
||||||
|
|
||||||
if (block !== 'TableField') {
|
if (block !== 'TableField') {
|
||||||
__parent?.service?.refresh?.();
|
__parent?.service?.refresh?.();
|
||||||
setVisible?.(false);
|
setVisible?.(false);
|
||||||
|
@ -39,7 +39,7 @@ export const TaskGantt: React.FC<TaskGanttProps> = forwardRef(
|
|||||||
width={gridProps.svgWidth}
|
width={gridProps.svgWidth}
|
||||||
height={calendarProps.headerHeight}
|
height={calendarProps.headerHeight}
|
||||||
fontFamily={barProps.fontFamily}
|
fontFamily={barProps.fontFamily}
|
||||||
style={{ borderBottom: '1px solid #f0f0f0',fontWeight:700 }}
|
style={{ borderBottom: '1px solid #f0f0f0', fontWeight: 700 }}
|
||||||
>
|
>
|
||||||
<Calendar {...calendarProps} />
|
<Calendar {...calendarProps} />
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -98,7 +98,7 @@ export const TaskItem: React.FC<TaskItemProps> = (props) => {
|
|||||||
{taskItem}
|
{taskItem}
|
||||||
<text
|
<text
|
||||||
x={isProjectBar ? task.x1 : getX()}
|
x={isProjectBar ? task.x1 : getX()}
|
||||||
y={isProjectBar ? task.y - 8 : task.y + taskHeight * 0.5}
|
y={isProjectBar ? task.y - 8 : isTextInside ? task.y + taskHeight * 0.5 : task.y + taskHeight * 0.65}
|
||||||
className={isProjectBar ? cx(projectLabel) : isTextInside ? cx(barLabel) : cx(barLabel) && cx(barLabelOutside)}
|
className={isProjectBar ? cx(projectLabel) : isTextInside ? cx(barLabel) : cx(barLabel) && cx(barLabelOutside)}
|
||||||
ref={textRef}
|
ref={textRef}
|
||||||
>
|
>
|
||||||
|
@ -58,7 +58,7 @@ ReadPretty.TextArea = (props) => {
|
|||||||
value
|
value
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div className={cls(prefixCls, props.className)} style={props.style}>
|
<div className={cls(prefixCls, props.className)} style={{ overflowWrap: 'break-word', ...props.style }}>
|
||||||
{props.addonBefore}
|
{props.addonBefore}
|
||||||
{props.prefix}
|
{props.prefix}
|
||||||
{content}
|
{content}
|
||||||
@ -94,7 +94,7 @@ ReadPretty.Html = (props) => {
|
|||||||
</EllipsisWithTooltip>
|
</EllipsisWithTooltip>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div className={cls(prefixCls, props.className)} style={props.style}>
|
<div className={cls(prefixCls, props.className)} style={{ overflowWrap: 'break-word', ...props.style }}>
|
||||||
{props.addonBefore}
|
{props.addonBefore}
|
||||||
{props.prefix}
|
{props.prefix}
|
||||||
{content}
|
{content}
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
.nb-markdown {
|
.nb-markdown {
|
||||||
line-height: 1.612;
|
line-height: 1.612;
|
||||||
a {
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nb-markdown > *:last-child {
|
.nb-markdown > *:last-child {
|
||||||
|
@ -131,7 +131,7 @@ const usePaginationProps = (pagination1, pagination2) => {
|
|||||||
...pagination1,
|
...pagination1,
|
||||||
...pagination2,
|
...pagination2,
|
||||||
};
|
};
|
||||||
return result.total < result.pageSize ? false : result;
|
return result.total <= result.pageSize ? false : result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const useValidator = (validator: (value: any) => string) => {
|
const useValidator = (validator: (value: any) => string) => {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import { Button, Cascader, Popover, Input as AntInput } from 'antd';
|
import { Button } from 'antd';
|
||||||
import { css } from "@emotion/css";
|
import { css } from "@emotion/css";
|
||||||
|
|
||||||
import { Input } from "../input";
|
import { Input } from "../input";
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { XButton } from './XButton';
|
import { VariableSelect } from './VariableSelect';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -21,7 +21,6 @@ export function JSONInput(props) {
|
|||||||
const inputRef = useRef<any>(null);
|
const inputRef = useRef<any>(null);
|
||||||
const { value, space = 2, scope } = props;
|
const { value, space = 2, scope } = props;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [selectedVar, setSelectedVar] = useState<string[]>([]);
|
|
||||||
const options = typeof scope === 'function' ? scope() : (scope ?? []);
|
const options = typeof scope === 'function' ? scope() : (scope ?? []);
|
||||||
|
|
||||||
function onFormat() {
|
function onFormat() {
|
||||||
@ -72,24 +71,7 @@ export function JSONInput(props) {
|
|||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<Button onClick={onFormat}>{t('Prettify')}</Button>
|
<Button onClick={onFormat}>{t('Prettify')}</Button>
|
||||||
<Popover
|
<VariableSelect options={options} onInsert={onInsert} />
|
||||||
content={(
|
|
||||||
<AntInput.Group compact>
|
|
||||||
<Cascader
|
|
||||||
placeholder={t('Select a variable')}
|
|
||||||
value={selectedVar}
|
|
||||||
options={options}
|
|
||||||
onChange={(keyPaths) => setSelectedVar(keyPaths as string[])}
|
|
||||||
changeOnSelect
|
|
||||||
/>
|
|
||||||
<Button onClick={onInsert}>{t('Insert')}</Button>
|
|
||||||
</AntInput.Group>
|
|
||||||
)}
|
|
||||||
trigger="click"
|
|
||||||
placement="topRight"
|
|
||||||
>
|
|
||||||
<XButton />
|
|
||||||
</Popover>
|
|
||||||
</Button.Group>
|
</Button.Group>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import React, { useState, useEffect, useRef, useMemo } from 'react';
|
import React, { useState, useEffect, useRef, useMemo } from 'react';
|
||||||
import { Input, Cascader, Button, Tag } from 'antd';
|
import { Input } from 'antd';
|
||||||
import { useForm } from '@formily/react';
|
import { useForm } from '@formily/react';
|
||||||
import { cx, css } from '@emotion/css';
|
import { cx, css } from '@emotion/css';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import * as sanitizeHTML from 'sanitize-html';
|
import * as sanitizeHTML from 'sanitize-html';
|
||||||
|
|
||||||
import { EllipsisWithTooltip, useCompile } from '../..';
|
import { EllipsisWithTooltip, useCompile } from '../..';
|
||||||
import { useRecord } from '../../../record-provider';
|
import { VariableSelect } from './VariableSelect';
|
||||||
|
|
||||||
type RangeIndexes = [number, number, number, number];
|
type RangeIndexes = [number, number, number, number];
|
||||||
|
|
||||||
@ -341,19 +341,6 @@ export function TextArea(props) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-button{
|
|
||||||
.ant-select.ant-cascader{
|
|
||||||
position: absolute;
|
|
||||||
top: -1px;
|
|
||||||
left: -1px;
|
|
||||||
min-width: auto;
|
|
||||||
width: calc(100% + 2px);
|
|
||||||
height: calc(100% + 2px);
|
|
||||||
overflow: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -382,63 +369,7 @@ export function TextArea(props) {
|
|||||||
dangerouslySetInnerHTML={{ __html: html }}
|
dangerouslySetInnerHTML={{ __html: html }}
|
||||||
/>
|
/>
|
||||||
{!disabled
|
{!disabled
|
||||||
? (
|
? <VariableSelect options={options} onInsert={onInsert} />
|
||||||
<Button className={cx('x-button', css`
|
|
||||||
position: relative;
|
|
||||||
`)}>
|
|
||||||
<span
|
|
||||||
className={css`
|
|
||||||
font-style: italic;
|
|
||||||
font-family: "New York", "Times New Roman", Times, serif;
|
|
||||||
`}
|
|
||||||
>x</span>
|
|
||||||
<Cascader
|
|
||||||
placeholder={t('Select a variable')}
|
|
||||||
value={[]}
|
|
||||||
options={options}
|
|
||||||
onChange={(keyPaths = [], selectedOptions = []) => {
|
|
||||||
setSelectedVar(keyPaths as string[]);
|
|
||||||
if (!keyPaths.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const option = selectedOptions[selectedOptions.length - 1];
|
|
||||||
if (!option?.children?.length) {
|
|
||||||
onInsert(keyPaths);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
changeOnSelect
|
|
||||||
onClick={(e: any) => {
|
|
||||||
if (e.detail !== 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (let n = e.target; n && n !== e.currentTarget; n = n.parentNode) {
|
|
||||||
if (Array.from(n.classList ?? []).includes('ant-cascader-menu-item')) {
|
|
||||||
onInsert(selectedVar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
dropdownClassName={css`
|
|
||||||
.ant-cascader-menu{
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
dropdownRender={(menu) => (
|
|
||||||
<>
|
|
||||||
{menu}
|
|
||||||
<div
|
|
||||||
className={css`
|
|
||||||
padding: .5em;
|
|
||||||
border-top: 1px solid rgba(0, 0, 0, .06);
|
|
||||||
color: rgba(0, 0, 0, .45);
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
{t('Double click to choose entire object')}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
</Input.Group>
|
</Input.Group>
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
import { css, cx } from "@emotion/css";
|
||||||
|
import { Button, Cascader } from "antd";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
export function VariableSelect(props) {
|
||||||
|
const { options, onInsert } = props;
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [selectedVar, setSelectedVar] = useState<string[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedVar([]);
|
||||||
|
}, [options]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button className={cx('x-button', css`
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.ant-select.ant-cascader{
|
||||||
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
left: -1px;
|
||||||
|
min-width: auto;
|
||||||
|
width: calc(100% + 2px);
|
||||||
|
height: calc(100% + 2px);
|
||||||
|
overflow: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
`)}>
|
||||||
|
<span
|
||||||
|
className={css`
|
||||||
|
font-style: italic;
|
||||||
|
font-family: "New York", "Times New Roman", Times, serif;
|
||||||
|
`}
|
||||||
|
>x</span>
|
||||||
|
<Cascader
|
||||||
|
placeholder={t('Select a variable')}
|
||||||
|
value={[]}
|
||||||
|
options={options}
|
||||||
|
onChange={(keyPaths = [], selectedOptions = []) => {
|
||||||
|
setSelectedVar(keyPaths as string[]);
|
||||||
|
if (!keyPaths.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const option = selectedOptions[selectedOptions.length - 1];
|
||||||
|
if (!option?.children?.length) {
|
||||||
|
onInsert(keyPaths);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
changeOnSelect
|
||||||
|
onClick={(e: any) => {
|
||||||
|
if (e.detail !== 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let n = e.target; n && n !== e.currentTarget; n = n.parentNode) {
|
||||||
|
if (Array.from(n.classList ?? []).includes('ant-cascader-menu-item')) {
|
||||||
|
onInsert(selectedVar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
dropdownClassName={css`
|
||||||
|
.ant-cascader-menu{
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
dropdownRender={(menu) => (
|
||||||
|
<>
|
||||||
|
{menu}
|
||||||
|
<div
|
||||||
|
className={css`
|
||||||
|
padding: .5em;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, .06);
|
||||||
|
color: rgba(0, 0, 0, .45);
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{t('Double click to choose entire object')}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
@ -19,15 +19,13 @@ export default {
|
|||||||
type: 'number',
|
type: 'number',
|
||||||
title: `{{t("End Status", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("End Status", { ns: "${NAMESPACE}" })}}`,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'Select',
|
'x-component': 'Radio.Group',
|
||||||
'x-component-props': {
|
|
||||||
placeholder: `{{t("Select status", { ns: "${NAMESPACE}" })}}`,
|
|
||||||
},
|
|
||||||
enum: [
|
enum: [
|
||||||
{ label: `{{t("Succeed and continue", { ns: "${NAMESPACE}" })}}`, value: JOB_STATUS.RESOLVED },
|
{ label: `{{t("Succeed and continue", { ns: "${NAMESPACE}" })}}`, value: JOB_STATUS.RESOLVED },
|
||||||
{ label: `{{t("Fail and exit", { ns: "${NAMESPACE}" })}}`, value: JOB_STATUS.FAILED },
|
{ label: `{{t("Fail and exit", { ns: "${NAMESPACE}" })}}`, value: JOB_STATUS.FAILED },
|
||||||
],
|
],
|
||||||
required: true
|
required: true,
|
||||||
|
default: JOB_STATUS.RESOLVED
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
view: {
|
view: {
|
||||||
|
@ -423,7 +423,6 @@ export function NodeDefaultView(props) {
|
|||||||
'x-component': 'fieldset',
|
'x-component': 'fieldset',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
className: css`
|
className: css`
|
||||||
.ant-input,
|
|
||||||
.ant-select,
|
.ant-select,
|
||||||
.ant-cascader-picker,
|
.ant-cascader-picker,
|
||||||
.ant-picker,
|
.ant-picker,
|
||||||
@ -434,6 +433,15 @@ export function NodeDefaultView(props) {
|
|||||||
min-width: 6em;
|
min-width: 6em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-input-affix-wrapper{
|
||||||
|
&:not(.full-width){
|
||||||
|
.ant-input{
|
||||||
|
width: auto;
|
||||||
|
min-width: 6em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
`
|
`
|
||||||
},
|
},
|
||||||
properties: instruction.fieldset
|
properties: instruction.fieldset
|
||||||
|
Loading…
Reference in New Issue
Block a user