mirror of
https://gitee.com/baidu/amis.git
synced 2024-12-02 12:08:13 +08:00
feat:「页面设计器」统一表达式组件 (#6552)
* 「页面设计器」统一表达式组件 * feat: 「页面设计器」统一表达式组件 --------- Co-authored-by: wutong25 <wutong25@baidu.com>
This commit is contained in:
parent
e42a68f360
commit
d578e0a27e
@ -1,11 +1,27 @@
|
||||
.ae-ExpressionFormulaControl {
|
||||
background-color: #fff;
|
||||
|
||||
.btn-configured,
|
||||
.btn-set-expression {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
}
|
||||
.btn-configured {
|
||||
position: relative;
|
||||
justify-content: left;
|
||||
padding-right: 32px;
|
||||
padding-left: 4px;
|
||||
& > span {
|
||||
max-width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
background-color: #007bff;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -25,9 +25,9 @@ div.ae-editor-FormulaControl {
|
||||
|
||||
& &-input {
|
||||
flex: 1;
|
||||
border-radius: var(--Form-select-borderRadius) 0 0
|
||||
var(--Form-select-borderRadius);
|
||||
max-width: calc(100% - 35px); // 避免表达式内容太长撑开面板
|
||||
border-radius: var(--input-default-default-top-left-border-radius) 0 0
|
||||
var(--input-default-default-bottom-left-border-radius);
|
||||
max-width: calc(100% - 29px); // 避免表达式内容太长撑开面板
|
||||
|
||||
&.is-clearable {
|
||||
padding-right: 30px; // 避免间隙过大
|
||||
@ -85,8 +85,8 @@ div.ae-editor-FormulaControl {
|
||||
}
|
||||
> div,
|
||||
&:not(.border-wrapper) > div > div {
|
||||
border-radius: var(--Form-select-borderRadius) 0 0
|
||||
var(--Form-select-borderRadius);
|
||||
border-radius: var(--input-default-default-top-left-border-radius) 0 0
|
||||
var(--input-default-default-bottom-left-border-radius);
|
||||
}
|
||||
|
||||
&.border-wrapper {
|
||||
@ -97,21 +97,28 @@ div.ae-editor-FormulaControl {
|
||||
|
||||
&-tooltipBox {
|
||||
flex: 1;
|
||||
max-width: calc(100% - 35px); // 避免表达式内容太长撑开面板
|
||||
max-width: calc(100% - 29px); // 避免表达式内容太长撑开面板
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-ResultBox-wrapper {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
& &-ResultBox {
|
||||
flex-wrap: nowrap !important; // 不换行,避免被其他样式覆盖
|
||||
white-space: nowrap;
|
||||
padding: 0 0 0 0.25rem;
|
||||
border-radius: var(--Form-select-borderRadius) 0 0
|
||||
var(--Form-select-borderRadius);
|
||||
font-size: 12px;
|
||||
border-radius: var(--input-default-default-top-left-border-radius) 0 0
|
||||
var(--input-default-default-bottom-left-border-radius);
|
||||
|
||||
&.is-clearable {
|
||||
& {
|
||||
> div:first-child {
|
||||
max-width: calc(100% - 30px); // 避免表达式内容太长撑开面板
|
||||
background: #f7f7f9;
|
||||
color: #151b26;
|
||||
// background: var(--button-primary-default-bg-color);
|
||||
background: #007bff;
|
||||
color: #fff;
|
||||
padding: 1px 5px;
|
||||
border-radius: 4px 0 0 4px;
|
||||
height: 26px;
|
||||
@ -122,9 +129,11 @@ div.ae-editor-FormulaControl {
|
||||
}
|
||||
}
|
||||
> div:last-child {
|
||||
background: #f7f7f9;
|
||||
// background: var(--button-primary-default-bg-color);
|
||||
background: #007bff;
|
||||
margin-right: 0.75rem;
|
||||
border-radius: 0 4px 4px 0;
|
||||
color: #fff;
|
||||
height: 26px;
|
||||
svg {
|
||||
fill: var(--Form-input-clearBtn-color);
|
||||
@ -136,12 +145,7 @@ div.ae-editor-FormulaControl {
|
||||
}
|
||||
|
||||
span.c-field {
|
||||
background: #007bff;
|
||||
padding: 3px 5px;
|
||||
margin: 0 1px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span.c-func {
|
||||
@ -152,9 +156,24 @@ div.ae-editor-FormulaControl {
|
||||
}
|
||||
}
|
||||
|
||||
.input-clear-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 8px;
|
||||
margin: auto;
|
||||
height: 14px;
|
||||
fill: var(--Form-input-clearBtn-color);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
fill: var(--Form-input-clearBtn-color-onHover);
|
||||
}
|
||||
}
|
||||
|
||||
&-icon {
|
||||
top: 0 !important;
|
||||
font-size: #{px2rem(18px)};
|
||||
font-size: #{px2rem(14px)};
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: var(--fontSizeSm);
|
||||
|
@ -15,6 +15,7 @@
|
||||
border-radius: var(--Form-input-borderRadius);
|
||||
background: var(--Form-input-bg);
|
||||
font-size: var(--Form-input-fontSize);
|
||||
padding-bottom: 26px;
|
||||
&::placeholder {
|
||||
color: var(--Form-input-placeholderColor);
|
||||
}
|
||||
@ -27,15 +28,11 @@
|
||||
height: 100%;
|
||||
border-radius: var(--Form-input-borderRadius);
|
||||
overflow: auto;
|
||||
padding-left: 8px;
|
||||
|
||||
& > .CodeMirror {
|
||||
height: 100%;
|
||||
padding-bottom: 26px;
|
||||
.CodeMirror-lines {
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
.CodeMirror-vscrollbar {
|
||||
margin-bottom: 26px;
|
||||
}
|
||||
font-family: inherit;
|
||||
|
||||
// 解决上下 pre标签中表达式浮层遮挡问题
|
||||
.CodeMirror-measure + div {
|
||||
@ -46,78 +43,16 @@
|
||||
z-index: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cm-expression {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
.expression-popover {
|
||||
&-placeholder {
|
||||
position: absolute;
|
||||
display: none;
|
||||
left: 0;
|
||||
bottom: -30px;
|
||||
transform: translate(calc(24px - 50%));
|
||||
line-height: 28px;
|
||||
top: 0;
|
||||
left: 13px;
|
||||
font-size: 12px;
|
||||
width: max-content;
|
||||
color: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 2px 8px;
|
||||
background: var(--Tooltip-bg--dark);
|
||||
border: none;
|
||||
box-shadow: var(--Tooltip-boxShadow--dark);
|
||||
z-index: 10;
|
||||
|
||||
.expression-popover-arrow {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: var(--Tooltip-arrow-width);
|
||||
height: var(--Tooltip-arrow-height);
|
||||
margin-left: calc(var(--Tooltip-arrow-width) * -1 / 2);
|
||||
left: 50%;
|
||||
top: calc(var(--Tooltip-arrow-height) * -1);
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
content: '';
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
border-width: 0 calc(var(--Tooltip-arrow-width) / 2)
|
||||
var(--Tooltip-arrow-height) calc(var(--Tooltip-arrow-width) / 2);
|
||||
}
|
||||
&::before {
|
||||
border-width: 0;
|
||||
}
|
||||
&::after {
|
||||
border-bottom-color: var(--Tooltip-arrow-color--dark);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cm-expression-text {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
padding: 0 5px;
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
margin: 0 1px 1px;
|
||||
background: var(--button-primary-default-bg-color);
|
||||
cursor: pointer;
|
||||
width: 40px;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
|
||||
&:hover {
|
||||
background: var(--button-primary-hover-bg-color);
|
||||
& ~ .expression-popover {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
color: var(--text--muted-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&-footer {
|
||||
@ -143,7 +78,7 @@
|
||||
}
|
||||
&-fxIcon {
|
||||
> a {
|
||||
font-size: 19px;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -199,3 +134,83 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cm-expression {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
border-radius: 4px;
|
||||
padding: 0 5px;
|
||||
margin: 0 1px 1px;
|
||||
background: var(--button-primary-default-bg-color);
|
||||
|
||||
&-text {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
max-width: 80px;
|
||||
min-width: 10px;
|
||||
min-height: 18px;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
|
||||
&:hover {
|
||||
background: var(--button-primary-hover-bg-color);
|
||||
& ~ .cm-expression-popover {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-close {
|
||||
font-size: 12px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&-popover {
|
||||
position: absolute;
|
||||
display: none;
|
||||
left: 0;
|
||||
bottom: -30px;
|
||||
transform: translate(calc(24px - 50%));
|
||||
font-size: 12px;
|
||||
width: max-content;
|
||||
color: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 2px 8px;
|
||||
background: var(--Tooltip-bg--dark);
|
||||
border: none;
|
||||
box-shadow: var(--Tooltip-boxShadow--dark);
|
||||
z-index: 10;
|
||||
|
||||
&-arrow {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: var(--Tooltip-arrow-width);
|
||||
height: var(--Tooltip-arrow-height);
|
||||
margin-left: calc(var(--Tooltip-arrow-width) * -1 / 2);
|
||||
left: 50%;
|
||||
top: calc(var(--Tooltip-arrow-height) * -1);
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
content: '';
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
border-width: 0 calc(var(--Tooltip-arrow-width) / 2)
|
||||
var(--Tooltip-arrow-height) calc(var(--Tooltip-arrow-width) / 2);
|
||||
}
|
||||
&::before {
|
||||
border-width: 0;
|
||||
}
|
||||
&::after {
|
||||
border-bottom-color: var(--Tooltip-arrow-color--dark);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
133
packages/amis-editor-core/scss/control/_tpl-formula-control.scss
Normal file
133
packages/amis-editor-core/scss/control/_tpl-formula-control.scss
Normal file
@ -0,0 +1,133 @@
|
||||
.ae-TplFormulaControl {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
height: 32px;
|
||||
|
||||
.ae-TplResultBox {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
overflow-x: auto;
|
||||
border: 1px solid var(--Form-input-borderColor);
|
||||
border-radius: var(--input-default-default-top-left-border-radius) 0 0
|
||||
var(--input-default-default-bottom-left-border-radius);
|
||||
background: var(--Form-input-bg);
|
||||
font-size: var(--Form-input-fontSize);
|
||||
padding: 0 8px;
|
||||
|
||||
.input-clear-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 8px;
|
||||
margin: auto;
|
||||
height: 14px;
|
||||
fill: var(--Form-input-clearBtn-color);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
fill: var(--Form-input-clearBtn-color-onHover);
|
||||
}
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--Form-input-placeholderColor);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: var(--Form-input-onHover-borderColor);
|
||||
}
|
||||
|
||||
&-editor {
|
||||
border-radius: var(--Form-input-borderRadius);
|
||||
& > .CodeMirror {
|
||||
height: 100%;
|
||||
color: var(--Form-input-color);
|
||||
font-family: inherit;
|
||||
|
||||
// 解决上下 pre标签中表达式浮层遮挡问题
|
||||
.CodeMirror-measure + div {
|
||||
z-index: inherit !important;
|
||||
}
|
||||
pre.CodeMirror-line,
|
||||
.CodeMirror pre.CodeMirror-line-like {
|
||||
z-index: initial;
|
||||
}
|
||||
|
||||
.CodeMirror-sizer {
|
||||
min-height: 30px !important;
|
||||
}
|
||||
|
||||
.CodeMirror-hscrollbar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.CodeMirror-lines {
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cm-expression {
|
||||
background: #007bff;
|
||||
padding: 0px 5px;
|
||||
margin: 0 1px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&-button {
|
||||
height: auto;
|
||||
background-color: #f7f7f9;
|
||||
padding: 4px 8px;
|
||||
border-radius: 0 var(--input-default-default-top-left-border-radius)
|
||||
var(--input-default-default-bottom-left-border-radius) 0;
|
||||
border-left: 0;
|
||||
&:not(:disabled):not(.is-disabled) {
|
||||
&:hover,
|
||||
&:hover:active {
|
||||
border-color: #e6f0ff;
|
||||
background-color: #e6f0ff;
|
||||
border-left-width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-icon {
|
||||
font-size: #{px2rem(14px)};
|
||||
&:not(:last-child) {
|
||||
margin-right: var(--fontSizeSm);
|
||||
}
|
||||
|
||||
&.is-filled {
|
||||
fill: var(--primary);
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
&-placeholder {
|
||||
position: absolute;
|
||||
line-height: 30px;
|
||||
top: 0;
|
||||
left: 14px;
|
||||
font-size: 12px;
|
||||
color: var(--text--muted-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&-tooltip {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
height: 18px;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.clearable {
|
||||
.ae-TplResultBox {
|
||||
padding-right: 28px;
|
||||
}
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@
|
||||
@import './control/feature-control';
|
||||
@import './control/databinding-control';
|
||||
@import './control/event-action';
|
||||
@import './control/tpl-formula-control';
|
||||
@import './control/timeline_item_control';
|
||||
@import './control/tree_option_control';
|
||||
@import './control/_inpupt-file';
|
||||
|
@ -153,6 +153,10 @@ import layout_3_2 from './layout/layout3-2.svg';
|
||||
import layout_free_container from './layout/layout-free-container.svg';
|
||||
import layout_fixed_top from './layout/layout-fixed-top.svg';
|
||||
|
||||
// 其他类 icon
|
||||
import inputAddFx from './other/+fx.svg';
|
||||
import inputFx from './other/fx.svg';
|
||||
|
||||
// 属性配置面板/显示类型
|
||||
import block from './display/block.svg';
|
||||
import inline from './display/inline.svg';
|
||||
@ -314,6 +318,9 @@ registerIcon('layout-3-2-plugin', layout_3_2);
|
||||
registerIcon('layout-free-container', layout_free_container);
|
||||
registerIcon('layout-fixed-top', layout_fixed_top);
|
||||
|
||||
registerIcon('input-add-fx', inputAddFx);
|
||||
registerIcon('input-fx', inputFx);
|
||||
|
||||
// 属性配置面板/显示类型
|
||||
registerIcon('inline-display', inline);
|
||||
registerIcon('inline-block-display', inline_block);
|
||||
|
11
packages/amis-editor/src/icons/other/+fx.svg
Normal file
11
packages/amis-editor/src/icons/other/+fx.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g>
|
||||
<rect x="0" y="0" width="16" height="16"></rect>
|
||||
<path d="M9.89726996,1.42896935 C10.7622099,0.875052946 11.5432726,0.94041116 12.2245758,1.01677633 C12.5114403,1.04904331 13.1427595,1.10712387 13.0775631,1.72880092 C13.0264925,2.22571234 12.2929786,2.16645279 12.0985292,2.17516075 C10.969708,2.22571234 10.2688899,1.60213545 9.57889382,4.10820386 L9.01001898,6.66018234 L9.89726996,6.65262698 C10.1971738,6.65262698 10.440574,6.89355373 10.440574,7.1904099 C10.440574,7.48726607 10.1971738,7.72819282 9.89726996,7.72819282 L8.77857147,7.73574818 L7.23361155,13.8963048 C7.23361155,13.8963048 7.09235251,15.042858 6.38062427,14.9987598 C6.35250991,14.9970159 6.32580901,14.9939663 6.30045749,14.9897032 L6.36964031,14.9691045 C6.31633924,14.9895282 6.2597435,15 6.20266347,15 L4.96959782,15 C4.71186493,15 4.50293115,14.7910662 4.50293115,14.5333333 L4.50293115,14.5005208 C4.5022854,14.259116 4.6858559,14.0570646 4.92622202,14.0346853 C5.29023392,14.0001846 5.620688,13.9495418 5.91758425,13.8827569 C5.92851129,13.830951 5.93837482,13.8005794 5.93837482,13.8005794 L7.47138205,7.73574818 L6.2633946,7.73574818 C5.97978992,7.73574818 5.75377545,7.51848389 5.72987008,7.2442146 C5.75377545,6.74084978 5.97978992,6.66018234 6.2633946,6.66018234 L7.70500277,6.66018234 L8.29126334,4.07593689 C8.53249032,2.94013936 9.03015678,1.98503689 9.89726996,1.42896935 Z M14.7815274,5.87796146 C14.9187471,5.96723342 14.9900301,6.07048774 14.9989404,6.18664885 C15.0069598,6.30280996 14.9695362,6.41681994 14.8839967,6.52975435 L13.2738929,8.6378634 L14.8626118,11.2676219 C14.9125099,11.3461382 14.9249844,11.440788 14.8964712,11.5440423 C14.8715222,11.6494478 14.7592515,11.7225862 14.6104483,11.8193872 C14.4491706,11.9215659 14.3092778,11.9118858 14.2094817,11.8785433 C14.1123587,11.8441252 14.0250371,11.7666844 13.951972,11.6494478 L12.6118524,9.50907173 L10.98571,11.6386921 C10.7914639,11.8925256 10.5713778,11.9301704 10.3201054,11.7505509 C10.2167451,11.6774125 10.0278453,11.4063699 10.0055693,11.2837554 C9.98418444,11.1622164 10.0242811,11.0342241 10.1276414,10.8976272 L11.925754,8.53568465 L10.6159296,6.44370909 C10.5615764,6.34798373 10.5562302,6.23612488 10.5989999,6.10920811 C10.6399876,5.98229134 10.7371107,5.90700173 10.840471,5.83278769 C10.9892741,5.7316845 11.1256028,5.7284578 11.22629,5.7381379 C11.3278682,5.74566686 11.419645,5.81557864 11.5042935,5.94464654 L12.5931407,7.65802292 L13.9109843,5.92528635 C14.0250371,5.772556 14.1542374,5.71877771 14.2789826,5.67575508 C14.4028367,5.63273244 14.6478719,5.78761392 14.7815274,5.87796146 Z" fill="currentColor" fill-rule="nonzero"></path>
|
||||
<rect fill="currentColor" x="0.897847116" y="6.66018234" width="4.5" height="1" rx="0.5"></rect>
|
||||
<rect fill="currentColor" transform="translate(3.147847, 7.160182) rotate(-270.000000) translate(-3.147847, -7.160182) " x="0.897847116" y="6.66018234" width="4.5" height="1" rx="0.5"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.1 KiB |
9
packages/amis-editor/src/icons/other/fx.svg
Normal file
9
packages/amis-editor/src/icons/other/fx.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g>
|
||||
<rect x="0" y="0" width="16" height="16"></rect>
|
||||
<path d="M6.85458095,1.4369501 C7.71952092,0.883033689 9.14623722,0.984136878 9.82754043,1.06050205 C10.1144049,1.09276903 10.7457242,1.15084958 10.6805277,1.77252664 C10.6294571,2.26943806 9.89599674,2.21888646 9.7014939,2.21888646 C9.48535769,2.21888646 9.2659886,2.19876183 9.04755999,2.18089337 L8.82959943,2.1646059 C7.92402473,2.10678268 7.05892911,2.21764793 6.53620481,4.11618461 L6.21348223,5.68866187 L7.84556744,5.68866187 C8.14547125,5.68866187 8.38887144,5.92958862 8.38887144,6.22644479 C8.38887144,6.52330096 8.14547125,6.76422771 7.84556744,6.76422771 L5.98203473,6.76422771 L4.51511393,13.8963048 C4.51511393,13.8963048 4.37385489,15.042858 3.66212666,14.9987598 C3.63797714,14.9972618 3.61487053,14.9948005 3.5927662,14.9914342 C3.56428372,14.9965963 3.53191413,15 3.49937138,15 L1.46666667,15 C1.20893378,15 1,14.7910662 1,14.5333333 L1,14.5005208 C0.999354255,14.259116 1.18292475,14.0570646 1.42329087,14.0346853 C1.93951851,13.9857578 2.38825526,13.9043655 2.76950112,13.7905085 C2.88970413,13.7546105 2.99265144,13.7126424 3.07834304,13.6646043 C3.13612492,13.6324776 3.19778289,13.6139204 3.25964158,13.6078865 L4.67484531,6.76422771 L2.46685786,6.76422771 C2.18325317,6.76422771 1.95723871,6.54696341 1.93333333,6.27269412 L1.945,6.342 L1.95324336,6.32324571 C1.96746558,6.3067537 1.99022114,6.29312987 1.99875447,6.27376968 C1.98595447,6.2576362 1.94115447,6.24365384 1.94115447,6.22644479 L1.941,6.319 L1.93333333,6.27269412 C1.95524659,5.81127637 2.14698763,5.70504184 2.39724012,5.69054037 L2.46685786,5.68866187 L4.90846603,5.68866187 L5.24857433,4.08391763 C5.48980131,2.9481201 5.98746777,1.99301764 6.85458095,1.4369501 Z M14.7335755,5.87796146 C14.9009132,5.96723342 14.9878418,6.07048774 14.9987079,6.18664885 C15.0084874,6.30280996 14.9628498,6.41681994 14.8585355,6.52975435 L12.8950348,8.6378634 L14.8324569,11.2676219 C14.8933069,11.3461382 14.9085194,11.440788 14.873748,11.5440423 C14.8433229,11.6494478 14.7064103,11.7225862 14.5249468,11.8193872 C14.3282708,11.9215659 14.1576733,11.9118858 14.0359732,11.8785433 C13.9175329,11.8441252 13.8110453,11.7666844 13.7219435,11.6494478 L12.0876851,9.50907173 L10.1046255,11.6386921 C9.86774493,11.8925256 9.59935275,11.9301704 9.2929293,11.7505509 C9.16688277,11.6774125 8.93652187,11.4063699 8.90935667,11.2837554 C8.88327808,11.1622164 8.93217544,11.0342241 9.05822197,10.8976272 L11.2509969,8.53568465 L9.65368315,6.44370909 C9.58740006,6.34798373 9.58088042,6.23612488 9.6330376,6.10920811 C9.68302157,5.98229134 9.80146184,5.90700173 9.92750837,5.83278769 C10.1089719,5.7316845 10.2752229,5.7284578 10.3980096,5.7381379 C10.5218829,5.74566686 10.6338036,5.81557864 10.7370313,5.94464654 L12.0648663,7.65802292 L13.6719595,5.92528635 C13.8110453,5.772556 13.9686035,5.71877771 14.1207286,5.67575508 C14.2717671,5.63273244 14.5705843,5.78761392 14.7335755,5.87796146 Z" fill="currentColor" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
@ -161,6 +161,7 @@ import './renderer/StatusControl';
|
||||
import './renderer/FormulaControl';
|
||||
import './renderer/ExpressionFormulaControl';
|
||||
import './renderer/textarea-formula/TextareaFormulaControl';
|
||||
import './renderer/TplFormulaControl';
|
||||
import './renderer/DateShortCutControl';
|
||||
import './renderer/BadgeControl';
|
||||
import './renderer/style-control/BoxModel';
|
||||
|
@ -239,8 +239,9 @@ export class TextControlPlugin extends BasePlugin {
|
||||
form.changeValue('validationErrors', {...validationErrors});
|
||||
}
|
||||
}),
|
||||
getSchemaTpl('valueFormula', {
|
||||
rendererSchema: context?.schema
|
||||
getSchemaTpl('tplFormulaControl', {
|
||||
name: 'value',
|
||||
label: '默认值'
|
||||
}),
|
||||
getSchemaTpl('clearable'),
|
||||
getSchemaTpl('showCounter', {
|
||||
|
@ -6,9 +6,10 @@ import React from 'react';
|
||||
import {autobind, FormControlProps} from 'amis-core';
|
||||
import cx from 'classnames';
|
||||
import {FormItem, Button, Icon, PickerContainer} from 'amis';
|
||||
import {FormulaEditor} from 'amis-ui/lib/components/formula/Editor';
|
||||
import type {VariableItem} from 'amis-ui/lib/components/formula/Editor';
|
||||
import {FormulaEditor} from 'amis-ui';
|
||||
import type {VariableItem} from 'amis-ui';
|
||||
import {getVariables} from './textarea-formula/utils';
|
||||
import {renderFormulaValue} from './FormulaControl';
|
||||
import {reaction} from 'mobx';
|
||||
|
||||
interface ExpressionFormulaControlProps extends FormControlProps {
|
||||
@ -115,13 +116,6 @@ export default class ExpressionFormulaControl extends React.Component<
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
renderFormulaValue(item: any) {
|
||||
const html = {__html: item.html};
|
||||
// bca-disable-next-line
|
||||
return <span dangerouslySetInnerHTML={html}></span>;
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleConfirm(value = '') {
|
||||
if (this.props.evalMode) {
|
||||
@ -190,11 +184,11 @@ export default class ExpressionFormulaControl extends React.Component<
|
||||
mouseLeaveDelay: 20,
|
||||
content: value,
|
||||
tooltipClassName: 'btn-configured-tooltip',
|
||||
children: () => this.renderFormulaValue(highlightValue)
|
||||
children: () => renderFormulaValue(highlightValue)
|
||||
}}
|
||||
onClick={onClick}
|
||||
>
|
||||
已配置表达式
|
||||
{renderFormulaValue(highlightValue)}
|
||||
<Icon
|
||||
icon="input-clear"
|
||||
className="icon"
|
||||
|
@ -23,9 +23,11 @@ import {
|
||||
TooltipWrapper
|
||||
} from 'amis';
|
||||
import {FormulaExec, isExpression} from 'amis';
|
||||
import {PickerContainer, relativeValueRe} from 'amis';
|
||||
import {FormulaEditor} from 'amis-ui/lib/components/formula/Editor';
|
||||
import {FormulaEditor} from 'amis-ui';
|
||||
|
||||
import FormulaPicker, {
|
||||
CustomFormulaPickerProps
|
||||
} from './textarea-formula/FormulaPicker';
|
||||
import {autobind, translateSchema} from 'amis-editor-core';
|
||||
|
||||
import type {
|
||||
@ -44,6 +46,12 @@ export enum FormulaDateType {
|
||||
IsRange // 日期时间范围类
|
||||
}
|
||||
|
||||
export function renderFormulaValue(item: any) {
|
||||
const html = {__html: typeof item === 'string' ? item : item.html};
|
||||
// bca-disable-next-line
|
||||
return <span dangerouslySetInnerHTML={html}></span>;
|
||||
}
|
||||
|
||||
export interface FormulaControlProps extends FormControlProps {
|
||||
manager?: EditorManager;
|
||||
|
||||
@ -112,7 +120,7 @@ export interface FormulaControlProps extends FormControlProps {
|
||||
* 备注2: 开关组件可以设置 true 和 false 对应的值,如果设置了就不是普通的 boolean 类型;
|
||||
* 备注3: 默认都是字符串类型;
|
||||
*/
|
||||
valueType?: string;
|
||||
// valueType?: string;
|
||||
|
||||
/**
|
||||
* 不在表单项上触发时,传入想要获取变量的 表单props 获取对应变量
|
||||
@ -131,6 +139,11 @@ export interface FormulaControlProps extends FormControlProps {
|
||||
* 默认为 FormulaDateType.NotDate
|
||||
*/
|
||||
DateTimeType?: FormulaDateType;
|
||||
|
||||
/**
|
||||
* 自定义fx面板
|
||||
*/
|
||||
customFormulaPicker?: React.FC<CustomFormulaPickerProps>;
|
||||
}
|
||||
|
||||
interface FormulaControlState {
|
||||
@ -138,6 +151,8 @@ interface FormulaControlState {
|
||||
variables: any;
|
||||
|
||||
variableMode?: 'tree' | 'tabs';
|
||||
|
||||
formulaPickerOpen: boolean;
|
||||
}
|
||||
|
||||
export default class FormulaControl extends React.Component<
|
||||
@ -159,7 +174,8 @@ export default class FormulaControl extends React.Component<
|
||||
super(props);
|
||||
this.state = {
|
||||
variables: [],
|
||||
variableMode: 'tabs'
|
||||
variableMode: 'tabs',
|
||||
formulaPickerOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
@ -353,6 +369,20 @@ export default class FormulaControl extends React.Component<
|
||||
? value
|
||||
: `\${${value}}`;
|
||||
this.props?.onChange?.(val);
|
||||
|
||||
this.closeFormulaPicker();
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleFormulaClick() {
|
||||
this.setState({
|
||||
formulaPickerOpen: true
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
closeFormulaPicker() {
|
||||
this.setState({formulaPickerOpen: false});
|
||||
}
|
||||
|
||||
handleSimpleInputChange = (value: any) => {
|
||||
@ -452,13 +482,6 @@ export default class FormulaControl extends React.Component<
|
||||
return curRendererSchema;
|
||||
}
|
||||
|
||||
@autobind
|
||||
renderFormulaValue(item: any) {
|
||||
const html = {__html: item.html};
|
||||
// bca-disable-next-line
|
||||
return <span dangerouslySetInnerHTML={html}></span>;
|
||||
}
|
||||
|
||||
@autobind
|
||||
getContextData() {
|
||||
// 当前数据域
|
||||
@ -475,16 +498,18 @@ export default class FormulaControl extends React.Component<
|
||||
label,
|
||||
value,
|
||||
header,
|
||||
variables,
|
||||
placeholder,
|
||||
simple,
|
||||
rendererSchema,
|
||||
rendererWrapper,
|
||||
manager,
|
||||
useExternalFormData = false,
|
||||
customFormulaPicker,
|
||||
clearable = true,
|
||||
render,
|
||||
...rest
|
||||
} = this.props;
|
||||
const {formulaPickerOpen, variables, variableMode} = this.state;
|
||||
|
||||
// 自身字段
|
||||
const selfName = this.props?.data?.name;
|
||||
@ -501,13 +526,15 @@ export default class FormulaControl extends React.Component<
|
||||
}
|
||||
|
||||
// 判断是否含有公式表达式
|
||||
const isTypeError = !this.isExpectType(value);
|
||||
// const isTypeError = !this.isExpectType(value);
|
||||
const exprValue = this.transExpr(value);
|
||||
|
||||
const isError = isLoop || isTypeError;
|
||||
const isError = isLoop;
|
||||
|
||||
const FormulaPickerCmp = customFormulaPicker ?? FormulaPicker;
|
||||
|
||||
const highlightValue = isExpression(value)
|
||||
? FormulaEditor.highlightValue(exprValue, this.state.variables) || {
|
||||
? FormulaEditor.highlightValue(exprValue, variables) || {
|
||||
html: exprValue
|
||||
}
|
||||
: value;
|
||||
@ -577,85 +604,82 @@ export default class FormulaControl extends React.Component<
|
||||
tooltipTheme: 'dark',
|
||||
mouseLeaveDelay: 20,
|
||||
content: exprValue,
|
||||
children: () => this.renderFormulaValue(highlightValue)
|
||||
children: () => renderFormulaValue(highlightValue)
|
||||
}}
|
||||
>
|
||||
<div className="ae-editor-FormulaControl-tooltipBox">
|
||||
<div
|
||||
className="ae-editor-FormulaControl-ResultBox-wrapper"
|
||||
onClick={this.handleFormulaClick}
|
||||
>
|
||||
<ResultBox
|
||||
className={cx(
|
||||
'ae-editor-FormulaControl-ResultBox',
|
||||
isError ? 'is-error' : ''
|
||||
)}
|
||||
allowInput={false}
|
||||
clearable={true}
|
||||
value={value}
|
||||
result={{
|
||||
html: this.hasDateShortcutkey(value)
|
||||
? '已配置相对值'
|
||||
: '已配置表达式'
|
||||
? value
|
||||
: highlightValue.html
|
||||
}}
|
||||
itemRender={this.renderFormulaValue}
|
||||
itemRender={renderFormulaValue}
|
||||
onChange={this.handleInputChange}
|
||||
onResultChange={() => {
|
||||
this.handleInputChange(undefined);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{value && (
|
||||
<Icon
|
||||
icon="input-clear"
|
||||
className="input-clear-icon"
|
||||
iconContent="InputText-clear"
|
||||
onClick={() => this.handleInputChange('')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</TooltipWrapper>
|
||||
)}
|
||||
<PickerContainer
|
||||
showTitle={false}
|
||||
bodyRender={({
|
||||
value,
|
||||
onChange
|
||||
}: {
|
||||
onChange: (value: any) => void;
|
||||
value: any;
|
||||
}) => {
|
||||
return (
|
||||
<FormulaEditor
|
||||
{...rest}
|
||||
evalMode={true}
|
||||
variableMode={rest.variableMode ?? this.state.variableMode}
|
||||
variables={this.state.variables}
|
||||
header={header || '表达式'}
|
||||
value={filterValue}
|
||||
onChange={onChange}
|
||||
selfVariableName={selfName}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
value={value}
|
||||
onConfirm={this.handleConfirm}
|
||||
size="md"
|
||||
>
|
||||
{({onClick}: {onClick: (e: React.MouseEvent) => void}) => (
|
||||
<Button
|
||||
className="ae-editor-FormulaControl-button"
|
||||
size="sm"
|
||||
tooltip={{
|
||||
enterable: false,
|
||||
content: '点击配置表达式',
|
||||
tooltipTheme: 'dark',
|
||||
placement: 'left',
|
||||
mouseLeaveDelay: 0
|
||||
}}
|
||||
onClick={onClick}
|
||||
// active={simple && value} // 不需要,避免 hover 时无任何反馈效果
|
||||
onClick={this.handleFormulaClick}
|
||||
>
|
||||
<Icon
|
||||
icon="function"
|
||||
icon="input-fx"
|
||||
className={cx('ae-editor-FormulaControl-icon', 'icon', {
|
||||
['is-filled']: !!isFx
|
||||
})}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
</PickerContainer>
|
||||
{isError && (
|
||||
<div className="desc-msg error-msg">
|
||||
{isLoop ? '当前表达式异常(存在循环引用)' : '数值类型不匹配'}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{formulaPickerOpen ? (
|
||||
<FormulaPickerCmp
|
||||
{...this.props}
|
||||
value={filterValue}
|
||||
initable={true}
|
||||
header={header}
|
||||
variables={variables}
|
||||
variableMode={rest.variableMode ?? variableMode}
|
||||
evalMode={true}
|
||||
onClose={this.closeFormulaPicker}
|
||||
onConfirm={this.handleConfirm}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
452
packages/amis-editor/src/renderer/TplFormulaControl.tsx
Normal file
452
packages/amis-editor/src/renderer/TplFormulaControl.tsx
Normal file
@ -0,0 +1,452 @@
|
||||
/**
|
||||
* @file 长文本公式输入框
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import {reaction} from 'mobx';
|
||||
import {CodeMirrorEditor, FormulaEditor} from 'amis-ui';
|
||||
import type {VariableItem, CodeMirror} from 'amis-ui';
|
||||
import {Icon, Button, FormItem, TooltipWrapper} from 'amis';
|
||||
import {autobind, FormControlProps} from 'amis-core';
|
||||
import {FormulaPlugin, editorFactory} from './textarea-formula/plugin';
|
||||
import {getVariables} from './textarea-formula/utils';
|
||||
import {renderFormulaValue} from './FormulaControl';
|
||||
import FormulaPicker, {
|
||||
CustomFormulaPickerProps
|
||||
} from './textarea-formula/FormulaPicker';
|
||||
|
||||
export interface TplFormulaControlProps extends FormControlProps {
|
||||
/**
|
||||
* 用于提示的变量集合,默认为空
|
||||
*/
|
||||
variables?: Array<VariableItem> | Function;
|
||||
|
||||
/**
|
||||
* 配合 variables 使用
|
||||
* 当 props.variables 存在时, 是否再从 amis数据域中取变量集合,默认 false;
|
||||
*/
|
||||
requiredDataPropsVariables?: boolean;
|
||||
|
||||
/**
|
||||
* 变量展现模式,可选值:'tabs' | 'tree'
|
||||
*/
|
||||
variableMode?: 'tree' | 'tabs';
|
||||
|
||||
/**
|
||||
* 自定义fx面板
|
||||
*/
|
||||
customFormulaPicker?: React.FC<CustomFormulaPickerProps>;
|
||||
|
||||
/**
|
||||
* 是否可清除
|
||||
*/
|
||||
clearable?: boolean;
|
||||
|
||||
/**
|
||||
* 弹窗顶部标题,默认为 "表达式"
|
||||
*/
|
||||
header: string;
|
||||
}
|
||||
|
||||
interface TplFormulaControlState {
|
||||
value: string; // 当前文本值
|
||||
|
||||
variables: Array<VariableItem>; // 变量数据
|
||||
|
||||
formulaPickerOpen: boolean; // 是否打开公式编辑器
|
||||
|
||||
formulaPickerValue: string; // 公式编辑器内容
|
||||
|
||||
expressionBrace?: Array<CodeMirror.Position>; // 表达式所在位置
|
||||
|
||||
tooltipStyle: {[key: string]: string}; // 提示框样式
|
||||
}
|
||||
|
||||
// 暂时记录输入的字符,用于快捷键判断
|
||||
let preInputLocation: {start: number; end: number} | null = {
|
||||
start: 0,
|
||||
end: 0
|
||||
};
|
||||
|
||||
export class TplFormulaControl extends React.Component<
|
||||
TplFormulaControlProps,
|
||||
TplFormulaControlState
|
||||
> {
|
||||
static defaultProps: Partial<TplFormulaControlProps> = {
|
||||
variableMode: 'tabs',
|
||||
requiredDataPropsVariables: false,
|
||||
placeholder: '请输入'
|
||||
};
|
||||
|
||||
wrapRef = React.createRef<HTMLDivElement>();
|
||||
tooltipRef = React.createRef<HTMLDivElement>();
|
||||
|
||||
editorPlugin: FormulaPlugin;
|
||||
unReaction: any;
|
||||
appLocale: string;
|
||||
appCorpusData: any;
|
||||
|
||||
constructor(props: TplFormulaControlProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: '',
|
||||
variables: [],
|
||||
formulaPickerOpen: false,
|
||||
formulaPickerValue: '',
|
||||
tooltipStyle: {
|
||||
display: 'none'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const editorStore = (window as any).editorStore;
|
||||
this.appLocale = editorStore?.appLocale;
|
||||
this.appCorpusData = editorStore?.appCorpusData;
|
||||
this.unReaction = reaction(
|
||||
() => editorStore?.appLocaleState,
|
||||
async () => {
|
||||
this.appLocale = editorStore?.appLocale;
|
||||
this.appCorpusData = editorStore?.appCorpusData;
|
||||
const variablesArr = await getVariables(this);
|
||||
this.setState({
|
||||
variables: variablesArr
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const variablesArr = await getVariables(this);
|
||||
this.setState({
|
||||
variables: variablesArr
|
||||
});
|
||||
if (this.tooltipRef.current) {
|
||||
this.tooltipRef.current.addEventListener(
|
||||
'mouseleave',
|
||||
this.hiddenToolTip
|
||||
);
|
||||
}
|
||||
if (this.wrapRef.current) {
|
||||
this.wrapRef.current.addEventListener(
|
||||
'keydown',
|
||||
this.handleKeyDown,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async componentDidUpdate(prevProps: TplFormulaControlProps) {
|
||||
if (this.props.data !== prevProps.data) {
|
||||
const variablesArr = await getVariables(this);
|
||||
this.setState({
|
||||
variables: variablesArr
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.tooltipRef.current) {
|
||||
this.tooltipRef.current.removeEventListener(
|
||||
'mouseleave',
|
||||
this.hiddenToolTip
|
||||
);
|
||||
}
|
||||
if (this.wrapRef.current) {
|
||||
this.wrapRef.current.removeEventListener('keydown', this.handleKeyDown);
|
||||
}
|
||||
this.editorPlugin?.dispose();
|
||||
this.unReaction?.();
|
||||
}
|
||||
|
||||
@autobind
|
||||
onExpressionClick(expression: string, brace?: Array<CodeMirror.Position>) {
|
||||
this.setState({
|
||||
formulaPickerValue: expression,
|
||||
formulaPickerOpen: true,
|
||||
expressionBrace: brace
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
onExpressionMouseEnter(
|
||||
e: MouseEvent,
|
||||
expression: string,
|
||||
brace?: Array<CodeMirror.Position>
|
||||
) {
|
||||
const wrapperRect = this.wrapRef.current?.getBoundingClientRect();
|
||||
const expressionRect = (
|
||||
e.target as HTMLSpanElement
|
||||
).getBoundingClientRect();
|
||||
if (!wrapperRect) {
|
||||
return;
|
||||
}
|
||||
const left = expressionRect.left - wrapperRect.left;
|
||||
this.setState({
|
||||
tooltipStyle: {
|
||||
left: `${left}px`,
|
||||
width: `${expressionRect.width}px`
|
||||
},
|
||||
formulaPickerValue: expression,
|
||||
expressionBrace: brace
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
hiddenToolTip() {
|
||||
this.setState({
|
||||
tooltipStyle: {
|
||||
display: 'none'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleKeyDown(e: any) {
|
||||
// 组件禁止回车折行,否则会导致内容超过一行
|
||||
if (e.key === 'Enter') {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
closeFormulaPicker() {
|
||||
this.setState({formulaPickerOpen: false});
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleConfirm(value: any) {
|
||||
const {expressionBrace} = this.state;
|
||||
// 去除可能包裹的最外层的${}
|
||||
value = value.replace(/^\$\{(.*)\}$/, (match: string, p1: string) => p1);
|
||||
value = value ? `\${${value}}` : value;
|
||||
this.editorPlugin?.insertContent(value, 'expression', expressionBrace);
|
||||
this.setState({
|
||||
formulaPickerOpen: false,
|
||||
expressionBrace: undefined
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleOnChange(value: any) {
|
||||
this.checkOpenFormulaPicker(value);
|
||||
this.props.onChange?.(value);
|
||||
}
|
||||
|
||||
// 检测用户输入'${}'自动打开表达式弹窗
|
||||
checkOpenFormulaPicker(value: string) {
|
||||
const preLength = this.props.value?.length || 0;
|
||||
// 删除了文本,无需检测
|
||||
if (value.length < preLength) {
|
||||
return;
|
||||
}
|
||||
let left = 0;
|
||||
let right = 0;
|
||||
let length = value.length;
|
||||
|
||||
while (
|
||||
left < preLength &&
|
||||
value.charAt(left) === this.props.value.charAt(left)
|
||||
) {
|
||||
left++;
|
||||
}
|
||||
while (
|
||||
right < preLength &&
|
||||
value.charAt(length - 1 - right) ===
|
||||
this.props.value.charAt(preLength - 1 - right)
|
||||
) {
|
||||
right++;
|
||||
}
|
||||
if (preInputLocation?.end !== left) {
|
||||
preInputLocation = null;
|
||||
}
|
||||
|
||||
const start = preInputLocation ? preInputLocation.start : left;
|
||||
const end = left === length - right ? left + 1 : length - right;
|
||||
const inputText = value.substring(start, end);
|
||||
|
||||
if (/\$|\{|\}$/.test(inputText)) {
|
||||
if (/\$\{\}/.test(inputText)) {
|
||||
const newValue =
|
||||
value.slice(0, start) +
|
||||
inputText.replace('${}', '') +
|
||||
value.slice(end);
|
||||
this.props.onChange(newValue);
|
||||
|
||||
const corsur = this.editorPlugin.getCorsur();
|
||||
this.setState({
|
||||
formulaPickerOpen: true,
|
||||
formulaPickerValue: '',
|
||||
expressionBrace: [
|
||||
{
|
||||
line: corsur?.line,
|
||||
ch: end - 3
|
||||
},
|
||||
{
|
||||
line: corsur?.line,
|
||||
ch: end
|
||||
}
|
||||
]
|
||||
});
|
||||
preInputLocation = null;
|
||||
} else {
|
||||
preInputLocation = {
|
||||
start: left,
|
||||
...preInputLocation,
|
||||
end
|
||||
};
|
||||
}
|
||||
} else {
|
||||
preInputLocation = null;
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleClear() {
|
||||
this.editorPlugin?.setValue('');
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleFormulaClick() {
|
||||
this.setState({
|
||||
formulaPickerOpen: true,
|
||||
formulaPickerValue: '',
|
||||
expressionBrace: undefined
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
editorFactory(dom: HTMLElement, cm: any) {
|
||||
return editorFactory(dom, cm, this.props.value, {
|
||||
lineWrapping: false,
|
||||
cursorHeight: 0.85
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleEditorMounted(cm: any, editor: any) {
|
||||
const variables = this.props.variables || this.state.variables;
|
||||
this.editorPlugin = new FormulaPlugin(editor, {
|
||||
getProps: () => ({...this.props, variables}),
|
||||
onExpressionClick: this.onExpressionClick,
|
||||
onExpressionMouseEnter: this.onExpressionMouseEnter,
|
||||
showPopover: false
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
editorAutoMark() {
|
||||
this.editorPlugin?.autoMark();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
className,
|
||||
header,
|
||||
label,
|
||||
placeholder,
|
||||
customFormulaPicker,
|
||||
clearable,
|
||||
...rest
|
||||
} = this.props;
|
||||
const {formulaPickerOpen, formulaPickerValue, variables, tooltipStyle} =
|
||||
this.state;
|
||||
|
||||
const FormulaPickerCmp = customFormulaPicker ?? FormulaPicker;
|
||||
|
||||
const highlightValue = FormulaEditor.highlightValue(
|
||||
formulaPickerValue,
|
||||
variables
|
||||
) || {
|
||||
html: formulaPickerValue
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx('ae-TplFormulaControl', className, {
|
||||
clearable: clearable
|
||||
})}
|
||||
ref={this.wrapRef}
|
||||
>
|
||||
<div className={cx('ae-TplResultBox')}>
|
||||
<CodeMirrorEditor
|
||||
className="ae-TplResultBox-editor"
|
||||
value={this.props.value}
|
||||
onChange={this.handleOnChange}
|
||||
editorFactory={this.editorFactory}
|
||||
editorDidMount={this.handleEditorMounted}
|
||||
onBlur={this.editorAutoMark}
|
||||
/>
|
||||
{!this.props.value && (
|
||||
<div className="ae-TplFormulaControl-placeholder">
|
||||
{placeholder}
|
||||
</div>
|
||||
)}
|
||||
{clearable && this.props.value && (
|
||||
<Icon
|
||||
icon="input-clear"
|
||||
className="input-clear-icon"
|
||||
iconContent="InputText-clear"
|
||||
onClick={this.handleClear}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
className="ae-TplFormulaControl-button"
|
||||
size="sm"
|
||||
tooltip={{
|
||||
enterable: false,
|
||||
content: '点击配置表达式',
|
||||
tooltipTheme: 'dark',
|
||||
placement: 'left',
|
||||
mouseLeaveDelay: 0
|
||||
}}
|
||||
onClick={this.handleFormulaClick}
|
||||
>
|
||||
<Icon
|
||||
icon="input-add-fx"
|
||||
className={cx('ae-TplFormulaControl-icon', 'icon')}
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<TooltipWrapper
|
||||
trigger="hover"
|
||||
placement="top"
|
||||
style={{fontSize: '12px'}}
|
||||
tooltip={{
|
||||
tooltipTheme: 'dark',
|
||||
children: () => renderFormulaValue(highlightValue)
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="ae-TplFormulaControl-tooltip"
|
||||
style={tooltipStyle}
|
||||
ref={this.tooltipRef}
|
||||
onClick={() => {
|
||||
this.setState({formulaPickerOpen: true});
|
||||
}}
|
||||
></div>
|
||||
</TooltipWrapper>
|
||||
|
||||
{formulaPickerOpen ? (
|
||||
<FormulaPickerCmp
|
||||
{...this.props}
|
||||
value={formulaPickerValue}
|
||||
initable={true}
|
||||
header={header}
|
||||
variables={variables}
|
||||
variableMode={rest.variableMode}
|
||||
evalMode={true}
|
||||
onClose={this.closeFormulaPicker}
|
||||
onConfirm={this.handleConfirm}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@FormItem({
|
||||
type: 'ae-tplFormulaControl'
|
||||
})
|
||||
export default class TplFormulaControlRenderer extends TplFormulaControl {}
|
@ -11,6 +11,10 @@ export interface FormulaPickerProps {
|
||||
initable?: boolean;
|
||||
variableMode?: 'tabs' | 'tree';
|
||||
evalMode?: boolean;
|
||||
/**
|
||||
* 弹窗顶部标题,默认为 "表达式"
|
||||
*/
|
||||
header: string;
|
||||
}
|
||||
|
||||
export interface CustomFormulaPickerProps extends FormulaPickerProps {
|
||||
@ -50,7 +54,7 @@ const FormulaPicker: React.FC<FormulaPickerProps> = props => {
|
||||
<Modal.Body>
|
||||
<FormulaEditor
|
||||
{...props}
|
||||
header="表达式"
|
||||
header={props.header || '表达式'}
|
||||
variables={variables}
|
||||
variableMode={variableMode}
|
||||
value={formula}
|
||||
|
@ -2,20 +2,53 @@
|
||||
* @file 长文本公式输入框
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, {MouseEvent} from 'react';
|
||||
import cx from 'classnames';
|
||||
import {Icon, FormItem} from 'amis';
|
||||
import {autobind, FormControlProps, Schema} from 'amis-core';
|
||||
import CodeMirrorEditor from 'amis-ui/lib/components/CodeMirror';
|
||||
import {Icon, FormItem, TooltipWrapper} from 'amis';
|
||||
import {autobind, FormControlProps, render as renderAmis} from 'amis-core';
|
||||
import {CodeMirrorEditor, FormulaEditor} from 'amis-ui';
|
||||
import type {VariableItem, CodeMirror} from 'amis-ui';
|
||||
import {FormulaPlugin, editorFactory} from './plugin';
|
||||
|
||||
import FormulaPicker, {CustomFormulaPickerProps} from './FormulaPicker';
|
||||
import debounce from 'lodash/debounce';
|
||||
import CodeMirror from 'codemirror';
|
||||
import {getVariables} from './utils';
|
||||
import {VariableItem} from 'amis-ui/lib/components/formula/Editor';
|
||||
import {reaction} from 'mobx';
|
||||
import {renderFormulaValue} from '../FormulaControl';
|
||||
|
||||
export interface AdditionalMenuClickOpts {
|
||||
/**
|
||||
* 当前表达式值
|
||||
*/
|
||||
value: string;
|
||||
/**
|
||||
* 对表达式重新赋值
|
||||
* @param value
|
||||
* @returns
|
||||
*/
|
||||
setValue: (value: string) => void;
|
||||
/**
|
||||
* 在光标位置插入新的值
|
||||
* @param content 要插入的内容
|
||||
* @param type 插入内容的类型,目前支持表达式expression 和普通文本string
|
||||
* @param brace 自定义插入的位置
|
||||
* @returns
|
||||
*/
|
||||
insertContent: (
|
||||
content: string,
|
||||
type: 'expression' | 'string',
|
||||
brace?: Array<CodeMirror.Position>
|
||||
) => void;
|
||||
}
|
||||
|
||||
export interface AdditionalMenu {
|
||||
label: string; // 文案(当存在图标时,为tooltip内容)
|
||||
onClick?: (
|
||||
e: MouseEvent<HTMLAnchorElement>,
|
||||
opts: AdditionalMenuClickOpts
|
||||
) => void; // 触发事件
|
||||
icon?: string; // 图标
|
||||
className?: string; //外层类名
|
||||
}
|
||||
export interface TextareaFormulaControlProps extends FormControlProps {
|
||||
/**
|
||||
* 输入框的高度, 默认 100 px
|
||||
@ -41,12 +74,7 @@ export interface TextareaFormulaControlProps extends FormControlProps {
|
||||
/**
|
||||
* 附加底部按钮菜单项
|
||||
*/
|
||||
additionalMenus?: Array<{
|
||||
label: string; // 文案(当存在图标时,为tooltip内容)
|
||||
onClick: () => void; // 触发事件
|
||||
icon?: string; // 图标
|
||||
className?: string; //外层类名
|
||||
}>;
|
||||
additionalMenus?: Array<AdditionalMenu>;
|
||||
|
||||
/**
|
||||
* 整体点击长文本公式输入框
|
||||
@ -57,11 +85,14 @@ export interface TextareaFormulaControlProps extends FormControlProps {
|
||||
* 自定义fx面板
|
||||
*/
|
||||
customFormulaPicker?: React.FC<CustomFormulaPickerProps>;
|
||||
|
||||
/**
|
||||
* 弹窗顶部标题,默认为 "表达式"
|
||||
*/
|
||||
header: string;
|
||||
}
|
||||
|
||||
interface TextareaFormulaControlState {
|
||||
value: string; // 当前文本值
|
||||
|
||||
variables: Array<VariableItem>; // 变量数据
|
||||
|
||||
formulaPickerOpen: boolean; // 是否打开公式编辑器
|
||||
@ -71,6 +102,8 @@ interface TextareaFormulaControlState {
|
||||
expressionBrace?: Array<CodeMirror.Position>; // 表达式所在位置
|
||||
|
||||
isFullscreen: boolean; //是否全屏
|
||||
|
||||
tooltipStyle: {[key: string]: string}; // 提示框样式
|
||||
}
|
||||
|
||||
export class TextareaFormulaControl extends React.Component<
|
||||
@ -80,12 +113,15 @@ export class TextareaFormulaControl extends React.Component<
|
||||
static defaultProps: Partial<TextareaFormulaControlProps> = {
|
||||
variableMode: 'tabs',
|
||||
requiredDataPropsVariables: false,
|
||||
height: 100
|
||||
height: 100,
|
||||
placeholder: '请输入'
|
||||
};
|
||||
|
||||
isUnmount: boolean;
|
||||
wrapRef = React.createRef<HTMLDivElement>();
|
||||
|
||||
editorPlugin?: FormulaPlugin;
|
||||
tooltipRef = React.createRef<HTMLDivElement>();
|
||||
|
||||
editorPlugin: FormulaPlugin;
|
||||
unReaction: any;
|
||||
appLocale: string;
|
||||
appCorpusData: any;
|
||||
@ -93,11 +129,11 @@ export class TextareaFormulaControl extends React.Component<
|
||||
constructor(props: TextareaFormulaControlProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: '',
|
||||
variables: [],
|
||||
formulaPickerOpen: false,
|
||||
formulaPickerValue: '',
|
||||
isFullscreen: false
|
||||
isFullscreen: false,
|
||||
tooltipStyle: {}
|
||||
};
|
||||
}
|
||||
|
||||
@ -117,6 +153,13 @@ export class TextareaFormulaControl extends React.Component<
|
||||
}
|
||||
);
|
||||
|
||||
if (this.tooltipRef.current) {
|
||||
this.tooltipRef.current.addEventListener(
|
||||
'mouseleave',
|
||||
this.hiddenToolTip
|
||||
);
|
||||
}
|
||||
|
||||
const variablesArr = await getVariables(this);
|
||||
this.setState({
|
||||
variables: variablesArr
|
||||
@ -133,7 +176,14 @@ export class TextareaFormulaControl extends React.Component<
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isUnmount = true;
|
||||
if (this.tooltipRef.current) {
|
||||
this.tooltipRef.current.removeEventListener(
|
||||
'mouseleave',
|
||||
this.hiddenToolTip
|
||||
);
|
||||
}
|
||||
this.editorPlugin?.dispose();
|
||||
|
||||
this.unReaction?.();
|
||||
}
|
||||
|
||||
@ -146,6 +196,41 @@ export class TextareaFormulaControl extends React.Component<
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
onExpressionMouseEnter(
|
||||
e: any,
|
||||
expression: string,
|
||||
brace?: Array<CodeMirror.Position>
|
||||
) {
|
||||
const wrapperRect = this.wrapRef.current?.getBoundingClientRect();
|
||||
const expressionRect = (
|
||||
e.target as HTMLSpanElement
|
||||
).getBoundingClientRect();
|
||||
if (!wrapperRect) {
|
||||
return;
|
||||
}
|
||||
const left = expressionRect.left - wrapperRect.left;
|
||||
const top = expressionRect.top - wrapperRect.top;
|
||||
this.setState({
|
||||
tooltipStyle: {
|
||||
left: `${left}px`,
|
||||
top: `${top}px`,
|
||||
width: `${expressionRect.width}px`
|
||||
},
|
||||
formulaPickerValue: expression,
|
||||
expressionBrace: brace
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
hiddenToolTip() {
|
||||
this.setState({
|
||||
tooltipStyle: {
|
||||
display: 'none'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
closeFormulaPicker() {
|
||||
this.setState({formulaPickerOpen: false});
|
||||
@ -162,28 +247,25 @@ export class TextareaFormulaControl extends React.Component<
|
||||
formulaPickerOpen: false,
|
||||
expressionBrace: undefined
|
||||
});
|
||||
|
||||
this.closeFormulaPicker();
|
||||
}
|
||||
|
||||
handleOnChange = debounce((value: any) => {
|
||||
@autobind
|
||||
handleOnChange(value: any) {
|
||||
this.props.onChange?.(value);
|
||||
}, 200);
|
||||
}
|
||||
|
||||
@autobind
|
||||
editorFactory(dom: HTMLElement, cm: any) {
|
||||
const variables = this.props.variables || this.state.variables;
|
||||
return editorFactory(dom, cm, {...this.props, variables});
|
||||
return editorFactory(dom, cm, this.props.value);
|
||||
}
|
||||
@autobind
|
||||
handleEditorMounted(cm: any, editor: any) {
|
||||
const variables = this.props.variables || this.state.variables;
|
||||
this.editorPlugin = new FormulaPlugin(
|
||||
editor,
|
||||
cm,
|
||||
() => ({...this.props, variables}),
|
||||
this.onExpressionClick
|
||||
);
|
||||
this.editorPlugin = new FormulaPlugin(editor, {
|
||||
getProps: () => ({...this.props, variables}),
|
||||
onExpressionClick: this.onExpressionClick,
|
||||
onExpressionMouseEnter: this.onExpressionMouseEnter
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
@ -213,6 +295,18 @@ export class TextareaFormulaControl extends React.Component<
|
||||
this.editorPlugin?.autoMark();
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleAddtionalMenuClick(
|
||||
e: MouseEvent<HTMLAnchorElement>,
|
||||
item: AdditionalMenu
|
||||
) {
|
||||
item.onClick?.(e, {
|
||||
value: this.props.value || '',
|
||||
setValue: this.editorPlugin.setValue,
|
||||
insertContent: this.editorPlugin.insertContent
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
className,
|
||||
@ -226,11 +320,11 @@ export class TextareaFormulaControl extends React.Component<
|
||||
...rest
|
||||
} = this.props;
|
||||
const {
|
||||
value,
|
||||
formulaPickerOpen,
|
||||
formulaPickerValue,
|
||||
isFullscreen,
|
||||
variables
|
||||
variables,
|
||||
tooltipStyle
|
||||
} = this.state;
|
||||
|
||||
const FormulaPickerCmp = customFormulaPicker ?? FormulaPicker;
|
||||
@ -241,6 +335,13 @@ export class TextareaFormulaControl extends React.Component<
|
||||
resultBoxStyle.height = `${height}px`;
|
||||
}
|
||||
|
||||
const highlightValue = FormulaEditor.highlightValue(
|
||||
formulaPickerValue,
|
||||
variables
|
||||
) || {
|
||||
html: formulaPickerValue
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
@ -250,16 +351,22 @@ export class TextareaFormulaControl extends React.Component<
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={this.wrapRef}
|
||||
>
|
||||
<div className={cx('ae-TextareaResultBox')} style={resultBoxStyle}>
|
||||
<CodeMirrorEditor
|
||||
className="ae-TextareaResultBox-editor"
|
||||
value={value}
|
||||
value={this.props.value}
|
||||
onChange={this.handleOnChange}
|
||||
editorFactory={this.editorFactory}
|
||||
editorDidMount={this.handleEditorMounted}
|
||||
onBlur={this.editorAutoMark}
|
||||
/>
|
||||
{!this.props.value && (
|
||||
<div className="ae-TextareaResultBox-placeholder">
|
||||
{placeholder}
|
||||
</div>
|
||||
)}
|
||||
<ul className="ae-TextareaResultBox-footer">
|
||||
<li className="ae-TextareaResultBox-footer-fullscreen">
|
||||
<a
|
||||
@ -280,24 +387,31 @@ export class TextareaFormulaControl extends React.Component<
|
||||
data-position="top"
|
||||
onClick={this.handleFormulaClick}
|
||||
>
|
||||
<Icon icon="function" className="icon" />
|
||||
<Icon icon="input-add-fx" className="icon" />
|
||||
</a>
|
||||
</li>
|
||||
{/* 附加底部按钮菜单项 */}
|
||||
{additionalMenus?.length &&
|
||||
additionalMenus?.map((item, i) => {
|
||||
return (
|
||||
<li key={i} className={item?.className || ''}>
|
||||
<li key={i}>
|
||||
{item.icon ? (
|
||||
<a
|
||||
data-tooltip={item.label}
|
||||
data-position="top"
|
||||
onClick={() => item.onClick()}
|
||||
onClick={e => this.handleAddtionalMenuClick(e, item)}
|
||||
>
|
||||
<Icon icon={item.icon} className="icon" />
|
||||
{renderAmis({
|
||||
type: 'icon',
|
||||
icon: item.icon,
|
||||
vendor: '',
|
||||
className: item.className
|
||||
})}
|
||||
</a>
|
||||
) : (
|
||||
<a onClick={() => item?.onClick()}>{item.label}</a>
|
||||
<a onClick={e => this.handleAddtionalMenuClick(e, item)}>
|
||||
{item.label}
|
||||
</a>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
@ -312,15 +426,35 @@ export class TextareaFormulaControl extends React.Component<
|
||||
></div>
|
||||
) : null}
|
||||
|
||||
<TooltipWrapper
|
||||
trigger="hover"
|
||||
placement="top"
|
||||
style={{fontSize: '12px'}}
|
||||
tooltip={{
|
||||
tooltipTheme: 'dark',
|
||||
children: () => renderFormulaValue(highlightValue)
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="ae-TplFormulaControl-tooltip"
|
||||
style={tooltipStyle}
|
||||
ref={this.tooltipRef}
|
||||
onClick={() => {
|
||||
this.setState({formulaPickerOpen: true});
|
||||
}}
|
||||
></div>
|
||||
</TooltipWrapper>
|
||||
|
||||
{formulaPickerOpen ? (
|
||||
<FormulaPickerCmp
|
||||
{...this.props}
|
||||
value={formulaPickerValue}
|
||||
initable={true}
|
||||
variables={variables}
|
||||
header={header}
|
||||
variableMode={rest.variableMode}
|
||||
evalMode={true}
|
||||
onClose={() => this.setState({formulaPickerOpen: false})}
|
||||
onClose={this.closeFormulaPicker}
|
||||
onConfirm={this.handleConfirm}
|
||||
/>
|
||||
) : null}
|
||||
|
@ -2,37 +2,60 @@
|
||||
* @file 扩展 codemirror
|
||||
*/
|
||||
|
||||
import type CodeMirror from 'codemirror';
|
||||
import {TextareaFormulaControlProps} from './TextareaFormulaControl';
|
||||
import {FormulaEditor} from 'amis-ui/lib/components/formula/Editor';
|
||||
import {FormulaEditor} from 'amis-ui';
|
||||
import type {VariableItem, CodeMirror} from 'amis-ui';
|
||||
|
||||
export function editorFactory(
|
||||
dom: HTMLElement,
|
||||
cm: typeof CodeMirror,
|
||||
props: any
|
||||
value: string,
|
||||
config?: Object
|
||||
) {
|
||||
return cm(dom, {
|
||||
value: props.value || '',
|
||||
value: value || '',
|
||||
autofocus: false,
|
||||
lineWrapping: true
|
||||
lineWrapping: true,
|
||||
...config
|
||||
});
|
||||
}
|
||||
|
||||
export class FormulaPlugin {
|
||||
constructor(
|
||||
readonly editor: CodeMirror.Editor,
|
||||
readonly cm: typeof CodeMirror,
|
||||
readonly getProps: () => TextareaFormulaControlProps,
|
||||
readonly onExpressionClick: (
|
||||
interface FormulaPluginConfig {
|
||||
getProps: () => TextareaFormulaControlProps;
|
||||
onExpressionClick?: (
|
||||
expression: string,
|
||||
brace?: Array<CodeMirror.Position>
|
||||
) => any
|
||||
) {
|
||||
const {value} = this.getProps();
|
||||
) => any;
|
||||
onExpressionMouseEnter?: (
|
||||
e: MouseEvent,
|
||||
expression: string,
|
||||
brace?: Array<CodeMirror.Position>
|
||||
) => any;
|
||||
showPopover?: boolean;
|
||||
showClearIcon?: boolean; // 表达式是否展示删除icon
|
||||
}
|
||||
|
||||
const defaultPluginConfig = {
|
||||
showPopover: false,
|
||||
showClearIcon: false
|
||||
};
|
||||
|
||||
export class FormulaPlugin {
|
||||
config: FormulaPluginConfig;
|
||||
|
||||
constructor(readonly editor: CodeMirror.Editor, config: FormulaPluginConfig) {
|
||||
this.config = {
|
||||
...defaultPluginConfig,
|
||||
...config
|
||||
};
|
||||
const {value} = this.config.getProps();
|
||||
if (value) {
|
||||
this.autoMark();
|
||||
this.focus(value);
|
||||
}
|
||||
|
||||
this.setValue = this.setValue.bind(this);
|
||||
this.insertContent = this.insertContent.bind(this);
|
||||
}
|
||||
|
||||
autoMark() {
|
||||
@ -154,27 +177,36 @@ export class FormulaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
// 重新赋值
|
||||
setValue(value: string) {
|
||||
this.editor.setValue(value);
|
||||
}
|
||||
|
||||
getCorsur() {
|
||||
return this.editor.getCursor();
|
||||
}
|
||||
|
||||
insertContent(
|
||||
value: any,
|
||||
type?: 'expression',
|
||||
content: string,
|
||||
type: 'expression' | 'string' = 'string',
|
||||
brace?: Array<CodeMirror.Position>
|
||||
) {
|
||||
if (brace) {
|
||||
// 替换
|
||||
const [from, to] = brace;
|
||||
if (type === 'expression') {
|
||||
this.editor.replaceRange(value, from, to);
|
||||
this.editor.replaceRange(content, from, to);
|
||||
this.autoMark();
|
||||
} else if (typeof value === 'string') {
|
||||
this.editor.replaceRange(value, from, to);
|
||||
} else if (type === 'string') {
|
||||
this.editor.replaceRange(content, from, to);
|
||||
}
|
||||
} else {
|
||||
// 新增
|
||||
if (type === 'expression') {
|
||||
this.editor.replaceSelection(value);
|
||||
this.editor.replaceSelection(content);
|
||||
this.autoMark();
|
||||
} else if (typeof value === 'string') {
|
||||
this.editor.replaceSelection(value);
|
||||
} else if (type === 'string') {
|
||||
this.editor.replaceSelection(content);
|
||||
}
|
||||
this.editor.focus();
|
||||
}
|
||||
@ -186,33 +218,59 @@ export class FormulaPlugin {
|
||||
expression = '',
|
||||
className = 'cm-expression'
|
||||
) {
|
||||
const wrap = document.createElement('span');
|
||||
wrap.className = className;
|
||||
const text = document.createElement('span');
|
||||
text.className = `${className}-text`;
|
||||
text.innerText = expression;
|
||||
text.setAttribute('data-expression', expression);
|
||||
text.onclick = () => {
|
||||
const brace = this.getExpressionBrace(expression);
|
||||
this.onExpressionClick(expression, brace);
|
||||
};
|
||||
const {variables} = this.getProps();
|
||||
const {
|
||||
onExpressionClick,
|
||||
onExpressionMouseEnter,
|
||||
getProps,
|
||||
showPopover,
|
||||
showClearIcon
|
||||
} = this.config;
|
||||
|
||||
const variables = getProps()?.variables as VariableItem[];
|
||||
const highlightValue = FormulaEditor.highlightValue(
|
||||
expression,
|
||||
variables
|
||||
) || {
|
||||
html: expression
|
||||
};
|
||||
|
||||
const wrap = document.createElement('span');
|
||||
wrap.className = className;
|
||||
const text = document.createElement('span');
|
||||
text.className = `${className}-text`;
|
||||
text.innerHTML = highlightValue.html;
|
||||
text.setAttribute('data-expression', expression);
|
||||
text.onclick = () => {
|
||||
const brace = this.getExpressionBrace(expression);
|
||||
onExpressionClick?.(expression, brace);
|
||||
};
|
||||
text.onmouseenter = e => {
|
||||
const brace = this.getExpressionBrace(expression);
|
||||
onExpressionMouseEnter?.(e, expression, brace);
|
||||
};
|
||||
wrap.appendChild(text);
|
||||
|
||||
if (showClearIcon) {
|
||||
const closeIcon = document.createElement('i');
|
||||
closeIcon.className = 'cm-expression-close iconfont icon-close';
|
||||
closeIcon.onclick = () => {
|
||||
const brace = this.getExpressionBrace(expression);
|
||||
this.insertContent('', 'expression', brace);
|
||||
};
|
||||
wrap.appendChild(closeIcon);
|
||||
}
|
||||
|
||||
if (showPopover) {
|
||||
// 添加popover
|
||||
const popoverEl = document.createElement('div');
|
||||
// bca-disable-next-line
|
||||
popoverEl.innerHTML = highlightValue.html;
|
||||
popoverEl.classList.add('expression-popover');
|
||||
popoverEl.classList.add('cm-expression-popover');
|
||||
const arrow = document.createElement('div');
|
||||
arrow.classList.add('expression-popover-arrow');
|
||||
arrow.classList.add('cm-expression-popover-arrow');
|
||||
popoverEl.appendChild(arrow);
|
||||
wrap.appendChild(text);
|
||||
wrap.appendChild(popoverEl);
|
||||
}
|
||||
|
||||
this.editor.markText(from, to, {
|
||||
atomic: true,
|
||||
|
@ -130,6 +130,13 @@ setSchemaTpl('textareaFormulaControl', (schema: object = {}) => {
|
||||
};
|
||||
});
|
||||
|
||||
setSchemaTpl('tplFormulaControl', (schema: object = {}) => {
|
||||
return {
|
||||
type: 'ae-tplFormulaControl',
|
||||
...schema
|
||||
};
|
||||
});
|
||||
|
||||
setSchemaTpl('DataPickerControl', (schema: object = {}) => {
|
||||
return {
|
||||
type: 'ae-DataPickerControl',
|
||||
|
@ -45,6 +45,7 @@
|
||||
var(--input-default-default-paddingLeft);
|
||||
background: var(--input-default-default-bg-color);
|
||||
height: var(--input-size-default-height);
|
||||
align-items: center;
|
||||
|
||||
&:hover,
|
||||
&.hover {
|
||||
|
@ -70,6 +70,8 @@ import SchemaVariableListPicker from './schema-editor/SchemaVariableListPicker';
|
||||
import SchemaVariableList from './schema-editor/SchemaVariableList';
|
||||
import VariableList from './formula/VariableList';
|
||||
import FormulaPicker from './formula/Picker';
|
||||
import {FormulaEditor} from './formula/Editor';
|
||||
import type {VariableItem} from './formula/Editor';
|
||||
import PickerContainer from './PickerContainer';
|
||||
import InputJSONSchema from './json-schema';
|
||||
import {Badge, withBadge} from './Badge';
|
||||
@ -122,6 +124,8 @@ import ConfirmBox from './ConfirmBox';
|
||||
import DndContainer from './DndContainer';
|
||||
import Menu from './menu';
|
||||
import InputBoxWithSuggestion from './InputBoxWithSuggestion';
|
||||
import {CodeMirrorEditor} from './CodeMirror';
|
||||
import type CodeMirror from 'codemirror';
|
||||
|
||||
export {
|
||||
NotFound,
|
||||
@ -193,6 +197,8 @@ export {
|
||||
PickerContainer,
|
||||
ConfirmBox,
|
||||
FormulaPicker,
|
||||
VariableItem,
|
||||
FormulaEditor,
|
||||
InputJSONSchema,
|
||||
withBadge,
|
||||
BadgeObject,
|
||||
@ -248,5 +254,7 @@ export {
|
||||
InputTable,
|
||||
InputTableColumnProps,
|
||||
DndContainer,
|
||||
Menu
|
||||
Menu,
|
||||
CodeMirror,
|
||||
CodeMirrorEditor
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user