diff --git a/designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java b/designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java index 2354007e18..2afcaabe4d 100644 --- a/designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java +++ b/designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java @@ -115,6 +115,9 @@ public abstract class DesignTableDataManager { //增强for循环用的iterator实现的, 如果中间哪个listener修改或删除了(如ChartEditPane.dsChangeListener), // 由于dsListeners是arraylist, 此时会ConcurrentModifyException ChangeEvent e = null; + if (dsListeners.get(i) == null) { + continue; + } dsListeners.get(i).stateChanged(e); } } @@ -185,23 +188,33 @@ public abstract class DesignTableDataManager { globalDsListeners.add(l); } +/** + * 添加模板数据集改变 监听事件. + * + * @param l ChangeListener监听器 + */ +public static void addDsChangeListener(ChangeListener l) { + getDsListenersForCurrentTemplate().add(l); +} + +/** + * 移除模板数据集改变 监听事件. + * + * @param l ChangeListener监听器 + */ +public static void removeDsChangeListener(ChangeListener l) { + getDsListenersForCurrentTemplate().remove(l); +} + /** - * 添加模板数据集改变 监听事件. + * 获取当前模板的监听器列表. * - * @param l ChangeListener监听器 + * @return 模板对应的监听器列表,如果列表不存在则新建. */ - public static void addDsChangeListener(ChangeListener l) { + private static List<ChangeListener> getDsListenersForCurrentTemplate() { JTemplate<?, ?> template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); - String key = StringUtils.EMPTY; - if (JTemplate.isValid(template)) { - key = template.getPath(); - } - List<ChangeListener> dsListeners = dsListenersMap.get(key); - if (dsListeners == null) { - dsListeners = new ArrayList<ChangeListener>(); - dsListenersMap.put(key, dsListeners); - } - dsListeners.add(l); + String key = JTemplate.isValid(template) ? template.getPath() : StringUtils.EMPTY; + return dsListenersMap.computeIfAbsent(key, k -> new ArrayList<>()); } /** diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataComboBox.java b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataComboBox.java index ebbdbf0f35..e177837470 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataComboBox.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataComboBox.java @@ -1,13 +1,14 @@ package com.fr.design.data.datapane; +import com.fr.design.ui.util.UIUtil; import java.awt.Component; import java.awt.event.ItemEvent; -import java.util.Iterator; -import java.util.Map.Entry; import javax.swing.DefaultComboBoxModel; import javax.swing.JLabel; import javax.swing.JList; +import javax.swing.event.AncestorEvent; +import javax.swing.event.AncestorListener; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -32,16 +33,53 @@ public class TableDataComboBox extends UIComboBox implements Prepare4DataSourceC protected java.util.Map<String, TableDataWrapper> resMap; private java.util.Map<String, TableDataWrapper> dsMap; private static final long serialVersionUID = 1L; - private boolean refresModel = false; - private String treeName; //树数据集本身的名字 + private boolean refreshModel = false; + private String treeName = StringUtils.EMPTY; //树数据集本身的名字 private ChangeListener changeListener; + /** + * 兼容插件调用 + * + * @param source 传入的数据源 + */ public TableDataComboBox(TableDataSource source){ - this(source,StringUtils.EMPTY); + this(); } + + /** + * 兼容插件调用 + * + * @param source 传入的数据源 + * @param treeName 树数据集名称 + */ public TableDataComboBox(TableDataSource source, String treeName) { + this(treeName); + } + + /** + * 根据树名称创建TableDataComboBox。 + * + * @param treeName 树数据集名称 + */ + public TableDataComboBox(String treeName) { + this(); + // 传入树数据集名称 + this.treeName = treeName; + } + + /** + * 初始化TableDataComboBox + */ + public TableDataComboBox() { super(); - this.treeName = treeName; + setListCellRenderer(); + addComboBoxListener(); + } + + /** + * 设置渲染器 + */ + private void setListCellRenderer() { this.setRenderer(new UIComboBoxRenderer() { private static final long serialVersionUID = 1L; @@ -60,37 +98,66 @@ public class TableDataComboBox extends UIComboBox implements Prepare4DataSourceC return renderer; } }); - refresh(source); - registerDSChangeListener(); } - /** - * refresh ComboBox - * @param source - */ + /** + * 在comboBox可见时添加数据集响应事件与refresh操作 + */ + private void addComboBoxListener() { + this.addAncestorListener(new AncestorListener() { + @Override + public void ancestorAdded(AncestorEvent event) { + registerDSChangeListener(); + refresh(DesignTableDataManager.getEditingTableDataSource()); + } + + @Override + public void ancestorRemoved(AncestorEvent event) { + DesignTableDataManager.removeDsChangeListener(changeListener); + } + + @Override + public void ancestorMoved(AncestorEvent event) { + } + }); + } + + /** + * 刷新数据源并更新下拉框的模型和选中项 + * + * @param source 数据源,用于刷新模型 + */ public void refresh(TableDataSource source) { - TableDataWrapper dataWrapper = getSelectedItem(); - refresModel = true; - setResMap(source); - setDsMap(); - DefaultComboBoxModel model = new DefaultComboBoxModel(); - this.setModel(model); - model.addElement(UIConstants.PENDING); - Iterator<Entry<String, TableDataWrapper>> entryIt = dsMap.entrySet().iterator(); - while (entryIt.hasNext()) { - TableDataWrapper tableDataWrapper = entryIt.next().getValue(); - if (!ComparatorUtils.equals(tableDataWrapper.getTableDataName(), treeName)) { - model.addElement(tableDataWrapper); - } - } - if (dataWrapper != null) { - if (DesignTableDataManager.isDsNameChanged(dataWrapper.getTableDataName())) { - this.setSelectedTableDataByName(DesignTableDataManager.getChangedDsNameByOldDsName(dataWrapper.getTableDataName())); - } else { - this.getModel().setSelectedItem(dataWrapper); - } - } - refresModel = false; + UIUtil.executeAsyncTaskAndUpdateUI( + () -> { + setResMap(source); + setDsMap(); + return null; + }, + result -> refreshComboBoxModel() + ); + } + + /** + * 刷新下拉框模型,同时保留当前选中的数据项 + * <p> + * 1. 获取下拉框中当前选中的数据项 + * 2. 刷新下拉框的模型(清空并重新填充数据),此操作会重置选中的数据项 + * 3. 在刷新模型后,恢复之前选中的数据项 + * <p> + * 关于 `refreshModel` 的作用: + * 1. **抑制事件触发**:下拉框模型在调用 `addElement` 方法时会触发 `fireItemStateChanged` 事件, + * 通过标记 `refreshModel`,可以在刷新过程中抑制此事件 + * 2. **处理异步和顺序问题**:由于取数操作是异步的,可能会导致回调后的 UI 操作与其他逻辑(如 `populateBean`)的调用顺序交错。 + * 标记 `refreshModel` 可确保在刷新模型时,不触发选中事件,从而避免逻辑干扰 + * 3. **逻辑清晰性**:刷新模型本质上是更新数据源的操作,不应触发与用户交互相关的选中事件,避免对上层逻辑造成额外负担 + */ + private void refreshComboBoxModel() { + refreshModel = true; + TableDataWrapper selectedItem = getSelectedItem(); + refreshModel(); + updateSelectedItem(selectedItem); + refreshModel = false; } protected void setResMap(TableDataSource source) { @@ -101,6 +168,26 @@ public class TableDataComboBox extends UIComboBox implements Prepare4DataSourceC dsMap = DesignTableDataManager.getAllDataSetIncludingProcedure(resMap); } + private void refreshModel() { + //创建ComboBox模型并设置 + DefaultComboBoxModel model = new DefaultComboBoxModel(); + this.setModel(model); + model.addElement(UIConstants.PENDING); + // 遍历添加所有数据项到模型,树数据集comboBox下拉模型中排除掉本身 + dsMap.values().stream() + .filter(tableDataWrapper -> tableDataWrapper != null && !ComparatorUtils.equals(tableDataWrapper.getTableDataName(), treeName)) + .forEach(model::addElement); + } + + private void updateSelectedItem(TableDataWrapper dataWrapper) { + if (dataWrapper != null) { + if (DesignTableDataManager.isDsNameChanged(dataWrapper.getTableDataName())) { + this.setSelectedTableData(DesignTableDataManager.getChangedDsNameByOldDsName(dataWrapper.getTableDataName())); + } else { + this.getModel().setSelectedItem(dataWrapper); + } + } + } /** @@ -117,8 +204,18 @@ public class TableDataComboBox extends UIComboBox implements Prepare4DataSourceC } public void setSelectedTableDataByName(String name) { - TableDataWrapper tableDataWrappe = dsMap.get(name) == null? dsMap.get(name + "_P_CURSOR") : dsMap.get(name); - this.getModel().setSelectedItem(tableDataWrappe); + setResMap(DesignTableDataManager.getEditingTableDataSource()); + setDsMap(); + // 数据集名称修改后控件传入的还是旧名称 + if (DesignTableDataManager.isDsNameChanged(name)) { + name = DesignTableDataManager.getChangedDsNameByOldDsName(name); + } + setSelectedTableData(name); + } + + private void setSelectedTableData(String name) { + TableDataWrapper tableDataWrapper = dsMap.get(name) == null ? dsMap.get(name + "_P_CURSOR") : dsMap.get(name); + this.getModel().setSelectedItem(tableDataWrapper); } @Override @@ -132,7 +229,7 @@ public class TableDataComboBox extends UIComboBox implements Prepare4DataSourceC //august:addElement方法竟然会fireItemStateChanged,蛋疼 @Override protected void fireItemStateChanged(ItemEvent e) { - if (!refresModel) { + if (!refreshModel) { super.fireItemStateChanged(e); } } diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/TreeTableDataDictPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/TreeTableDataDictPane.java index 175ddbfda3..fcb8705e9a 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/TreeTableDataDictPane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/TreeTableDataDictPane.java @@ -167,8 +167,7 @@ public class TreeTableDataDictPane extends BasicPane implements Previewable { } protected void setTableDataNameComboBox(String treeName) { - tableDataNameComboBox = new TableDataComboBox(DesignTableDataManager.getEditingTableDataSource(), treeName); - + tableDataNameComboBox = new TableDataComboBox(treeName); } private void tdChange(boolean isChangeDS) { diff --git a/designer-base/src/main/java/com/fr/design/editor/editor/ColumnSelectedEditor.java b/designer-base/src/main/java/com/fr/design/editor/editor/ColumnSelectedEditor.java index 5260ce1752..29d6387f5d 100644 --- a/designer-base/src/main/java/com/fr/design/editor/editor/ColumnSelectedEditor.java +++ b/designer-base/src/main/java/com/fr/design/editor/editor/ColumnSelectedEditor.java @@ -31,7 +31,7 @@ public class ColumnSelectedEditor extends Editor<SimpleDSColumn> implements Prep public ColumnSelectedEditor() { this.setName(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_DS_Column")); this.setLayout(FRGUIPaneFactory.createLeftZeroLayout()); - tableDataComboBox = new TableDataComboBox(DesignTableDataManager.getEditingTableDataSource()); + tableDataComboBox = new TableDataComboBox(); columnNames = new String[0]; tableDataComboBox.addItemListener(new ItemListener() { diff --git a/designer-base/src/main/java/com/fr/design/fun/DefaultValueAdjustProvider.java b/designer-base/src/main/java/com/fr/design/fun/DefaultValueAdjustProvider.java index 28a2d6c939..60b7c314a8 100644 --- a/designer-base/src/main/java/com/fr/design/fun/DefaultValueAdjustProvider.java +++ b/designer-base/src/main/java/com/fr/design/fun/DefaultValueAdjustProvider.java @@ -6,6 +6,7 @@ import com.fr.chartx.attr.ChartProvider; import com.fr.design.style.color.FRColorSelectorStyle; import com.fr.general.FRFont; import com.fr.report.cell.CellElement; +import com.fr.stable.StringUtils; import com.fr.stable.collections.combination.Pair; import com.fr.stable.fun.mark.Selectable; @@ -83,4 +84,29 @@ public interface DefaultValueAdjustProvider extends Selectable { default List getColorSelector(){ return FRColorSelectorStyle.COLOR_CONFIG; } + + /** + * 移动端弹窗界面是否支持配置POST传参方式 + * + * @return 是否支持 + */ + default boolean isNeedPostCombo4MobilePopupPane() { + return false; + } + + /** + * JsContentPane是否支持内容提示 + * @return 是否支持 + */ + default boolean isNeedContentWarning4JsContentPane() { + return false; + } + + /** + * 自定义匹配js内容,并返回对应的提示文本 + * @return <是否匹配,提示文本> + */ + default Pair<Boolean, String> checkJsContent(String content) { + return new Pair<>(false, StringUtils.EMPTY); + } } diff --git a/designer-base/src/main/java/com/fr/design/hyperlink/popup/ContentSettingPane.java b/designer-base/src/main/java/com/fr/design/hyperlink/popup/ContentSettingPane.java index 848ac9e57c..41fda2cd3b 100644 --- a/designer-base/src/main/java/com/fr/design/hyperlink/popup/ContentSettingPane.java +++ b/designer-base/src/main/java/com/fr/design/hyperlink/popup/ContentSettingPane.java @@ -6,10 +6,12 @@ import com.fr.base.Parameter; import com.fr.design.dialog.BasicDialog; import com.fr.design.dialog.DialogActionAdapter; import com.fr.design.formula.TinyFormulaPane; +import com.fr.design.fun.DefaultValueAdjustProvider; import com.fr.design.gui.frpane.ReportletParameterViewPane; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ibutton.UIRadioButton; import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.gui.icombobox.UIComboBox; import com.fr.design.gui.itableeditorpane.UITableEditAction; import com.fr.design.gui.itextfield.UITextField; import com.fr.design.gui.itree.filetree.ReportletPane; @@ -18,6 +20,7 @@ import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.module.DesignModuleFactory; import com.fr.design.parameter.ParameterReader; +import com.fr.design.utils.DesignUtils; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.js.MobilePopupHyperlink; import com.fr.stable.CommonUtils; @@ -25,8 +28,14 @@ import com.fr.stable.FormulaProvider; import com.fr.stable.ParameterProvider; import com.fr.stable.StringUtils; -import javax.swing.*; -import java.awt.*; +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.List; @@ -44,6 +53,7 @@ public class ContentSettingPane extends AbstractHyperLinkPane<MobilePopupHyperli private JPanel textSettingPanel; private TinyFormulaPane textContentPane; private CustomFontPane fontPane; + private UIComboBox postComboBox; public ContentSettingPane() { super(); @@ -112,7 +122,15 @@ public class ContentSettingPane extends AbstractHyperLinkPane<MobilePopupHyperli JPanel templateContentPane = new JPanel(); templateContentPane.setLayout(new BorderLayout(0,8)); - templateContentPane.add(this.createTemplateSelectPanel(), BorderLayout.NORTH); + if (isNeedShowPostCombo()) { + JPanel jPanel = new JPanel(new BorderLayout(0, 5)); + jPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + jPanel.add(this.createTemplateSelectPanel(), BorderLayout.NORTH); + jPanel.add(this.createPostComboPanel(), BorderLayout.SOUTH); + templateContentPane.add(jPanel, BorderLayout.NORTH); + } else { + templateContentPane.add(this.createTemplateSelectPanel(), BorderLayout.NORTH); + } parameterViewPane = this.createReportletParameterViewPane(); templateContentPane.add(parameterViewPane, BorderLayout.CENTER); @@ -123,6 +141,14 @@ public class ContentSettingPane extends AbstractHyperLinkPane<MobilePopupHyperli return templateContentPane; } + private boolean isNeedShowPostCombo() { + DefaultValueAdjustProvider valueAdjust = DesignUtils.getValueAdjust(); + if (valueAdjust != null) { + return valueAdjust.isNeedPostCombo4MobilePopupPane(); + } + return false; + } + private JPanel createTemplateSelectPanel() { JPanel templatePanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); // 路径输入框 @@ -152,6 +178,15 @@ public class ContentSettingPane extends AbstractHyperLinkPane<MobilePopupHyperli return MobilePopupUIUtils.createLeftTileRightContentPanel(Toolkit.i18nText("FR-Plugin-Designer_Mobile_Popup_Template"), templatePanel); } + private JPanel createPostComboPanel() { + JPanel postComboPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + postComboBox = new UIComboBox(new String[]{"GET", "POST"}); + postComboBox.setPreferredSize(new Dimension(60, 20)); + postComboBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + postComboPanel.add(postComboBox, BorderLayout.WEST); + return MobilePopupUIUtils.createLeftTileRightContentPanel(Toolkit.i18nText("Fine-Design_Basic_Reportlet_Parameter_Type"), postComboPanel); + } + private ReportletParameterViewPane createReportletParameterViewPane() { ReportletParameterViewPane templateParameterViewPane = new ReportletParameterViewPane( new UITableEditAction[]{ @@ -285,6 +320,10 @@ public class ContentSettingPane extends AbstractHyperLinkPane<MobilePopupHyperli ParameterProvider[] parameters =link.getParameters(); parameterViewPane.populate(parameters); + if (isNeedShowPostCombo()) { + this.postComboBox.setSelectedIndex(link.isByPost() ? 1 : 0); + } + // 继承参数 extendParametersCheckBox.setSelected(link.isExtendParameters()); } @@ -292,6 +331,10 @@ public class ContentSettingPane extends AbstractHyperLinkPane<MobilePopupHyperli private void updateTemplateContentBean(MobilePopupHyperlink link) { link.setReportletPath(templatePathTextField.getText()); + if (isNeedShowPostCombo()) { + link.setByPost(postComboBox.getSelectedIndex() == 1); + } + List<ParameterProvider> parameterList = this.parameterViewPane.update(); if (!parameterList.isEmpty()) { Parameter[] parameters = new Parameter[parameterList.size()]; diff --git a/designer-base/src/main/java/com/fr/design/javascript/JSContentPane.java b/designer-base/src/main/java/com/fr/design/javascript/JSContentPane.java index 31c45da762..d752e12254 100644 --- a/designer-base/src/main/java/com/fr/design/javascript/JSContentPane.java +++ b/designer-base/src/main/java/com/fr/design/javascript/JSContentPane.java @@ -25,12 +25,19 @@ import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.mainframe.DesignerContext; import com.fr.general.IOUtils; import com.fr.js.JavaScriptImpl; +import com.fr.stable.StringUtils; +import com.fr.stable.collections.combination.Pair; +import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.KeyStroke; import javax.swing.SwingConstants; import javax.swing.SwingWorker; +import javax.swing.border.EmptyBorder; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; import java.awt.FontMetrics; @@ -54,6 +61,13 @@ public class JSContentPane extends BasicPane { private JSImplPopulateAction jsImplPopulateAction; private boolean modal; BasicDialog advancedEditorDialog ; + private JLabel warningLabel; + //用来标记当前显示状态 + private boolean showWarning = false; + private static final Color WARING_LABEL_BACKGROUND = Color.decode("#FFFBE6"); + private static final Color WARING_LABEL_FOREGROUND = new Color(0, 0, 0, 216); + private JPanel endBracketsPanel; + public JSContentPane(){} public JSContentPane(String[] args) { @@ -73,14 +87,91 @@ public class JSContentPane extends BasicPane { UILabel funNameLabel2 = new UILabel(); funNameLabel2.setText("}"); - this.add(funNameLabel2, BorderLayout.SOUTH); + + if (isNeedContentWarning()) { + endBracketsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + endBracketsPanel.add(funNameLabel2, BorderLayout.NORTH); + warningLabel = initWarningLabel(); + this.add(endBracketsPanel, BorderLayout.SOUTH); + } else { + this.add(funNameLabel2, BorderLayout.SOUTH); + } + } + + protected JLabel initWarningLabel() { + JLabel warningLabel = new JLabel(StringUtils.EMPTY); + warningLabel.setOpaque(true); + warningLabel.setAlignmentX(LEFT_ALIGNMENT); + // 设置左右 5px 的间距 + warningLabel.setBorder(new EmptyBorder(0, 5, 0, 5)); + warningLabel.setPreferredSize(new Dimension(200, 20)); + warningLabel.setBackground(WARING_LABEL_BACKGROUND); + warningLabel.setForeground(WARING_LABEL_FOREGROUND); + + addContentListener4Warning(); + return warningLabel; } + + private void addContentListener4Warning() { + contentTextArea.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + checkContent(contentTextArea); + } + + @Override + public void removeUpdate(DocumentEvent e) { + checkContent(contentTextArea); + } + + @Override + public void changedUpdate(DocumentEvent e) { + checkContent(contentTextArea); + } + }); + } + + private void checkContent(RSyntaxTextArea contentTextArea) { + String content = contentTextArea.getText().trim(); + Pair<Boolean, String> pair = checkContent(content); + boolean matches = Boolean.TRUE.equals(pair.getFirst()); + String tip = pair.getSecond() != null ? pair.getSecond() : StringUtils.EMPTY; + // 更新提示 + updateWarningTip(tip); + + if (matches == showWarning) { + return; + } + if (matches) { + addWarningLabel(); + showWarning = true; + } else { + removeWarningLabel(); + showWarning = false; + } + } + + protected void updateWarningTip(String tip) { + if (!StringUtils.equals(warningLabel.getText(), tip)) { + warningLabel.setText(tip); + } + } + + protected void removeWarningLabel() { + endBracketsPanel.remove(warningLabel); + endBracketsPanel.revalidate(); + } + + protected void addWarningLabel() { + endBracketsPanel.add(warningLabel, BorderLayout.SOUTH); + endBracketsPanel.revalidate(); + } + public JSContentPane(String[] args,boolean modal) { this(args); this.modal = modal; } - public void setJsImplUpdateAction(JSImplUpdateAction jsImplUpdateAction){ this.jsImplUpdateAction = jsImplUpdateAction; } @@ -93,7 +184,6 @@ public class JSContentPane extends BasicPane { this.javaScript = javaScript; } - private void addNewPaneLabel(){ UILabel advancedEditorLabel = new UILabel(Toolkit.i18nText("Fine-Design_Advanced_Editor"), IconUtils.readIcon("com/fr/design/images/edit/advancedEditor.svg"), SwingConstants.LEFT); advancedEditorLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); @@ -328,4 +418,20 @@ public class JSContentPane extends BasicPane { protected boolean needAdvancedEditor() { return true; } + + /** + * 是否支持内容提示 + * @return 是否支持 + */ + protected boolean isNeedContentWarning() { + return false; + } + + /** + * 自定义匹配js内容,并返回对应的提示文本 + * @return <是否匹配,提示文本> + */ + protected Pair<Boolean, String> checkContent(String content) { + return new Pair<>(false, StringUtils.EMPTY); + } } \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/javascript/JSContentWithDescriptionPane.java b/designer-base/src/main/java/com/fr/design/javascript/JSContentWithDescriptionPane.java index 4116f4f1d0..490ff1899b 100644 --- a/designer-base/src/main/java/com/fr/design/javascript/JSContentWithDescriptionPane.java +++ b/designer-base/src/main/java/com/fr/design/javascript/JSContentWithDescriptionPane.java @@ -32,6 +32,7 @@ import javax.swing.BorderFactory; import javax.swing.DefaultListCellRenderer; import javax.swing.DefaultListModel; import javax.swing.JComponent; +import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JPopupMenu; @@ -71,7 +72,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; -import java.util.Locale; import java.util.concurrent.ExecutionException; public class JSContentWithDescriptionPane extends JSContentPane implements KeyListener { @@ -95,6 +95,9 @@ public class JSContentWithDescriptionPane extends JSContentPane implements KeyLi private JPopupMenu popupMenu; + private JPanel endBracketsPanel; + private JLabel warningLabel; + private InterfaceAndDescriptionPanel interfaceAndDescriptionPanel; private JList helpDOCList; @@ -159,8 +162,13 @@ public class JSContentWithDescriptionPane extends JSContentPane implements KeyLi endBracketsLabel.setText("}"); //结尾括号和复用函数按钮面板 - JPanel endBracketsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); - endBracketsPanel.add(endBracketsLabel, BorderLayout.WEST); + endBracketsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + if (isNeedContentWarning()) { + warningLabel = initWarningLabel(); + endBracketsPanel.add(endBracketsLabel, BorderLayout.NORTH); + } else { + endBracketsPanel.add(endBracketsLabel, BorderLayout.WEST); + } JPanel northPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); northPanel.add(jsParaAndSearchPane, BorderLayout.NORTH); @@ -178,6 +186,24 @@ public class JSContentWithDescriptionPane extends JSContentPane implements KeyLi this.add(functionNameAndDescriptionPanel, BorderLayout.SOUTH); } + protected void updateWarningTip(String tip) { + if (!StringUtils.equals(warningLabel.getText(), tip)) { + warningLabel.setText(tip); + } + } + + @Override + protected void removeWarningLabel() { + endBracketsPanel.remove(warningLabel); + endBracketsPanel.revalidate(); + } + + @Override + protected void addWarningLabel() { + endBracketsPanel.add(warningLabel, BorderLayout.SOUTH); + endBracketsPanel.revalidate(); + } + public void populate(String js) { contentTextArea.setText(js); ifHasBeenWriten = 1; diff --git a/designer-base/src/main/java/com/fr/design/javascript/JavaScriptImplPane.java b/designer-base/src/main/java/com/fr/design/javascript/JavaScriptImplPane.java index 37bca2f6eb..3b57db544e 100644 --- a/designer-base/src/main/java/com/fr/design/javascript/JavaScriptImplPane.java +++ b/designer-base/src/main/java/com/fr/design/javascript/JavaScriptImplPane.java @@ -3,6 +3,7 @@ package com.fr.design.javascript; import com.fr.base.Parameter; import com.fr.design.data.tabledata.tabledatapane.OneListTableModel; import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.fun.DefaultValueAdjustProvider; import com.fr.design.gui.frpane.ReportletParameterViewPane; import com.fr.design.gui.itableeditorpane.ParameterTableModel; import com.fr.design.gui.itableeditorpane.UITableEditAction; @@ -13,10 +14,12 @@ import com.fr.design.javascript.jsapi.JSImplPopulateAction; import com.fr.design.javascript.jsapi.JSImplUpdateAction; import com.fr.design.mainframe.DesignerContext; import com.fr.design.scrollruler.ModLineBorder; +import com.fr.design.utils.DesignUtils; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.js.JavaScriptImpl; import com.fr.stable.ParameterProvider; import com.fr.stable.StringUtils; +import com.fr.stable.collections.combination.Pair; import javax.swing.BorderFactory; import javax.swing.JPanel; @@ -84,7 +87,25 @@ public class JavaScriptImplPane extends AbstractHyperLinkPane<JavaScriptImpl> { } protected JSContentPane createJSContentPane(String[] defaultArgs){ - JSContentPane jsContentPane= new JSContentPane(defaultArgs,modal); + JSContentPane jsContentPane= new JSContentPane(defaultArgs,modal) { + @Override + protected boolean isNeedContentWarning() { + DefaultValueAdjustProvider valueAdjust = DesignUtils.getValueAdjust(); + if (valueAdjust != null) { + return valueAdjust.isNeedContentWarning4JsContentPane(); + } + return super.isNeedContentWarning(); + } + + @Override + protected Pair<Boolean, String> checkContent(String content) { + DefaultValueAdjustProvider valueAdjust = DesignUtils.getValueAdjust(); + if (valueAdjust != null) { + return valueAdjust.checkJsContent(content); + } + return super.checkContent(content); + } + }; jsContentPane.setJsImplUpdateAction(new JSImplUpdateAction() { @Override public void update(JavaScriptImpl javaScript) { diff --git a/designer-base/src/main/java/com/fr/design/javascript/NewJavaScriptImplPane.java b/designer-base/src/main/java/com/fr/design/javascript/NewJavaScriptImplPane.java index 23226c6dde..5a59bf8c78 100644 --- a/designer-base/src/main/java/com/fr/design/javascript/NewJavaScriptImplPane.java +++ b/designer-base/src/main/java/com/fr/design/javascript/NewJavaScriptImplPane.java @@ -1,7 +1,10 @@ package com.fr.design.javascript; +import com.fr.design.fun.DefaultValueAdjustProvider; +import com.fr.design.utils.DesignUtils; import com.fr.js.JavaScriptImpl; +import com.fr.stable.collections.combination.Pair; public class NewJavaScriptImplPane extends JavaScriptImplPane { @@ -10,7 +13,25 @@ public class NewJavaScriptImplPane extends JavaScriptImplPane { } protected JSContentPane createJSContentPane(String[] defaultArgs){ - return new JSContentWithDescriptionPane(defaultArgs); + return new JSContentWithDescriptionPane(defaultArgs) { + @Override + protected boolean isNeedContentWarning() { + DefaultValueAdjustProvider valueAdjust = DesignUtils.getValueAdjust(); + if (valueAdjust != null) { + return valueAdjust.isNeedContentWarning4JsContentPane(); + } + return super.isNeedContentWarning(); + } + + @Override + protected Pair<Boolean, String> checkContent(String content) { + DefaultValueAdjustProvider valueAdjust = DesignUtils.getValueAdjust(); + if (valueAdjust != null) { + return valueAdjust.checkJsContent(content); + } + return super.checkContent(content); + } + }; } public void populate(JavaScriptImpl javaScript) { diff --git a/designer-base/src/main/java/com/fr/design/present/dict/TableDataDictPane.java b/designer-base/src/main/java/com/fr/design/present/dict/TableDataDictPane.java index 5ebd426593..f068f71216 100644 --- a/designer-base/src/main/java/com/fr/design/present/dict/TableDataDictPane.java +++ b/designer-base/src/main/java/com/fr/design/present/dict/TableDataDictPane.java @@ -83,7 +83,7 @@ public class TableDataDictPane extends FurtherBasicBeanPane<TableDataDictionary> } private void initBasicComponets() { - tableDataNameComboBox = new TableDataComboBox(DesignTableDataManager.getEditingTableDataSource()); + tableDataNameComboBox = new TableDataComboBox(); tableDataNameComboBox.addItemListener(e -> { if (e.getStateChange() == ItemEvent.SELECTED) { tdChange(e); diff --git a/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java b/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java index 86093a6d82..8565e90f31 100644 --- a/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java +++ b/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java @@ -3,9 +3,12 @@ package com.fr.design.ui.util; import com.fr.log.FineLoggerFactory; import com.fr.third.guava.base.Stopwatch; import com.fr.third.guava.base.Supplier; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import javax.swing.SwingWorker; +import javax.swing.SwingUtilities; import org.jetbrains.annotations.NotNull; -import javax.swing.SwingUtilities; import java.util.concurrent.TimeUnit; /** @@ -81,4 +84,53 @@ public class UIUtil { } return result; } + + /** + * 执行异步任务并在任务完成后更新UI。 + * <p> + * 该方法将执行一个耗时的后台任务,并在任务完成后将结果传递给一个回调函数来更新UI。 + * 使用 SwingWorker 来处理后台任务,确保在任务完成后回到 EDT(事件分发线程) 更新 UI。 + * 提供了一个可选的 `finallyBlock` 参数,用于执行清理操作,例如释放资源或重置状态。 + * + * @param <T> 任务结果的类型 + * @param task 需要在后台执行的任务。该任务的执行过程由 Supplier 提供,返回任务的结果。 + * @param uiUpdater 在任务完成后执行的回调函数,用来处理结果并更新UI。 + * @param finallyBlock 可选的清理操作,在任务结束后无论是否发生异常都会执行。可以传入 null 表示不需要清理操作。常见场景包括状态标志的重置或资源释放。 + * + */ + public static <T> void executeAsyncTaskAndUpdateUI(Supplier<T> task, Consumer<T> uiUpdater, Runnable finallyBlock) { + new SwingWorker<T, Void>() { + @Override + protected T doInBackground() throws Exception { + return task.get(); + } + @Override + protected void done() { + try { + T result = get(); + uiUpdater.accept(result); + } catch (InterruptedException | ExecutionException e) { + FineLoggerFactory.getLogger().debug(e.getMessage(), e); + } finally { + if (finallyBlock != null) { + finallyBlock.run(); + } + } + } + }.execute(); + } + + /** + * 执行异步任务并在任务完成后更新UI。 + * <p> + * 该方法将执行一个耗时的后台任务,并在任务完成后将结果传递给一个回调函数来更新UI。 + * 使用 SwingWorker 来处理后台任务,确保在任务完成后回到 EDT(事件分发线程) 更新 UI。 + * + * @param <T> 任务结果的类型 + * @param task 需要在后台执行的任务。该任务的执行过程由 Supplier 提供,返回任务的结果。 + * @param uiUpdater 在任务完成后执行的回调函数,用来处理结果并更新UI。 + */ + public static <T> void executeAsyncTaskAndUpdateUI(Supplier<T> task, Consumer<T> uiUpdater) { + executeAsyncTaskAndUpdateUI(task, uiUpdater, null); + } } diff --git a/designer-chart/src/main/java/com/fr/design/chart/AutoChartTypePane.java b/designer-chart/src/main/java/com/fr/design/chart/AutoChartTypePane.java index 589028ae52..4aaf6a0281 100644 --- a/designer-chart/src/main/java/com/fr/design/chart/AutoChartTypePane.java +++ b/designer-chart/src/main/java/com/fr/design/chart/AutoChartTypePane.java @@ -141,7 +141,7 @@ public class AutoChartTypePane extends ChartWizardPane implements CallbackEvent } private void initDataFiledBox() { - tableNameComboBox = new TableDataComboBox(DesignTableDataManager.getEditingTableDataSource()); + tableNameComboBox = new TableDataComboBox(); tableNameComboBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { diff --git a/designer-chart/src/main/java/com/fr/design/chartx/component/MapAreaMatchPane.java b/designer-chart/src/main/java/com/fr/design/chartx/component/MapAreaMatchPane.java index 58068d10a8..ba25b5b81c 100644 --- a/designer-chart/src/main/java/com/fr/design/chartx/component/MapAreaMatchPane.java +++ b/designer-chart/src/main/java/com/fr/design/chartx/component/MapAreaMatchPane.java @@ -128,7 +128,7 @@ public class MapAreaMatchPane extends BasicBeanPane<MapMatchResult> { } private void initButtonGroup() { - tableNameCombox = new TableDataComboBox(DesignTableDataManager.getEditingTableDataSource()); + tableNameCombox = new TableDataComboBox(); tableNameCombox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { diff --git a/designer-chart/src/main/java/com/fr/design/mainframe/ChartPropertyPane.java b/designer-chart/src/main/java/com/fr/design/mainframe/ChartPropertyPane.java index 297245960d..fe46528ca5 100644 --- a/designer-chart/src/main/java/com/fr/design/mainframe/ChartPropertyPane.java +++ b/designer-chart/src/main/java/com/fr/design/mainframe/ChartPropertyPane.java @@ -9,18 +9,17 @@ import com.fr.base.chart.BaseChartCollection; import com.fr.chart.chartattr.ChartCollection; import com.fr.chart.charttypes.ChartTypeManager; import com.fr.chartx.attr.ChartProvider; -import com.fr.decision.webservice.v10.map.geojson.helper.GEOJSONHelper; import com.fr.design.ChartTypeInterfaceManager; import com.fr.design.designer.TargetComponent; import com.fr.design.gui.chart.BaseChartPropertyPane; import com.fr.design.gui.chart.ChartEditPaneProvider; import com.fr.design.gui.frpane.UITitlePanel; import com.fr.design.mainframe.chart.ChartEditPane; +import com.fr.design.ui.util.UIUtil; import com.fr.design.utils.gui.GUICoreUtils; import javax.swing.BorderFactory; import javax.swing.Icon; -import javax.swing.SwingWorker; import java.awt.BorderLayout; import java.awt.Component; @@ -116,7 +115,7 @@ public class ChartPropertyPane extends BaseChartPropertyPane { */ public void populateChartPropertyPane(BaseChartCollection collection, TargetComponent<?> ePane) { if (collection instanceof ChartCollection) { - populateChartPropertyPane((ChartCollection) collection, ePane); + UIUtil.invokeAndWaitIfNeeded(() -> populateChartPropertyPane((ChartCollection) collection, ePane)); } } diff --git a/designer-chart/src/main/java/com/fr/design/mainframe/chart/gui/data/DatabaseTableDataPane.java b/designer-chart/src/main/java/com/fr/design/mainframe/chart/gui/data/DatabaseTableDataPane.java index 40983c56b5..3fbf1aa5a4 100644 --- a/designer-chart/src/main/java/com/fr/design/mainframe/chart/gui/data/DatabaseTableDataPane.java +++ b/designer-chart/src/main/java/com/fr/design/mainframe/chart/gui/data/DatabaseTableDataPane.java @@ -82,7 +82,7 @@ public class DatabaseTableDataPane extends BasicPane{ } private void initTableCombox() { - tableNameCombox = new TableDataComboBox(DesignTableDataManager.getEditingTableDataSource()){ + tableNameCombox = new TableDataComboBox(){ //图表的数据集选择下拉框,不需要注册监听,chartEditPane已经注册了。 public void registerDSChangeListener() { diff --git a/designer-realize/src/main/java/com/fr/design/dscolumn/SelectedDataColumnPane.java b/designer-realize/src/main/java/com/fr/design/dscolumn/SelectedDataColumnPane.java index 052fba9046..de10613835 100644 --- a/designer-realize/src/main/java/com/fr/design/dscolumn/SelectedDataColumnPane.java +++ b/designer-realize/src/main/java/com/fr/design/dscolumn/SelectedDataColumnPane.java @@ -330,7 +330,7 @@ public class SelectedDataColumnPane extends BasicPane { protected void initTableNameComboBox() { - tableNameComboBox = new TableDataComboBox(DesignTableDataManager.getEditingTableDataSource()); + tableNameComboBox = new TableDataComboBox(); tableNameComboBox.setPreferredSize(new Dimension(100, 20)); }