diff --git a/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java b/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java index 3da135e7f3..fcaaccfc20 100644 --- a/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java +++ b/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java @@ -36,6 +36,7 @@ import com.fr.design.layout.TableLayoutHelper; import com.fr.design.layout.VerticalFlowLayout; import com.fr.design.mainframe.vcs.VcsConfigManager; import com.fr.design.mainframe.vcs.common.VcsHelper; +import com.fr.design.mainframe.vcs.ui.VcsMovePanel; import com.fr.design.os.impl.SupportOSImpl; import com.fr.design.unit.UnitConvertUtil; import com.fr.design.utils.gui.GUICoreUtils; @@ -54,9 +55,12 @@ import com.fr.stable.os.OperatingSystem; import com.fr.third.apache.logging.log4j.Level; import com.fr.transaction.Configurations; import com.fr.transaction.Worker; +import com.fr.transaction.WorkerAdaptor; import com.fr.workspace.WorkContext; +import com.fr.workspace.server.vcs.VcsConfig; import com.fr.workspace.server.vcs.VcsOperator; import com.fr.workspace.server.vcs.git.config.GcConfig; +import com.fr.workspace.server.vcs.v2.scheduler.VcsAutoCleanOperator; import org.jetbrains.annotations.NotNull; import javax.swing.BorderFactory; @@ -77,14 +81,7 @@ import javax.swing.UIManager; import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Dialog; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.Window; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; @@ -122,6 +119,7 @@ public class PreferencePane extends BasicPane { private static final int PREFERENCE_LABEL_MAX_WIDTH = 460; private static final int OFFSET_HEIGHT = 60; + private static final int VCS_FILL_TOTAL = 5; private static final String TYPE = "pressed"; private static final String DISPLAY_TYPE = "+"; private static final String BACK_SLASH = "BACK_SLASH"; @@ -149,6 +147,32 @@ public class PreferencePane extends BasicPane { private static final Level[] LOG = {Level.FATAL, Level.ERROR, Level.WARN, Level.INFO, Level.DEBUG}; + private static final int ONE_DAY_INT = 1; + private static final int ONE_WEEK_INT = 7; + private static final int ONE_MONTH_INT = 30; + private static final int THREE_MONTH_INT = 90; + private static final int SIX_MONTH_INT = 180; + + private static final int ONE_DAY_INDEX = 0; + private static final int ONE_WEEK_INDEX = 1; + private static final int ONE_MONTH_INDEX = 2; + private static final int THREE_MONTH_INDEX = 3; + private static final int SIX_MONTH_INDEX = 4; + private static final String ONE_DAY = Toolkit.i18nText("Fine-Design_Vcs_Auto_Clean_ONE_DAY"); + private static final String ONE_WEEK = Toolkit.i18nText("Fine-Design_Vcs_Auto_Clean_ONE_WEEK"); + private static final String ONE_MONTH = Toolkit.i18nText("Fine-Design_Vcs_Auto_Clean_ONE_MONTH"); + private static final String THREE_MONTH = Toolkit.i18nText("Fine-Design_Vcs_Auto_Clean_THREE_MONTH"); + private static final String SIX_MONTH = Toolkit.i18nText("Fine-Design_Vcs_Auto_Clean_SIX_MONTH"); + private static final String[] INTERVAL = { + ONE_DAY, + ONE_WEEK, + ONE_MONTH, + THREE_MONTH, + SIX_MONTH + }; + + private static final int DEFAULT_INDEX = 3; + private boolean languageChanged; // 是否修改了设计器语言设置 //设置是否支持undo private UICheckBox supportUndoCheckBox; @@ -186,8 +210,20 @@ public class PreferencePane extends BasicPane { private UICheckBox cloudAnalyticsDelayCheckBox; private UICheckBox vcsEnableCheckBox; + private UICheckBox useVcsAutoSaveScheduleCheckBox; + + private UICheckBox useVcsAutoCleanScheduleCheckBox; + + private UIComboBox autoCleanIntervalComboBox; + private UIComboBox autoCleanRetainIntervalComboBox; + + private IntegerEditor autoSaveIntervalEditor; private UICheckBox saveCommitCheckBox; private UICheckBox useIntervalCheckBox; + private VcsMovePanel movePanel; + + private JPanel saveIntervalPane; + private JPanel autoCleanPane; private UICheckBox startupPageEnabledCheckBox; private IntegerEditor saveIntervalEditor; private UICheckBox gcEnableCheckBox; @@ -227,13 +263,26 @@ public class PreferencePane extends BasicPane { JPanel advancePane = FRGUIPaneFactory.createY_AXISBoxInnerContainer_L_Pane(); UIScrollPane adviceScrollPane = patchScroll(advancePane); jtabPane.addTab(i18nText("Fine-Design_Basic_Advanced"), adviceScrollPane); + //初始化vcs总面板 + JPanel vcsParentPane = new JPanel(); + CardLayout cardLayout = new CardLayout(); + vcsParentPane.setLayout(cardLayout); + //vcs配置面板 + JPanel vcsPane = new JPanel(new BorderLayout()); + //添加滚动条 + UIScrollPane vcsScrollPane = patchScroll(vcsPane); + //配置面板作为vcs总面板的一张卡片 + vcsParentPane.add(vcsScrollPane, VcsMovePanel.SETTING); + jtabPane.addTab(i18nText("Fine-Design_Vcs_Title"), vcsParentPane); contentPane.add(jtabPane, BorderLayout.NORTH); createFunctionPane(generalPane); createEditPane(generalPane); createColorSettingPane(generalPane); - createVcsSettingPane(generalPane); + + // vcsPane + createVcsSettingPane(vcsPane, vcsParentPane, cardLayout); // ConfPane JPanel confLocationPane = FRGUIPaneFactory.createX_AXISBoxInnerContainer_S_Pane(); @@ -345,21 +394,33 @@ public class PreferencePane extends BasicPane { return generalPanelWithScroll; } - private void createVcsSettingPane(JPanel generalPane) { - JPanel vcsPane = FRGUIPaneFactory.createTopVerticalTitledBorderPane(i18nText("Fine-Design_Vcs_Title")); - generalPane.add(vcsPane); + private void createVcsSettingPane(JPanel generalPane,JPanel parentPane, CardLayout cardLayout) { + //迁移面板 + movePanel = createMovePane(cardLayout, parentPane); + generalPane.add(movePanel, BorderLayout.NORTH); + JPanel savePane = FRGUIPaneFactory.createTopVerticalTitledBorderPane(i18nText("Fine-Design_Vcs_Save_Setting")); + JPanel vcsPane = FRGUIPaneFactory.createTopVerticalTitledBorderPane(i18nText("Fine-Design_Vcs_Clean_Setting")); + JPanel containPane = FRGUIPaneFactory.createY_AXISBoxInnerContainer_L_Pane(); + containPane.add(savePane); + containPane.add(vcsPane); + //填充一下面板 + fillPane(containPane, VCS_FILL_TOTAL); + generalPane.add(containPane, BorderLayout.CENTER); remindVcsLabel = new UILabel(i18nText("Fine-Design_Vcs_Remind")); remindVcsLabel.setVisible(!VcsHelper.getInstance().needInit()); vcsEnableCheckBox = new UICheckBox(i18nText("Fine-Design_Vcs_SaveAuto")); + + saveIntervalPane = createSaveIntervalPane(); saveCommitCheckBox = new UICheckBox(i18nText("Fine-Design_Vcs_No_Delete")); saveIntervalEditor = new IntegerEditor(60); useIntervalCheckBox = new UICheckBox(); + savePane.add(vcsEnableCheckBox); + savePane.add(saveIntervalPane); //gc面板 JPanel gcControlPane = createGcControlPane(); JPanel enableVcsPanel = new JPanel(FRGUIPaneFactory.createLeftZeroLayout()); - enableVcsPanel.add(vcsEnableCheckBox); enableVcsPanel.add(remindVcsLabel); JPanel intervalPanel = new JPanel(FRGUIPaneFactory.createLeftZeroLayout()); final UILabel everyLabel = new UILabel(i18nText("Fine-Design_Vcs_Every")); @@ -368,6 +429,7 @@ public class PreferencePane extends BasicPane { intervalPanel.add(everyLabel); intervalPanel.add(saveIntervalEditor); intervalPanel.add(delayLabel); + autoCleanPane = createAutoCleanPane(); vcsEnableCheckBox.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { @@ -390,9 +452,64 @@ public class PreferencePane extends BasicPane { vcsPane.add(enableVcsPanel); vcsPane.add(intervalPanel); vcsPane.add(saveCommitCheckBox); - vcsPane.add(gcControlPane); + vcsPane.add(autoCleanPane); + saveIntervalPane.setVisible(!VcsHelper.getInstance().isLegacyMode()); + autoCleanPane.setVisible(!VcsHelper.getInstance().isLegacyMode()); + if (VcsHelper.getInstance().isLegacyMode()) { + // 老版本时才显示gc选项 + vcsPane.add(gcControlPane); + } + } + + private void fillPane(JPanel containPane, int total) { + for (int i = 0; i < total; i++) { + containPane.add(new JPanel()); + } + } + + private VcsMovePanel createMovePane(CardLayout cardLayout, JPanel parentPane) { + return new VcsMovePanel(cardLayout, parentPane, new VcsMovePanel.MoveCallBack(){ + @Override + public void doCallBack(boolean visible) { + saveIntervalPane.setVisible(visible); + autoCleanPane.setVisible(visible); + } + }); + }; + + private JPanel createAutoCleanPane() { + JPanel autoCleanPane = new JPanel(FRGUIPaneFactory.createLeftZeroLayout()); + useVcsAutoCleanScheduleCheckBox = new UICheckBox(); + autoCleanIntervalComboBox = new UIComboBox(INTERVAL); + autoCleanIntervalComboBox.setSelectedIndex(DEFAULT_INDEX); + autoCleanRetainIntervalComboBox = new UIComboBox(INTERVAL); + autoCleanRetainIntervalComboBox.setSelectedIndex(DEFAULT_INDEX); + autoCleanPane.add(useVcsAutoCleanScheduleCheckBox); + autoCleanPane.add(new UILabel(i18nText("Fine-Design_Vcs_Auto_Clean_Every"))); + autoCleanPane.add(autoCleanIntervalComboBox); + autoCleanPane.add(new UILabel(i18nText("Fine-Design_Vcs_Auto_Clean_Content"))); + autoCleanPane.add(autoCleanRetainIntervalComboBox); + autoCleanPane.add(new UILabel(i18nText("Fine-Design_Vcs_Auto_Clean_Last"))); + useVcsAutoCleanScheduleCheckBox.setEnabled(!VcsHelper.getInstance().isLegacyMode()); + autoCleanPane.setVisible(false); + return autoCleanPane; + } + + private JPanel createSaveIntervalPane() { + JPanel saveIntervalPane = new JPanel(FRGUIPaneFactory.createLeftZeroLayout()); + useVcsAutoSaveScheduleCheckBox = new UICheckBox(); + autoSaveIntervalEditor = new IntegerEditor(60); + saveIntervalPane.add(useVcsAutoSaveScheduleCheckBox); + saveIntervalPane.add(new UILabel(i18nText("Fine-Design_Vcs_Every"))); + saveIntervalPane.add(autoSaveIntervalEditor); + saveIntervalPane.add(new UILabel(i18nText("Fine-Design_Vcs_Save_Delay"))); + useVcsAutoSaveScheduleCheckBox.setEnabled(!VcsHelper.getInstance().isLegacyMode()); + saveIntervalPane.setVisible(false); + return saveIntervalPane; } + + /** * 模创建板版本gc 配置操作面板 * @@ -796,6 +913,10 @@ public class PreferencePane extends BasicPane { gcEnableCheckBox.setSelected(GcConfig.getInstance().isGcEnable()); gcButton.setEnabled(gcEnableCheckBox.isSelected()); + useVcsAutoSaveScheduleCheckBox.setSelected(vcsConfigManager.isUseAutoSave()); + useVcsAutoCleanScheduleCheckBox.setSelected(VcsConfig.getInstance().isUseV2AutoClean()); + autoSaveIntervalEditor.setValue(vcsConfigManager.getAutoSaveInterval()); + gridLineColorTBButton.setColor(designerEnvManager.getGridLineColor()); paginationLineColorTBButton.setColor(designerEnvManager.getPaginationLineColor()); @@ -883,6 +1004,21 @@ public class PreferencePane extends BasicPane { } } + private int getDay(int dateIndex) { + switch (dateIndex) { + case ONE_DAY_INDEX: + return ONE_DAY_INT; + case ONE_WEEK_INDEX: + return ONE_WEEK_INT; + case ONE_MONTH_INDEX: + return ONE_MONTH_INT; + case SIX_MONTH_INDEX: + return SIX_MONTH_INT; + default: + return THREE_MONTH_INT; + } + } + /** * The method of update. */ @@ -931,6 +1067,17 @@ public class PreferencePane extends BasicPane { vcsConfigManager.setVcsEnable(this.vcsEnableCheckBox.isSelected()); vcsConfigManager.setSaveCommit(this.saveCommitCheckBox.isSelected()); vcsConfigManager.setUseInterval(this.useIntervalCheckBox.isSelected()); + vcsConfigManager.setUseAutoSave(this.useVcsAutoSaveScheduleCheckBox.isSelected()); + vcsConfigManager.setAutoSaveInterval(this.autoSaveIntervalEditor.getValue()); + Configurations.update(new WorkerAdaptor(VcsConfig.class) { + @Override + public void run() { + VcsConfig.getInstance().setUseV2AutoClean(useVcsAutoCleanScheduleCheckBox.isSelected()); + VcsConfig.getInstance().setV2CleanInterval(getDay(autoCleanIntervalComboBox.getSelectedIndex())); + VcsConfig.getInstance().setV2RetainInterval(getDay(autoCleanRetainIntervalComboBox.getSelectedIndex())); + } + }); + dealWithSchedule(); designerEnvManager.setStartupPageEnabled(this.startupPageEnabledCheckBox.isSelected()); Configurations.update(new Worker() { @Override @@ -1005,6 +1152,31 @@ public class PreferencePane extends BasicPane { } + private void dealWithSchedule() { + new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + boolean v2FunctionSupport = VcsHelper.getInstance().checkV2FunctionSupport(); + if (v2FunctionSupport) { + //如果支持V2 + if (useVcsAutoSaveScheduleCheckBox.isSelected()) { + FineLoggerFactory.getLogger().info("[VcsV2] start auto save!"); + VcsHelper.getInstance().startAutoSave(autoSaveIntervalEditor.getValue()); + } else { + VcsHelper.getInstance().stopAutoSave(); + } + if (useVcsAutoCleanScheduleCheckBox.isSelected()) { + FineLoggerFactory.getLogger().info("[VcsV2] start auto clean!"); + WorkContext.getCurrent().get(VcsAutoCleanOperator.class).addOrUpdateVcsAutoCleanJob(getDay(autoCleanIntervalComboBox.getSelectedIndex())); + } else { + WorkContext.getCurrent().get(VcsAutoCleanOperator.class).stopVcsAutoCleanJob(); + } + } + return null; + } + }.execute(); + } + // 如果语言设置改变了,则显示重启对话框 public void showRestartDialog() { if (!languageChanged) { diff --git a/designer-base/src/main/java/com/fr/design/file/TemplateTreePane.java b/designer-base/src/main/java/com/fr/design/file/TemplateTreePane.java index d985595f5f..74f26aaf80 100644 --- a/designer-base/src/main/java/com/fr/design/file/TemplateTreePane.java +++ b/designer-base/src/main/java/com/fr/design/file/TemplateTreePane.java @@ -18,6 +18,8 @@ import com.fr.design.lock.LockInfoDialog; import com.fr.design.mainframe.JTemplate; import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; import com.fr.design.mainframe.manager.search.searcher.control.pane.TemplateSearchRemindPane; +import com.fr.design.mainframe.vcs.VcsService; +import com.fr.design.mainframe.vcs.common.VcsHelper; import com.fr.file.FILE; import com.fr.file.FileNodeFILE; import com.fr.file.filetree.FileNode; @@ -38,6 +40,7 @@ import java.util.UUID; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; import javax.swing.ToolTipManager; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeNode; @@ -59,6 +62,7 @@ import java.util.Objects; import java.util.Observable; import java.util.Observer; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import org.jetbrains.annotations.Nullable; @@ -346,13 +350,7 @@ public class TemplateTreePane extends JPanel implements FileOperations { YES_NO_OPTION) == JOptionPane.YES_OPTION) { // 删除所有选中的即可 - if (!deleteNodes(Arrays.asList(treeNodes))) { - FineJOptionPane.showConfirmDialog(null, - Toolkit.i18nText("Fine-Design_Basic_Delete_Failure"), - Toolkit.i18nText("Fine-Design_Basic_Error"), - JOptionPane.DEFAULT_OPTION, - JOptionPane.ERROR_MESSAGE); - } + deleteNodes(Arrays.asList(treeNodes)); } } else { @@ -377,13 +375,7 @@ public class TemplateTreePane extends JPanel implements FileOperations { YES_NO_OPTION) == JOptionPane.YES_OPTION) { // 删除其他 - if (!deleteNodes(deletableNodes)) { - FineJOptionPane.showConfirmDialog(null, - Toolkit.i18nText("Fine-Design_Basic_Delete_Failure"), - Toolkit.i18nText("Fine-Design_Basic_Error"), - JOptionPane.DEFAULT_OPTION, - JOptionPane.ERROR_MESSAGE); - } + deleteNodes(deletableNodes); } } Set deletedFileNode = deletableNodes.stream().map(treeNode -> (FileNode) treeNode.getUserObject()).collect(Collectors.toSet()); @@ -407,23 +399,46 @@ public class TemplateTreePane extends JPanel implements FileOperations { } } - private boolean deleteNodes(Collection nodes) { - - boolean success = true; - for (ExpandMutableTreeNode treeNode : nodes) { - Object node = treeNode.getUserObject(); - if (node instanceof FileNode) { - FileNodeFILE nodeFILE = new FileNodeFILE((FileNode) node); - if (nodeFILE.exists()) { - if (TemplateResourceManager.getResource().delete(nodeFILE)) { - HistoryTemplateListCache.getInstance().deleteFile(nodeFILE); - } else { - success = false; + private void deleteNodes(Collection nodes) { + new SwingWorker(){ + @Override + protected Boolean doInBackground() throws Exception { + boolean success = true; + for (ExpandMutableTreeNode treeNode : nodes) { + Object node = treeNode.getUserObject(); + if (node instanceof FileNode) { + FileNodeFILE nodeFILE = new FileNodeFILE((FileNode) node); + if (nodeFILE.exists()) { + VcsService.getInstance().doRecycle(VcsHelper.getInstance().dealWithFilePath(((FileNode) node).getEnvPath())); + if (TemplateResourceManager.getResource().delete(nodeFILE)) { + HistoryTemplateListCache.getInstance().deleteFile(nodeFILE); + } else { + success = false; + } + } } } + return success; } - } - return success; + @Override + protected void done() { + try { + if (!get()) { + showErrorDialog(); + } + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + }.execute(); + } + + private void showErrorDialog() { + FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Delete_Failure"), + Toolkit.i18nText("Fine-Design_Basic_Error"), + JOptionPane.DEFAULT_OPTION, + JOptionPane.ERROR_MESSAGE); } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java b/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java index fc2f9c658c..6ef68afe7e 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java @@ -16,6 +16,7 @@ import com.fr.design.data.DesignTableDataManager; import com.fr.design.data.datapane.TableDataTreePane; import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager; import com.fr.design.data.tabledata.ResponseDataSourceChange; +import com.fr.design.dialog.BasicDialog; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.file.FileOperations; import com.fr.design.file.FileToolbarStateChangeListener; @@ -34,8 +35,10 @@ import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayoutHelper; import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; import com.fr.design.mainframe.manager.search.searcher.control.pane.TemplateTreeSearchToolbarPane; +import com.fr.design.mainframe.vcs.RecycleAction; import com.fr.design.mainframe.vcs.common.VcsHelper; import com.fr.design.mainframe.vcs.ui.FileVersionsPanel; +import com.fr.design.mainframe.vcs.ui.VcsNewPane; import com.fr.design.menu.KeySetUtils; import com.fr.design.menu.ShortCut; import com.fr.design.menu.ToolBarDef; @@ -150,6 +153,8 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt private VcsAction vcsAction = new VcsAction(); + private RecycleAction recycleAction = new RecycleAction(); + //搜索 private SwitchAction switchAction = new SwitchAction(); private TemplateTreeSearchToolbarPane searchToolbarPane; @@ -325,13 +330,17 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt if (VcsHelper.getInstance().needInit()) { vcsAction = new VcsAction(); - if (!WorkContext.getCurrent().isCluster()) { + if (!isLegacyOnCluster()) { vcsAction.setName(Toolkit.i18nText("Fine-Design_Vcs_Title")); } else { vcsAction.setName(Toolkit.i18nText("Fine-Design_Vcs_NotSupportRemote")); } toolbarDef.addShortCut(vcsAction); - + //11.0.19及其之后加入回收站逻辑 + if (!VcsHelper.getInstance().isLegacyMode()) { + recycleAction = new RecycleAction(); + toolbarDef.addShortCut(recycleAction); + } } } @@ -492,14 +501,19 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt public void actionPerformed(ActionEvent e) { String path = DesignerFrameFileDealerPane.getInstance().getSelectedOperation().getFilePath(); path = StableUtils.pathJoin(ProjectConstants.REPORTLETS_NAME, path); + if (VcsHelper.getInstance().isLegacyMode()) { + boolean currentEditing = isCurrentEditing(path); + // 如果模板已经打开了,关掉,避免出现2个同名tab(1个是模板,1个是版本) + closeOpenedTemplate(path, currentEditing); + FileVersionsPanel fileVersionTablePanel = FileVersionsPanel.getInstance(); + fileVersionTablePanel.showFileVersionsPane(); + stateChange(); + } else { + VcsNewPane panel = new VcsNewPane(path); + BasicDialog dialog = panel.showWindow(DesignerContext.getDesignerFrame(), false); + dialog.setVisible(true); + } - boolean isCurrentEditing = isCurrentEditing(path); - - // 如果模板已经打开了,关掉,避免出现2个同名tab(1个是模板,1个是版本) - closeOpenedTemplate(path, isCurrentEditing); - FileVersionsPanel fileVersionTablePanel = FileVersionsPanel.getInstance(); - fileVersionTablePanel.showFileVersionsPane(); - stateChange(); } @@ -509,7 +523,7 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt private void fireVcsActionChange(boolean enable) { if (!DesignerEnvManager.getEnvManager().getVcsConfigManager().isVcsEnable() || VcsHelper.getInstance().isUnSelectedTemplate() - || WorkContext.getCurrent().isCluster()) { + || isLegacyOnCluster()) { setEnabled(false); return; } @@ -810,6 +824,11 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt } } + private boolean isLegacyOnCluster() { + // 老模式且为集群,用于代替之前的只判断集群逻辑 + return WorkContext.getCurrent().isCluster() && VcsHelper.getInstance().isLegacyMode(); + } + private String doCheck (String userInput, String suffix) { String errorMsg = StringUtils.EMPTY; if (selectedOperation.duplicated(userInput, suffix, true)) { diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/RecycleAction.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/RecycleAction.java new file mode 100644 index 0000000000..aa43ed5b91 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/RecycleAction.java @@ -0,0 +1,31 @@ +package com.fr.design.mainframe.vcs; + +import com.fr.design.actions.UpdateAction; +import com.fr.design.dialog.BasicDialog; +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.vcs.ui.RecyclePane; + +import java.awt.event.ActionEvent; + +/** + * 回收站 + * + * @author Destiny.Lin + * @since 11.0 + * Created on 2023/7/4 + */ +public class RecycleAction extends UpdateAction { + + public RecycleAction() { + this.setSmallIcon("/com/fr/design/standard/vcslist/vcs_recycle"); + this.setName(Toolkit.i18nText("Fine-Design_Vcs_Recycle")); + } + + @Override + public void actionPerformed(ActionEvent e) { + RecyclePane pane = new RecyclePane(); + BasicDialog dialog = pane.showWindow(DesignerContext.getDesignerFrame(), false); + dialog.setVisible(true); + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/TableEntity.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/TableEntity.java new file mode 100644 index 0000000000..a143025ce0 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/TableEntity.java @@ -0,0 +1,25 @@ +package com.fr.design.mainframe.vcs; + +/** + * 表格选中包装类 + * + * @author Destiny.Lin + * @since 11.0 + * Created on 2023/7/11 + */ +public interface TableEntity { + + /** + * 是否选中 + * + * @return + */ + boolean isSelect(); + + /** + * 设置选中属性 + * + * @param select + */ + void setSelect(boolean select); +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/TableValueOperator.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/TableValueOperator.java new file mode 100644 index 0000000000..3fcc57cd02 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/TableValueOperator.java @@ -0,0 +1,21 @@ +package com.fr.design.mainframe.vcs; + + +/** + * 表格操作 + * + * @author Destiny.Lin + * @since 11.0 + * Created on 2023/7/11 + */ +public interface TableValueOperator { + + /** + * 获取对应列的值 + * + * @param o 表格内容 + * @param columnIndex 列数 + * @return 对应列的值 + */ + Object getValue(T o, int columnIndex); +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsConfigManager.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsConfigManager.java index f61bba67a8..8551f735b9 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsConfigManager.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsConfigManager.java @@ -16,6 +16,10 @@ public class VcsConfigManager implements XMLReadable, XMLWriter { private boolean useInterval = true; private int saveInterval = 60; + private boolean useAutoSave = false; + + private int autoSaveInterval = 15; + public static VcsConfigManager getInstance() { return instance; } @@ -48,6 +52,23 @@ public class VcsConfigManager implements XMLReadable, XMLWriter { return saveInterval; } + public int getAutoSaveInterval() { + return autoSaveInterval; + } + + public void setAutoSaveInterval(int autoSaveInterval) { + this.autoSaveInterval = autoSaveInterval; + } + + public boolean isUseAutoSave() { + return useAutoSave; + } + + public void setUseAutoSave(boolean useAutoSave) { + this.useAutoSave = useAutoSave; + } + + public void setSaveInterval(int saveInterval) { this.saveInterval = saveInterval; } @@ -59,6 +80,8 @@ public class VcsConfigManager implements XMLReadable, XMLWriter { this.setSaveInterval(reader.getAttrAsInt("saveInterval", 60)); this.setUseInterval(reader.getAttrAsBoolean("useInterval", true)); this.setVcsEnable(reader.getAttrAsBoolean("vcsEnable", true)); + this.setAutoSaveInterval(reader.getAttrAsInt("autoSaveInterval", 15)); + this.setUseAutoSave(reader.getAttrAsBoolean("useAutoSave", false)); } } @@ -69,6 +92,8 @@ public class VcsConfigManager implements XMLReadable, XMLWriter { writer.attr("saveInterval", this.getSaveInterval()); writer.attr("useInterval", this.isUseInterval()); writer.attr("vcsEnable", this.isVcsEnable()); + writer.attr("autoSaveInterval", this.getAutoSaveInterval()); + writer.attr("useAutoSave", this.isUseAutoSave()); writer.end(); } } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsService.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsService.java new file mode 100644 index 0000000000..e83b0212e9 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsService.java @@ -0,0 +1,162 @@ +package com.fr.design.mainframe.vcs; + +import com.fr.concurrent.NamedThreadFactory; +import com.fr.design.mainframe.vcs.common.VcsHelper; +import com.fr.log.FineLoggerFactory; +import com.fr.report.entity.VcsEntity; +import com.fr.workspace.WorkContext; +import com.fr.workspace.server.vcs.VcsOperator; + +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * 版本管理常用操作 + *

便于版本管理面板快捷实现版本管理相关操作 + * + * @author Destiny.Lin + * @since 11.0 + * Created on 2023/7/10 + */ +public class VcsService { + + private static final String SERVICE = "VcsService"; + private static final VcsService INSTANCE = new VcsService(); + + private static final ExecutorService executorService = Executors.newFixedThreadPool(5, new NamedThreadFactory(SERVICE)); + + /** + * 获取单例 + * + * @return + */ + public static VcsService getInstance() { + return INSTANCE; + } + + private VcsService() { + } + + /** + * 回收模板 + * + * @param filename + */ + public void doRecycle(String filename) { + try { + WorkContext.getCurrent().get(VcsOperator.class).recycleVersion(VcsHelper.getInstance().getCurrentUsername(), filename); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + + + /** + * 从回收站还原版本 + * + * @param vcsEntities + */ + public void doRestore(List vcsEntities) { + try { + executorService.execute(new Runnable() { + @Override + public void run() { + VcsOperator operator = WorkContext.getCurrent().get(VcsOperator.class); + for (VcsEntity vcsEntity : vcsEntities) { + String fileName = vcsEntity.getFilename(); + operator.restoreVersion(fileName); + } + } + }); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + + + /** + * 删除对应列表中所有模板的指定的历史版本 + * + * @param vcsEntities + */ + public void doDelete(List vcsEntities, boolean all) { + try { + executorService.execute(new Runnable() { + @Override + public void run() { + VcsOperator operator = WorkContext.getCurrent().get(VcsOperator.class); + for (VcsEntity vcsEntity : vcsEntities) { + String fileName = vcsEntity.getFilename(); + if (all) { + operator.deleteVersionForRecycle(fileName); + } else { + operator.deleteVersion(fileName, vcsEntity.getVersion()); + } + } + } + }); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + + /** + * 删除指定模板的全部历史版本 + * + * @param entity VcsEntity + */ + public void deleteEntity(VcsEntity entity) { + try { + executorService.execute(new Runnable() { + @Override + public void run() { + VcsOperator vcsOperator = WorkContext.getCurrent().get(VcsOperator.class); + String fileName = entity.getFilename(); + vcsOperator.deleteVersionForRecycle(fileName); + } + }); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + + /** + * 删除指定模板的指定版本 + * + * @param fileName 文件名 + * @param version 版本号 + */ + public void deleteEntity(String fileName, int version) { + try { + executorService.execute(new Runnable() { + @Override + public void run() { + VcsOperator vcsOperator = WorkContext.getCurrent().get(VcsOperator.class); + vcsOperator.deleteVersion(fileName, version); + } + }); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + + /** + * 更新版本 + * + * @param entity 版本 + */ + public void updateEntityAnnotation(VcsEntity entity) { + try { + executorService.execute(new Runnable() { + @Override + public void run() { + VcsOperator vcsOperator = WorkContext.getCurrent().get(VcsOperator.class); + vcsOperator.updateVersion(entity); + } + }); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsTableEntity.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsTableEntity.java new file mode 100644 index 0000000000..200aa38e7a --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsTableEntity.java @@ -0,0 +1,108 @@ +package com.fr.design.mainframe.vcs; + +import com.fr.report.entity.VcsEntity; + +/** + * 包装VcsEntity的用于表格展示与处理的类 + * + * @author Destiny.Lin + * @since 11.0 + * Created on 2023/7/10 + */ +public class VcsTableEntity implements TableEntity{ + + private boolean select = false; + + private static final String MB = "MB"; + + private static final String VERSION = "V."; + private static final double MB_SIZE = 1024.0; + private VcsEntity entity; + + + public VcsTableEntity(VcsEntity entity) { + this.entity = entity; + } + + /** + * 获取模板名 + * + * @return 模板名 + */ + public String getFilename() { + return entity.getFilename(); + } + + /** + * 获取版本大小 + * + * @return 版本大小 + */ + public String getSize() { + return String.format("%.2f",entity.getSize()/MB_SIZE) + MB; + } + + /** + * 获取修改时间 + * + * @return 修改时间 + */ + public String getTime() { + return entity.getTime().toLocaleString(); + } + + /** + * 获取用户名 + * + * @return 用户名 + */ + public String getUserName() { + return entity.getUsername(); + } + + + /** + * 获取备注 + * + * @return 备注 + */ + public String getCommitMsg() { + return entity.getCommitMsg(); + } + + /** + * 获取版本号 + * + * @return 版本号 + */ + public String getVersion() { + return VERSION + entity.getVersion(); + } + + /** + * 获取删除时间 + * + * @return 删除时间 + */ + public String getDeleteTime() { + return entity.getDeleteTime().toLocaleString(); + } + + @Override + public boolean isSelect() { + return select; + } + + @Override + public void setSelect(boolean select) { + this.select = select; + } + + public VcsEntity getEntity() { + return entity; + } + + public void setEntity(VcsEntity entity) { + this.entity = entity; + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsHelper.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsHelper.java index 980ccb5a3a..2a78159217 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsHelper.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsHelper.java @@ -2,6 +2,7 @@ package com.fr.design.mainframe.vcs.common; import com.fr.concurrent.NamedThreadFactory; +import com.fr.config.ConfigEvent; import com.fr.design.DesignerEnvManager; import com.fr.design.file.HistoryTemplateListCache; import com.fr.design.file.TemplateTreePane; @@ -12,6 +13,9 @@ import com.fr.design.mainframe.JTemplate; import com.fr.design.mainframe.JTemplateActionListener; import com.fr.design.mainframe.vcs.VcsConfigManager; import com.fr.design.mainframe.vcs.ui.FileVersionTable; +import com.fr.event.Event; +import com.fr.event.EventDispatcher; +import com.fr.event.ListenerAdaptor; import com.fr.general.IOUtils; import com.fr.log.FineLoggerFactory; import com.fr.plugin.context.PluginContext; @@ -25,11 +29,14 @@ import com.fr.workspace.server.vcs.filesystem.VcsFileSystem; import com.fr.workspace.server.vcs.git.config.GcConfig; import javax.swing.Icon; +import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; import java.awt.Color; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; /** * Created by XiaXiang on 2019/4/17. @@ -55,10 +62,65 @@ public class VcsHelper implements JTemplateActionListener { private final static String SERVICE_NAME_MOVE = "moveVcs"; private static final VcsHelper INSTANCE = new VcsHelper(); + private static ScheduledExecutorService saveSchedule; + + private volatile boolean legacyMode; + public static VcsHelper getInstance() { return INSTANCE; } + private VcsHelper() { + VcsOperator op = WorkContext.getCurrent().get(VcsOperator.class); + // 开了设计器启动页面时一开始取不到VcsOperator,通过下面的切换环境事件再取,这边判断下 + if (op != null) { + legacyMode = op.isLegacyMode(); + } + EventDispatcher.listen(ConfigEvent.READY, new ListenerAdaptor() { + @Override + protected void on(Event event) { + try { + legacyMode = WorkContext.getCurrent().get(VcsOperator.class).isLegacyMode(); + FineLoggerFactory.getLogger().info("[VcsHelper] legacyMode:{}", legacyMode); + } catch (Exception e) { + //保险起见走老逻辑 + legacyMode = true; + FineLoggerFactory.getLogger().error("[VcsHelper] get legacy failed", e.getMessage()); + } + } + }); + } + + /** + * 开始自动保存任务 + * + * @param interval 时间间隔 + */ + public void startAutoSave(int interval) { + stopAutoSave(); + saveSchedule = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("VcsAutoSaveSchedule")); + saveSchedule.scheduleWithFixedDelay(new Runnable() { + @Override + public void run() { + FineLoggerFactory.getLogger().info("[VcsV2] start to run auto save schedule"); + JTemplate template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + if (DesignerEnvManager.getEnvManager().getVcsConfigManager().isVcsEnable() && JTemplate.isValid(template)) { + fireAutoSaveVcs(template); + } + } + }, interval, interval, TimeUnit.MINUTES); + } + + /** + * 停止任务 + */ + public void stopAutoSave() { + if (saveSchedule != null && !saveSchedule.isShutdown()) { + saveSchedule.shutdown(); + } + } + + private int containsFolderCounts() { TemplateFileTree fileTree = TemplateTreePane.getInstance().getTemplateFileTree(); if (fileTree.getSelectionPaths() == null) { @@ -93,18 +155,28 @@ public class VcsHelper implements JTemplateActionListener { } private String getEditingFilename() { - String vcsCacheDir = VcsFileSystem.getInstance().getVcsCacheRelativePath(); JTemplate jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); String editingFilePath = jt.getEditingFILE().getPath(); - if (editingFilePath.startsWith(ProjectConstants.REPORTLETS_NAME)) { - editingFilePath = editingFilePath.replaceFirst(ProjectConstants.REPORTLETS_NAME, StringUtils.EMPTY); - } else if (editingFilePath.startsWith(vcsCacheDir)) { - editingFilePath = editingFilePath.replaceFirst(vcsCacheDir, StringUtils.EMPTY); + return dealWithFilePath(editingFilePath); + } + + /** + * 处理传入的文件名,使其符合Vcs规范 + * + * @param filePath 文件路径 + * @return 处理完的文件 + */ + public String dealWithFilePath(String filePath) { + String vcsCacheDir = VcsFileSystem.getInstance().getVcsCacheRelativePath(); + if (filePath.startsWith(ProjectConstants.REPORTLETS_NAME)) { + filePath = filePath.replaceFirst(ProjectConstants.REPORTLETS_NAME, StringUtils.EMPTY); + } else if (filePath.startsWith(vcsCacheDir)) { + filePath = filePath.replaceFirst(vcsCacheDir, StringUtils.EMPTY); } - if (editingFilePath.startsWith(VCS_FILE_SLASH)) { - editingFilePath = editingFilePath.substring(1); + if (filePath.startsWith(VCS_FILE_SLASH)) { + filePath = filePath.substring(1); } - return editingFilePath; + return filePath; } private boolean needDeleteVersion(VcsEntity entity) { @@ -133,24 +205,21 @@ public class VcsHelper implements JTemplateActionListener { fireVcs.execute(new Runnable() { @Override public void run() { - String fileName = getEditingFilename(); VcsOperator operator = WorkContext.getCurrent().get(VcsOperator.class); VcsEntity entity = operator.getFileVersionByIndex(fileName, 0); + boolean replace = needDeleteVersion(entity); int latestFileVersion = 0; if (entity != null) { latestFileVersion = entity.getVersion(); } if (jt.getEditingFILE() instanceof VcsCacheFileNodeFile) { - operator.saveVersionFromCache(getCurrentUsername(), fileName, StringUtils.EMPTY, latestFileVersion + 1); + operator.saveVersionFromCache(getCurrentUsername(), fileName, StringUtils.EMPTY, latestFileVersion + 1, replace); String path = DesignerFrameFileDealerPane.getInstance().getSelectedOperation().getFilePath(); - FileVersionTable.getInstance().updateModel(1, WorkContext.getCurrent().get(VcsOperator.class).getVersions(path.replaceFirst(VCS_FILE_SLASH, StringUtils.EMPTY))); + List updatedList = WorkContext.getCurrent().get(VcsOperator.class).getVersions(path.replaceFirst(VCS_FILE_SLASH, StringUtils.EMPTY)); + SwingUtilities.invokeLater(() -> FileVersionTable.getInstance().updateModel(1, updatedList)); } else { - operator.saveVersion(getCurrentUsername(), fileName, StringUtils.EMPTY, latestFileVersion + 1); - } - VcsEntity oldEntity = WorkContext.getCurrent().get(VcsOperator.class).getFileVersionByIndexAndUsername(fileName, getCurrentUsername(), 1); - if (needDeleteVersion(oldEntity)) { - operator.deleteVersion(oldEntity.getFilename(), oldEntity.getVersion()); + operator.saveVersion(getCurrentUsername(), fileName, StringUtils.EMPTY, latestFileVersion + 1, replace); } if (GcConfig.getInstance().isGcEnable()) { operator.gc(); @@ -158,7 +227,9 @@ public class VcsHelper implements JTemplateActionListener { } }); - fireVcs.shutdown(); + if (!fireVcs.isShutdown()) { + fireVcs.shutdown(); + } } /** @@ -188,12 +259,85 @@ public class VcsHelper implements JTemplateActionListener { moveVcs.shutdown(); } + /** + * 判断是否为老模式 + * @return 是否为老模式 + */ + public boolean isLegacyMode() { + return legacyMode; + } + + /** + * 更新当前的legacyMode状态 + *

  • 目前用在迁移结束后更新模式为新模式
  • + * + */ + public void updateLegacyMode() { + this.legacyMode = !legacyMode; + } @Override public void templateOpened(JTemplate jt) { + try { + if (checkAutoSaveSupport()) { + startAutoSave(VcsConfigManager.getInstance().getAutoSaveInterval()); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage()); + } + + } + + /** + * 响应版本管理自动保存 + * + *
  • 直接用template的file来保存的话相当于拿源文件来保存模板,这样用户做的改动会丢失
  • + *
  • 因此需要自己实现一下自动保存的逻辑,将当前模板的数据导出,再拿这个Byte[]去做我们需要的保存处理
  • + *
  • 保存后需要触发清理逻辑
  • + * + * @param jt 模板 + */ + public void fireAutoSaveVcs(final JTemplate jt) { + String fileName = getEditingFilename(); + VcsOperator operator = WorkContext.getCurrent().get(VcsOperator.class); + VcsEntity entity = operator.getFileVersionByIndex(fileName, 0); + boolean replace = needDeleteVersion(entity); + int latestFileVersion = 0; + if (entity != null) { + latestFileVersion = entity.getVersion(); + } + if (JTemplate.isValid(jt)) { + doSave(jt, fileName, latestFileVersion, replace, operator); + } + } + + private void doSave(JTemplate jt, String fileName, int latestFileVersion, boolean replace, VcsOperator operator) { + if (jt.getEditingFILE() instanceof VcsCacheFileNodeFile) { + operator.saveVersionFromCache(getCurrentUsername(), fileName, StringUtils.EMPTY, latestFileVersion + 1, replace); + String path = DesignerFrameFileDealerPane.getInstance().getSelectedOperation().getFilePath(); + List updatedList = WorkContext.getCurrent().get(VcsOperator.class).getVersions(path.replaceFirst(VCS_FILE_SLASH, StringUtils.EMPTY)); + SwingUtilities.invokeLater(() -> FileVersionTable.getInstance().updateModel(1, updatedList)); + } else { + autoSave(jt, getCurrentUsername(), fileName, latestFileVersion + 1, replace, operator); + } + if (GcConfig.getInstance().isGcEnable()) { + operator.gc(); + } + } + private void autoSave(JTemplate jt, String currentUsername, String fileName, int nowVersion, boolean replace, VcsOperator operator) { + try { + if (JTemplate.isValid(jt)) { + operator.autoSave(currentUsername, fileName, StringUtils.EMPTY, nowVersion, jt.exportData(), replace); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage()); + } } + + + /** * 模板保存时 处理. * @@ -210,6 +354,40 @@ public class VcsHelper implements JTemplateActionListener { @Override public void templateClosed(JTemplate jt) { + try { + if (checkAutoSaveSupport()) { + stopAutoSave(); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage()); + } + + } + + /** + * 判断是否支持V2功能 + * + * @return 支持返回true + */ + public boolean checkV2FunctionSupport() { + return !VcsHelper.getInstance().isLegacyMode() && (WorkContext.getCurrent().isLocal() || WorkContext.getCurrent().isRoot()); + } + + /** + * 判断是否支持迁移功能 + * + * @return 支持返回true + */ + public boolean checkMoveFunctionSupport() { + return WorkContext.getCurrent().isLocal() || WorkContext.getCurrent().isRoot(); + } + /** + * 是否支持自动保存 + * + * @return 支持返回true + */ + public boolean checkAutoSaveSupport() { + return VcsConfigManager.getInstance().isUseAutoSave() && !VcsHelper.getInstance().isLegacyMode(); } } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/AbstractSupportSelectTablePane.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/AbstractSupportSelectTablePane.java new file mode 100644 index 0000000000..eaa63b1ccd --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/AbstractSupportSelectTablePane.java @@ -0,0 +1,370 @@ +package com.fr.design.mainframe.vcs.ui; + +import com.fr.design.data.tabledata.tabledatapane.loading.TipsPane; +import com.fr.design.dialog.BasicPane; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.itableeditorpane.UITableEditAction; +import com.fr.design.gui.itableeditorpane.UITableEditorPane; +import com.fr.design.gui.itableeditorpane.UITableModelAdapter; +import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.mainframe.vcs.TableEntity; +import com.fr.design.mainframe.vcs.TableValueOperator; +import com.fr.stable.StringUtils; + +import javax.swing.BorderFactory; +import javax.swing.DefaultCellEditor; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.SwingConstants; +import javax.swing.SwingWorker; +import javax.swing.UIManager; +import javax.swing.plaf.ColorUIResource; +import javax.swing.plaf.UIResource; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellRenderer; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.List; +import java.util.concurrent.ExecutionException; + + +/** + * 比较通用的带全选功能的表格面板 + * + *

    整体划分为north与center两个面板(参考BorderLayout的布局),center部分用于置放表格,表格部分支持全选,north部分支持自定义,用于展示各种标签按钮输入框

    + *

    获取数据的时候不需要用SwingWorker(内部已经实现),考虑到大部分表格内容的展现需要去获取数据,而获取数据大部分都需要一定时间,因此为了防止UI冻结,在加载数据结束之前先展示进度条面板

    + *

    使用该面板你可能需要:

    + *
  • 自己封装一个实现TableEntity接口(该接口用于提供(选中/全选)功能),内部存放你要放入表格的类,例如VcsEntity,参考VcsTableEntity
  • + *
  • 初始化的时候赋予变量model值(如果有需要的话),该变量用于控制表格的数据类型,默认DefaultModel——分为5列,第一列为勾选框
  • + *
  • (一定要干的)初始化的时候赋予变量model内operators值,用于确认表格每一列getValueAt返回的值
  • + *
  • 上面板也允许自定义,重写initTableTopPane即可
  • + *
  • 要使用该面板可以参考:RecyclePane
  • + * + * @author Destiny.Lin + * @since 11.0 + * Created on 2023/7/11 + */ +public abstract class AbstractSupportSelectTablePane extends BasicPane { + + public static final Color DEFAULT_HEADER_COLOR = new Color(232, 232, 233); + public static final Color DEFAULT_SELECT_TABLE_ROW_COLOR = new ColorUIResource(200, 221, 233); + private CardLayout layout; + + + protected JPanel contentPane; + + protected UITextField searchTextField = new UITextField();; + + protected UILabel deleteLabel = new UILabel(); + + /** + * 整体面板的center部分,用来放表格 + */ + protected UITableEditorPane tableContentPane; + + protected UITableModelAdapter model; + /** + * 整体面板的north部分 + */ + protected JPanel tableTopPane = new JPanel(); + /** + * 整体面板 + */ + protected JPanel tablePane = new JPanel(); + + private int selectCount = 0; + private static final String LOADING = "loading"; + private static final String TABLE ="table"; + + protected List entities; + + protected TableValueOperator operator; + + + public AbstractSupportSelectTablePane(String title, TableValueOperator operators, String[] tableNames, boolean needBorder) { + this.operator = operators; + this.model = new DefaultModel(tableNames, new Class[]{ + Boolean.class, + UILabel.class, + UILabel.class, + UILabel.class, + UILabel.class + + }); + init(title, needBorder); + } + + public AbstractSupportSelectTablePane(String title, TableValueOperator operators, boolean needBorder) { + this.operator = operators; + init(title, needBorder); + } + + /** + * 初始化 + * + */ + private void init(String title, boolean needBorder) { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + contentPane = new JPanel(); + layout = new CardLayout(); + contentPane.setLayout(layout); + contentPane.add(new TipsPane(true), LOADING); + this.add(contentPane); + new SwingWorker, Void>(){ + @Override + protected List doInBackground() { + return getTableList(); + } + @Override + protected void done() { + try { + entities = get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + contentPane.add(createTablePane(title, needBorder), TABLE); + layout.show(contentPane, TABLE); + } + }.execute(); + } + + /** + * 获取表格数据 + * + * @return 表格数据 + */ + abstract protected List getTableList(); + + private JPanel createTablePane(String title, boolean needBorder) { + tablePane = needBorder ? FRGUIPaneFactory.createTopVerticalTitledBorderPane(title) : new JPanel(); + if (isNeedTopPane()) { + initTableTopPane(); + initTopPaneListener(); + } + initTableContentPane(entities); + tablePane.setLayout(new BorderLayout()); + tablePane.add(tableTopPane, BorderLayout.NORTH); + tablePane.add(tableContentPane, BorderLayout.CENTER); + tableContentPane.getEditTable().getColumnModel().getColumn(0).setMaxWidth(50); + return tablePane; + } + + /** + * 初始化表格面板 + * + * @param entities 表格数据 + */ + protected void initTableContentPane(List entities) { + tableContentPane = new UITableEditorPane<>(model); + model.setList(entities); + JTable table = tableContentPane.getEditTable(); + table.getTableHeader().setBackground(DEFAULT_HEADER_COLOR); + table.getTableHeader().setDefaultRenderer(new HeaderRenderer(tableContentPane.getEditTable())); + table.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + int row = ((JTable) e.getSource()).rowAtPoint(e.getPoint()); + int col = ((JTable) e.getSource()).columnAtPoint(e.getPoint()); + if (col == 0) { + T entity = model.getSelectedValue(); + //改变面板的各个状态 + changeComponentStatus(entity, row, col, table); + } + } + }); + initExtraListener4Table(table); + } + + /** + * 设定额外的表格事件 + * + * @param table 表格 + */ + protected void initExtraListener4Table(JTable table) { + //do nothing + } + + /** + * 是否需要上面板 + * + * @return + */ + protected boolean isNeedTopPane() { + return true; + } + + private void changeComponentStatus(T entity, int row, int col, JTable table) { + boolean select = entity.isSelect(); + entity.setSelect(!select); + table.setValueAt(entity.isSelect(), row, col); + if (select) { + selectCount--; + } else { + selectCount++; + } + //更新表头的勾选框状态 + HeaderRenderer renderer = (HeaderRenderer) table.getTableHeader().getDefaultRenderer(); + renderer.refreshHeader(table, selectCount >= table.getRowCount()); + } + + + /** + * 初始化上面板 + */ + abstract protected void initTableTopPane(); + + /** + * 初始化上面板事件 + */ + abstract protected void initTopPaneListener(); + + + /** + * 表头渲染 + */ + public class HeaderRenderer implements TableCellRenderer { + JTableHeader tableHeader; + final UICheckBox selectBox; + + public HeaderRenderer(JTable table) { + this.tableHeader = table.getTableHeader(); + selectBox = new UICheckBox(); + selectBox.setSelected(false); + tableHeader.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() > 0) { + //获得选中列 + int selectColumn = tableHeader.columnAtPoint(e.getPoint()); + if (selectColumn == 0) { + boolean value = !selectBox.isSelected(); + selectBox.setSelected(value); + selectAllOrNull(value); + selectCount = value ? table.getRowCount() : 0; + tableHeader.repaint(); + model.fireTableDataChanged(); + } + } + } + + private void selectAllOrNull(boolean value) { + int len = table.getRowCount(); + for (T entity : model.getList()) { + entity.setSelect(value); + } + } + }); + } + + /** + * 刷新表头 + * + * @param table + */ + public void refreshHeader(JTable table, boolean value) { + selectBox.setSelected(value); + int rowHeight = table.getRowHeight(); + table.updateUI(); + table.setRowHeight(rowHeight); + } + + @Override + public Component getTableCellRendererComponent(JTable table, + Object value, + boolean isSelected, + boolean hasFocus, + int row, int column) { + tableHeader = table.getTableHeader(); + tableHeader.setReorderingAllowed(false); + String valueStr = (String) value; + JLabel label = new JLabel(valueStr); + label.setHorizontalAlignment(SwingConstants.LEFT); + selectBox.setHorizontalAlignment(SwingConstants.CENTER); + selectBox.setBorderPainted(true); + JComponent component = (column == 0) ? selectBox : label; + component.setForeground(tableHeader.getForeground()); + component.setBackground(tableHeader.getBackground()); + component.setFont(tableHeader.getFont()); + component.setBorder(UIManager.getBorder("TableHeader.cellBorder")); + return component; + } + + + } + + @Override + protected String title4PopupWindow() { + return StringUtils.EMPTY; + } + + /** + * 默认的数据model + */ + public class DefaultModel extends UITableModelAdapter { + + public DefaultModel(String[] tableNames, Class[] classes) { + super(tableNames); + setColumnClass(classes); + this.setDefaultEditor(Boolean.class, new BooleanEditor()); + this.setDefaultRenderer(Boolean.class, new BooleanRenderer()); + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + T vcsEntity = this.getList().get(rowIndex); + return operator.getValue(vcsEntity, columnIndex); + } + + @Override + public boolean isCellEditable(int row, int col) { + return col == 0; + } + + + @Override + public UITableEditAction[] createAction() { + return new UITableEditAction[0]; + } + + + } + + /** + * 用于展示指定风格的checkbox的Editor + */ + public class BooleanEditor extends DefaultCellEditor { + public BooleanEditor() { + super(new UICheckBox()); + UICheckBox checkBox = (UICheckBox) getComponent(); + checkBox.setHorizontalAlignment(UICheckBox.CENTER); + } + } + + /** + * 用于展示指定风格的checkbox的渲染器 + */ + public class BooleanRenderer extends UICheckBox implements TableCellRenderer, UIResource { + public BooleanRenderer() { + super(); + setHorizontalAlignment(JLabel.CENTER); + setBorderPainted(true); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + setSelected((Boolean) table.getValueAt(row, 0)); + setUI(getUICheckBoxUI()); + setBorder(BorderFactory.createEmptyBorder()); + setBackground(isSelected ? DEFAULT_SELECT_TABLE_ROW_COLOR : Color.WHITE); + return this; + } + } + + +} + diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/EditFileVersionDialog.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/EditFileVersionDialog.java index 105a9b872a..13d705754a 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/EditFileVersionDialog.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/EditFileVersionDialog.java @@ -81,12 +81,7 @@ public class EditFileVersionDialog extends UIDialog { ok.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - entity.setCommitMsg(msgTestArea.getText()); - WorkContext.getCurrent().get(VcsOperator.class).updateVersion(entity); - setVisible(false); - String path = DesignerFrameFileDealerPane.getInstance().getSelectedOperation().getFilePath(); - FileVersionTable table = FileVersionTable.getInstance(); - table.updateModel(table.getSelectedRow(), WorkContext.getCurrent().get(VcsOperator.class).getVersions(path.replaceFirst("/", StringUtils.EMPTY))); + doOK(); } }); @@ -101,4 +96,23 @@ public class EditFileVersionDialog extends UIDialog { public void checkValid() throws Exception { } + + /** + * 确定事件 + * + */ + public void doOK() { + entity.setCommitMsg(msgTestArea.getText()); + WorkContext.getCurrent().get(VcsOperator.class).updateVersion(entity); + setVisible(false); + String path = DesignerFrameFileDealerPane.getInstance().getSelectedOperation().getFilePath(); + FileVersionTable table = FileVersionTable.getInstance(); + table.updateModel(table.getSelectedRow(), WorkContext.getCurrent().get(VcsOperator.class).getVersions(path.replaceFirst("/", StringUtils.EMPTY))); + } + + public UITextArea getMsgTestArea() { + return msgTestArea; + } + + } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/RecyclePane.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/RecyclePane.java new file mode 100644 index 0000000000..e829318174 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/RecyclePane.java @@ -0,0 +1,266 @@ +package com.fr.design.mainframe.vcs.ui; + +import com.fr.base.svg.IconUtils; +import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.vcs.VcsService; +import com.fr.design.mainframe.vcs.TableEntity; +import com.fr.design.mainframe.vcs.TableValueOperator; +import com.fr.design.mainframe.vcs.VcsTableEntity; +import com.fr.report.entity.VcsEntity; +import com.fr.stable.StringUtils; +import com.fr.workspace.WorkContext; +import com.fr.workspace.server.vcs.VcsOperator; + +import javax.swing.Icon; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static com.fr.design.i18n.Toolkit.i18nText; + +/** + * 回收面板 + * + * @author Destiny.Lin + * @since 11.0 + * Created on 2023/7/5 + */ +public class RecyclePane extends AbstractSupportSelectTablePane { + public static final Icon ICON_SEARCH = IconUtils.readIcon("/com/fr/design/standard/vcslist/vcs_recycle_search"); + public static final Icon ICON_REFRESH = IconUtils.readIcon("/com/fr/design/standard/vcslist/vcs_recycle_restore"); + public static final Icon ICON_DELETE = IconUtils.readIcon("/com/fr/design/standard/vcslist/vcs_recycle_delete"); + + protected UITextField searchTextField; + + protected UILabel deleteLabel; + + protected UILabel restoreLabel; + + private static final int COLUMNS_COUNT = 15; + + public RecyclePane() { + super(i18nText("Fine-Design_Vcs_Recycle"), (o, columnIndex) -> { + switch (columnIndex) { + case 0: + return o.isSelect(); + case 1: + return o.getFilename(); + case 2: + return o.getSize(); + case 3: + return o.getDeleteTime(); + case 4: + return o.getTime(); + default: + return o; + + } + }, new String[]{ + StringUtils.EMPTY, + Toolkit.i18nText("Fine-Design_Vcs_Template"), + Toolkit.i18nText("Fine-Design_Vcs_Recycle_Size"), + Toolkit.i18nText("Fine-Design_Vcs_Delete_Time"), + Toolkit.i18nText("Fine-Design_Vcs_Time") + }, true); + } + + public RecyclePane(String title, TableValueOperator operators, boolean needBorder) { + super(title, operators, needBorder); + } + + + @Override + protected List getTableList() { + List entityList = WorkContext.getCurrent().get(VcsOperator.class).getRecycleEntities(); + List tableEntities = new ArrayList<>(); + for (VcsEntity entity : entityList) { + tableEntities.add(new VcsTableEntity(entity)); + } + return tableEntities; + } + + + @Override + protected void initTableTopPane() { + tableTopPane = new JPanel(); + tableTopPane.setLayout(new BorderLayout()); + JPanel leftPane = new JPanel(); + JPanel rightPane = new JPanel(); + //左边面板,包含搜索icon+搜索框 + if (isNeedSearch()) { + searchTextField = new UITextField(); + searchTextField.setPlaceholder(Toolkit.i18nText("Fine-Design_Vcs_Start_Search")); + searchTextField.setColumns(COLUMNS_COUNT); + leftPane.add(new UILabel(ICON_SEARCH)); + leftPane.add(searchTextField); + } + //右边面板,包括还原按钮+删除按钮 + if (isNeedRestore()) { + restoreLabel = new UILabel(ICON_REFRESH); + restoreLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); + rightPane.add(restoreLabel); + } + if (isNeedDelete()) { + deleteLabel = new UILabel(ICON_DELETE); + deleteLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); + rightPane.add(deleteLabel); + } + tableTopPane.add(leftPane, BorderLayout.EAST); + tableTopPane.add(rightPane, BorderLayout.WEST); + } + + @Override + protected void initTopPaneListener() { + initSearchTextFiledListener(); + initRestoreListener(); + initDeleteLabelListener(); + } + + private void initDeleteLabelListener() { + if (isNeedDelete()) { + deleteLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + fireListener(new VcsResponseListener() { + @Override + public void doAfterChooseYes(List selectList) { + VcsService.getInstance().doDelete(selectList, isNeedDeleteAllVersion()); + } + }, true); + } + }); + } + } + + private void initRestoreListener() { + if (isNeedRestore()) { + restoreLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + fireListener(new VcsResponseListener() { + @Override + public void doAfterChooseYes(List selectList) { + VcsService.getInstance().doRestore(selectList); + } + }, false); + } + }); + } + } + + private void initSearchTextFiledListener() { + if (isNeedSearch()) { + searchTextField.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String str = searchTextField.getText(); + List entityList = model.getList(); + model.setList(entityList.stream().filter(entity -> entity.getEntity().getFilename().contains(str)).collect(Collectors.toList())); + model.fireTableDataChanged(); + } + }); + } + + } + + private void fireListener(VcsResponseListener listener, boolean isDelete) { + List selectList = model.getList().stream().filter(TableEntity::isSelect).map(VcsTableEntity::getEntity).collect(Collectors.toList()); + if (selectList.size() > 0) { + int selVal = FineJOptionPane.showConfirmDialog( + RecyclePane.this, + isDelete ? getDeleteTip(selectList.size()) : getRestoreTip(selectList.size()), + Toolkit.i18nText("Fine-Design_Basic_Confirm"), + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE); + if (selVal == JOptionPane.YES_OPTION) { + model.setList(model.getList().stream().filter(tableEntity -> !tableEntity.isSelect()).collect(Collectors.toList())); + model.fireTableDataChanged(); + listener.doAfterChooseYes(selectList); + } + } + } + + + /** + * 删除范围 + * + * @return 默认删除全部版本 + */ + protected boolean isNeedDeleteAllVersion() { + return true; + } + + @Override + protected String title4PopupWindow() { + return i18nText("Fine-Design_Vcs_Recycle"); + } + + /** + * 删除提示 + * + * @return 默认需要 + */ + protected String getDeleteTip(int size) { + return Toolkit.i18nText("Fine-Design_Vcs_Delete_Select_All_Version_Forever", size); + } + + /** + * 还原提示 + * + * @return 默认需要 + */ + protected String getRestoreTip(int size) { + return Toolkit.i18nText("Fine-Design_Vcs_Restore_Select_All_Version", size); + } + + /** + * 是否需要还原功能 + * + * @return 默认需要 + */ + protected boolean isNeedRestore() { + return true; + } + + /** + * 是否需要搜索功能 + * + * @return 默认需要 + */ + protected boolean isNeedSearch() { + return true; + } + + /** + * 是否需要删除功能 + * + * @return 默认需要 + */ + protected boolean isNeedDelete() { + return true; + } + + /** + * 版本管理按钮事件响应 + */ + public interface VcsResponseListener { + + /** + * 选择确认之后要做的事情 + * + * @param selectList 所选上的内容 + */ + void doAfterChooseYes(List selectList); + + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/VcsCenterPane.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/VcsCenterPane.java new file mode 100644 index 0000000000..6d927b8376 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/VcsCenterPane.java @@ -0,0 +1,192 @@ +package com.fr.design.mainframe.vcs.ui; + +import com.fr.base.svg.IconUtils; +import com.fr.design.dialog.BasicDialog; +import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.vcs.VcsService; + +import com.fr.design.mainframe.vcs.VcsTableEntity; +import com.fr.file.FileNodeFILE; +import com.fr.file.filetree.FileNode; +import com.fr.report.entity.VcsEntity; +import com.fr.stable.StableUtils; +import com.fr.stable.StringUtils; +import com.fr.stable.project.ProjectConstants; +import com.fr.workspace.WorkContext; +import com.fr.workspace.server.vcs.VcsOperator; + +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JOptionPane; +import javax.swing.JTable; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; + + +/** + * 版本中心面板 + * + * @author Destiny.Lin + * @since 11.0 + * Created on 2023/7/13 + */ +public class VcsCenterPane extends VcsNewPane { + + private UILabel manager; + private UILabel open; + private UILabel delete; + + private static final Icon MANAGER_ICON = IconUtils.readIcon("/com/fr/design/standard/vcslist/vcs_center_manager"); + private static final Icon DELETE_ICON = IconUtils.readIcon("/com/fr/design/standard/vcslist/vcs_operator_delete"); + private static final Icon OPEN_ICON = IconUtils.readIcon("/com/fr/design/standard/vcslist/vcs_center_open"); + private static final Class[] CLASSES = new Class[]{ + Boolean.class, + UILabel.class, + UILabel.class, + UILabel.class, + VcsOperatorPane.class + }; + + private static final int OPERATOR_COL = 4; + private static final String[] COLUMN_NAMES = new String[]{ + StringUtils.EMPTY, + Toolkit.i18nText("Fine-Design_Vcs_Template"), + Toolkit.i18nText("Fine-Design_Vcs_Time"), + Toolkit.i18nText("Fine-Design_Vcs_Size"), + Toolkit.i18nText("Fine-Design_Vcs_Operator") + }; + + private static final String TITLE = Toolkit.i18nText("Fine-Design_Vcs_Center"); + + public VcsCenterPane() { + super(TITLE, (o, columnIndex) -> { + switch (columnIndex) { + case 0: + return o.isSelect(); + case 1: + return o.getFilename(); + case 2: + return o.getTime(); + case 3: + return o.getSize(); + default: + return o; + } + }, false, COLUMN_NAMES, CLASSES, OPERATOR_COL); + } + + @Override + public VcsOperatorPane createOperatorPane() { + manager = new UILabel(MANAGER_ICON); + open = new UILabel(OPEN_ICON); + delete = new UILabel(DELETE_ICON); + initManagerListener(); + initOpenListener(); + initDeleteListener(); + List components = new ArrayList<>(); + components.add(manager); + components.add(open); + components.add(delete); + return new VcsOperatorPane(components); + } + + private void initDeleteListener() { + delete.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + JTable table = tableContentPane.getEditTable(); + Object o = table.getValueAt(table.getEditingRow(), table.getEditingColumn()); + if (o instanceof VcsTableEntity) { + VcsEntity entity = ((VcsTableEntity) o).getEntity(); + String fileName = entity.getFilename(); + int selVal = FineJOptionPane.showConfirmDialog( + VcsCenterPane.this, + Toolkit.i18nText("Fine-Design_Vcs_Center_Delete", fileName), + Toolkit.i18nText("Fine-Design_Basic_Confirm"), + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE); + if (selVal == JOptionPane.YES_OPTION) { + VcsService.getInstance().deleteEntity(entity); + } + DesignerContext.getDesignerFrame().openTemplate(new FileNodeFILE(new FileNode(getTemplateTruePath(fileName), false))); + } + } + }); + } + + private void initOpenListener() { + open.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + JTable table = tableContentPane.getEditTable(); + Object o = table.getValueAt(table.getEditingRow(), table.getEditingColumn()); + if (o instanceof VcsTableEntity) { + VcsEntity entity = ((VcsTableEntity) o).getEntity(); + DesignerContext.getDesignerFrame().openTemplate(new FileNodeFILE(new FileNode(getTemplateTruePath(entity.getFilename()), false))); + } + } + }); + } + + private void initManagerListener() { + manager.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + JTable table = tableContentPane.getEditTable(); + Object o = table.getValueAt(table.getEditingRow(), table.getEditingColumn()); + if (o instanceof VcsTableEntity) { + VcsEntity entity = ((VcsTableEntity) o).getEntity(); + VcsNewPane pane = new VcsNewPane(getTemplateTruePath(entity.getFilename())) { + @Override + protected String title4PopupWindow() { + return entity.getFilename()+Toolkit.i18nText("Fine-Design_Vcs_Version_Tips"); + } + }; + BasicDialog dialog = pane.showWindow(DesignerContext.getDesignerFrame(), false); + dialog.setVisible(true); + } + } + }); + } + + @Override + protected List getTableList() { + List entities = WorkContext.getCurrent().get(VcsOperator.class).getEveryVersion(); + List tableEntities = new ArrayList<>(); + for (VcsEntity entity : entities) { + tableEntities.add(new VcsTableEntity(entity)); + } + return tableEntities; + } + + @Override + protected String title4PopupWindow() { + return TITLE; + } + + + private String getTemplateTruePath(String filename) { + return StableUtils.pathJoin(ProjectConstants.REPORTLETS_NAME, filename); + } + + + @Override + protected String getDeleteTip(int size) { + return Toolkit.i18nText("Fine-Design_Vcs_Delete_Select_All_Version"); + } + + @Override + protected boolean isNeedTopPane() { + return true; + } + + @Override + protected boolean isNeedRestore() { + return false; + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/VcsMovePanel.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/VcsMovePanel.java new file mode 100644 index 0000000000..a8a719a188 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/VcsMovePanel.java @@ -0,0 +1,434 @@ +package com.fr.design.mainframe.vcs.ui; + + +import com.fr.base.svg.IconUtils; +import com.fr.design.dialog.BasicDialog; +import com.fr.design.dialog.BasicPane; +import com.fr.design.dialog.DialogActionAdapter; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.ibutton.UIRadioButton; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.iprogressbar.ModernUIProgressBarUI; +import com.fr.design.gui.ispinner.UISpinner; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.VerticalFlowLayout; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.vcs.common.VcsHelper; +import com.fr.design.utils.DesignUtils; +import com.fr.design.widget.FRWidgetFactory; +import com.fr.general.FRFont; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.StringUtils; +import com.fr.workspace.server.vcs.v2.move.VcsMoveService; +import com.fr.workspace.server.vcs.v2.move.VcsMoveStrategy; + +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.List; +import java.util.concurrent.ExecutionException; + + +/** + * 迁移面板 + * + * @author Destiny.Lin + * @since 11.0 + * Created on 2023/6/13 + */ +public class VcsMovePanel extends BasicPane { + private static final FRFont FONT = DesignUtils + .getDefaultGUIFont() + .applySize(14) + .applyStyle(FRFont.BOLD); + + private static final Color BACK_GROUND_COLOR = new Color(202,232,255); + + //提示字体的颜色,直接模仿其他面板的写法 + private static final Color TIP_COLOR = new Color(51, 51, 52, (int)Math.round(0.5 * 255)); + + private static final Color LABEL_COLOR = new Color(34,149,233); + + private static final int MIN_VALUE = 1; + + private static final int MAX_VALUE = 999; + + private static final int STEP = 1; + + private static final int DEFAULT_VALUE = 5; + + public static final String SETTING = "SETTING"; + + public static final String PROCESS = "PROCESS"; + + public static final String SUCCESS = "SUCCESS"; + + public static final String FAILED = "FAILED"; + + public static boolean moving = false; + private UILabel vcsUpdateExistLabel = new UILabel(); + + private UILabel vcsUpdateFireLabel = new UILabel(); + + private UIButton centerButton; + private BasicPane choosePane; + private CardLayout parentCard; + private JPanel parentPane; + private static final JProgressBar PROGRESS_BAR = new JProgressBar(); + + private JPanel progressPanel; + private UILabel tipLabel; + private UIButton successButton; + private UILabel iconLabel; + private UILabel successLabel; + private UILabel successTipLabel; + private UIButton failedButton; + private UILabel failedIconLabel; + private UILabel failedLabel; + private UILabel failedTipLabel; + + //保留全部 + private UIRadioButton moveAllButton; + //默认选项,保留部分 + private UIRadioButton moveDefaultButton; + //全部放弃 + private UIRadioButton moveNothingButton; + + private UISpinner spinner; + + private MoveCallBack callBack; + + private JPanel updatePane; + + private boolean visible = false; + + + public VcsMovePanel(CardLayout cardLayout, JPanel parentPane, MoveCallBack callBack) { + this.parentCard = cardLayout; + this.parentPane = parentPane; + this.callBack = callBack; + this.setLayout(new BorderLayout()); + updatePane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane(); + updatePane.setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 0)); + //初始化顶部的面板 + initVcsLabel(updatePane); + //initVcsChoosePane + initVcsChoosePane(); + //初始化listener + initListener(); + this.add(updatePane); + checkVisible(); + //如果已经在迁移 + if (VcsMoveService.getInstance().isMoving()) { + initProcessPane(); + VcsMovePanel.this.getParentCard().show(getParentPane(), PROCESS); + } + } + + private void checkVisible() { + new SwingWorker() { + @Override + protected Boolean doInBackground() throws Exception { + return VcsHelper.getInstance().checkMoveFunctionSupport(); + } + @Override + protected void done() { + try { + boolean useMove = get(); + VcsMovePanel.this.setVisible(useMove); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + }.execute(); + } + + private void initProcessPane() { + JPanel processPane = new JPanel(); + JPanel body = FRGUIPaneFactory.createY_AXISBoxInnerContainer_L_Pane(); + PROGRESS_BAR.setStringPainted(true); + PROGRESS_BAR.setUI(new ModernUIProgressBarUI()); + PROGRESS_BAR.setBorderPainted(false); + PROGRESS_BAR.setOpaque(false); + PROGRESS_BAR.setBorder(null); + PROGRESS_BAR.setSize(BasicDialog.MEDIUM); + body.add(PROGRESS_BAR); + body.add(new UILabel(StringUtils.BLANK)); + tipLabel = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_While_Moving")); + tipLabel.setAlignmentX(CENTER_ALIGNMENT); + body.add(tipLabel); + processPane.add(body); + processPane.setLayout(FRGUIPaneFactory.createCenterLayout(body, 0.5f, 0.5f)); + parentPane.add(processPane, PROCESS); + } + + private void initVcsChoosePane() { + choosePane = new BasicPane() { + @Override + protected String title4PopupWindow() { + return Toolkit.i18nText("Fine-Design_Vcs_Deal_With_Entry"); + } + }; + VerticalFlowLayout layout = new VerticalFlowLayout(VerticalFlowLayout.TOP); + layout.setAlignLeft(true); + choosePane.setLayout(layout); + + //初始化上方的文字板块 + initTopDesc(); + //初始化中间区域的单选框 + initRadioButton(); + //初始化下方的Tip描述 + initTipDesc(); + } + + private void initTopDesc() { + UILabel label = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_How_To_Deal_With_Entry")); + choosePane.add(label); + } + + private void initTipDesc() { + UILabel descLabel = FRWidgetFactory.createLineWrapLabel(Toolkit.i18nText("Fine-Design_Vcs_Move_Desc")); + descLabel.setForeground(TIP_COLOR); + choosePane.add(descLabel); + } + + private void initRadioButton() { + //保留全部 + moveAllButton = new UIRadioButton(Toolkit.i18nText("Fine-Design_Vcs_Move_All")); + //默认选项,保留部分 + moveDefaultButton = new UIRadioButton(Toolkit.i18nText("Fine-Design_Vcs_Move_Default")); + //全部放弃 + moveNothingButton = new UIRadioButton(Toolkit.i18nText("Fine-Design_Vcs_Move_Nothing")); + // 将按钮"保留部分"设置为选中状态 + moveDefaultButton.setSelected(true); + // 创建一个按钮组,添加三个按钮 + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(moveAllButton); + buttonGroup.add(moveDefaultButton); + buttonGroup.add(moveNothingButton); + + JPanel moveDefaultPanel = new JPanel(); + JPanel moveAllPane = new JPanel(); + JPanel moveNothingPane = new JPanel(); + moveAllPane.add(moveAllButton); + moveDefaultPanel.add(moveDefaultButton); + moveDefaultPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Move_Default_Text_Left"))); + spinner = new UISpinner(MIN_VALUE, MAX_VALUE, STEP, DEFAULT_VALUE); + moveDefaultPanel.add(spinner); + moveDefaultPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Move_Default_Text_Right"))); + moveNothingPane.add(moveNothingButton); + choosePane.add(moveAllPane); + choosePane.add(moveDefaultPanel); + choosePane.add(moveNothingPane); + } + + private void initVcsLabel(JPanel parent) { + if (!VcsHelper.getInstance().isLegacyMode()) { + centerButton = new UIButton(Toolkit.i18nText("Fine-Design_Vcs_Center")); + parent.add(centerButton); + initVcsCenterListener(); + } else { + parent.setBackground(BACK_GROUND_COLOR); + vcsUpdateExistLabel = new UILabel(IconUtils.readIcon("/com/fr/design/vcs/vcs_move_icon.svg")); + vcsUpdateExistLabel.setText(Toolkit.i18nText("Fine-Design_Vcs_Can_Update")); + vcsUpdateFireLabel = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Update")); + vcsUpdateFireLabel.setForeground(LABEL_COLOR); + vcsUpdateFireLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); + parent.add(vcsUpdateExistLabel); + parent.add(vcsUpdateFireLabel); + } + } + + private void initVcsCenterListener() { + centerButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + VcsCenterPane vcsCenterPane = new VcsCenterPane(); + BasicDialog dialog = vcsCenterPane.showWindow(DesignerContext.getDesignerFrame(), false); + dialog.setVisible(true); + + } + }); + } + + private void initListener() { + vcsUpdateFireLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + BasicDialog dlg = choosePane.showMediumWindow(SwingUtilities.getWindowAncestor(VcsMovePanel.this), new DialogActionAdapter() { + @Override + public void doOk() { + //进度条面板 + initProcessPane(); + VcsMovePanel.this.getParentCard().next(getParentPane()); + VcsMoveStrategy strategy; + if (moveDefaultButton.isSelected()) { + strategy = VcsMoveStrategy.createNumStrategy((int) spinner.getValue()); + } else if (moveNothingButton.isSelected()) { + strategy = VcsMoveStrategy.ALL_GIVE_UP; + } else { + strategy = VcsMoveStrategy.ALL_RETAIN; + } + new MoveWorker(strategy).execute(); + } + }); + dlg.setVisible(true); + } + }); + } + + private void initSuccessPane() { + JPanel successPane = new JPanel(); + JPanel body = FRGUIPaneFactory.createY_AXISBoxInnerContainer_L_Pane(); + successButton = new UIButton(Toolkit.i18nText("Fine-Design_Vcs_Move_Success_Go")); + initSuccessButtonListener(); + iconLabel = new UILabel(IconUtils.readIcon("/com/fr/design/vcs/move_success.svg")); + successLabel = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Move_Success")); + successLabel.setFont(FONT); + successTipLabel = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Move_Success_Tip")); + initStatusPane(successTipLabel, iconLabel, successLabel, successButton, body, SUCCESS, successPane); + } + + private void initSuccessButtonListener() { + successButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + doAfterMove(); + } + }); + } + + private void initFailedButtonListener() { + failedButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + doAfterMove(); + } + }); + } + + private void doAfterMove() { + visible = !VcsHelper.getInstance().isLegacyMode(); + if (visible) { + updatePane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane(); + updatePane.add(new UIButton(Toolkit.i18nText("Fine-Design_Vcs_Center"))); + } + updatePane.setVisible(!visible); + callBack.doCallBack(visible); + parentCard.show(parentPane, SETTING); + } + + + @Override + protected String title4PopupWindow() { + return StringUtils.EMPTY; + } + + public CardLayout getParentCard() { + return parentCard; + } + + public JPanel getParentPane() { + return parentPane; + } + + private void initFailedPane() { + JPanel failedPane = new JPanel(); + JPanel body = FRGUIPaneFactory.createY_AXISBoxInnerContainer_L_Pane(); + failedButton = new UIButton(Toolkit.i18nText("Fine-Design_Vcs_Move_Failed_Go")); + initFailedButtonListener(); + failedIconLabel = new UILabel(IconUtils.readIcon("/com/fr/design/vcs/move_failed.svg")); + failedLabel = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Move_Failed")); + failedLabel.setFont(FONT); + failedTipLabel = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Move_Failed_Tip")); + initStatusPane(failedTipLabel, failedIconLabel, failedLabel, failedButton, body, FAILED, failedPane); + } + + private void initStatusPane(UILabel tipLabel, UILabel iconLabel, UILabel label, UIButton button, JPanel body, String tag,JPanel statusPane) { + tipLabel.setForeground(TIP_COLOR); + body.add(iconLabel); + body.add(new UILabel(StringUtils.BLANK)); + body.add(label); + body.add(new UILabel(StringUtils.BLANK)); + body.add(tipLabel); + body.add(new UILabel(StringUtils.BLANK)); + body.add(button); + statusPane.add(body); + statusPane.setLayout(FRGUIPaneFactory.createCenterLayout(body, 0.5f, 0.5f)); + parentPane.add(statusPane, tag); + iconLabel.setAlignmentX(CENTER_ALIGNMENT); + label.setAlignmentX(CENTER_ALIGNMENT); + button.setAlignmentX(CENTER_ALIGNMENT); + tipLabel.setAlignmentX(CENTER_ALIGNMENT); + } + + + private class MoveWorker extends SwingWorker { + + private VcsMoveStrategy strategy; + + public MoveWorker(VcsMoveStrategy strategy) { + this.strategy = strategy; + } + + @Override + protected Void doInBackground() throws Exception { + try { + //开始迁移 + VcsMoveService.getInstance().startMove(new VcsMoveService.BaseMoveServiceWhileMoving() { + @Override + public void publishProgress() { + int num = VcsMoveService.getInstance().getCurrentMove(); + publish(num); + } + @Override + public void prepare4Move() { + PROGRESS_BAR.setMaximum(VcsMoveService.getInstance().getTotal()); + } + }, strategy); + } catch (Exception e) { + this.cancel(true); + VcsMoveService.getInstance().stopMoving(); + initFailedPane(); + VcsMovePanel.this.getParentCard().show(getParentPane(), FAILED); + FineLoggerFactory.getLogger().error("[VcsV2] Vcs move failed!"); + } + return null; + } + + @Override + protected void process(List chunks) { + PROGRESS_BAR.setValue(chunks.get(chunks.size() - 1)); + } + + @Override + protected void done() { + VcsMoveService.getInstance().stopMoving(); + initSuccessPane(); + VcsMovePanel.this.getParentCard().show(getParentPane(), SUCCESS); + VcsHelper.getInstance().updateLegacyMode(); + } + } + + + /** + * 迁移回调事件 + * + */ + public static class MoveCallBack { + /** + * 处理回调 + */ + public void doCallBack(boolean visible){} + } + +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/VcsNewPane.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/VcsNewPane.java new file mode 100644 index 0000000000..0916062ccb --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/VcsNewPane.java @@ -0,0 +1,339 @@ +package com.fr.design.mainframe.vcs.ui; + +import com.fr.base.svg.IconUtils; +import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.file.MultiTemplateTabPane; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.JTemplate; +import com.fr.design.mainframe.vcs.TableValueOperator; +import com.fr.design.mainframe.vcs.VcsService; +import com.fr.design.mainframe.vcs.VcsTableEntity; +import com.fr.design.mainframe.vcs.common.VcsCacheFileNodeFile; +import com.fr.design.mainframe.vcs.common.VcsHelper; +import com.fr.file.FileNodeFILE; +import com.fr.file.filetree.FileNode; +import com.fr.report.entity.VcsEntity; +import com.fr.stable.StringUtils; +import com.fr.workspace.WorkContext; +import com.fr.workspace.server.vcs.VcsOperator; + +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JOptionPane; +import javax.swing.JTable; +import javax.swing.SwingWorker; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; + + +/** + * 版本管理交互优化新面板 + *
  • 采用表格的形式展现
  • + * + * @author Destiny.Lin + * @since 11.0 + * Created on 2023/7/11 + */ +public class VcsNewPane extends RecyclePane { + + private String filePath = StringUtils.EMPTY; + + protected static int OPERATOR_COL = 5; + + protected static int EDIT_COL = 4; + + + private static final int DOUBLE_CLICK_COUNT = 2; + protected VcsOperatorPane operatorPane; + private static final Icon PREVIEW_ICON = IconUtils.readIcon("/com/fr/design/standard/vcslist/vcs_operator_preview"); + private static final Icon DELETE_ICON = IconUtils.readIcon("/com/fr/design/standard/vcslist/vcs_operator_delete"); + private static final Icon RESTORE_ICON = IconUtils.readIcon("/com/fr/design/standard/vcslist/vcs_operator_restore"); + + private UILabel restore; + private UILabel delete; + private UILabel preview; + private static final String TITLE = Toolkit.i18nText("Fine-Design_Vcs_Title"); + private static final String[] COLUMN_NAMES = new String[]{ + StringUtils.EMPTY, + Toolkit.i18nText("Fine-Design_Vcs_Version"), + Toolkit.i18nText("Fine-Design_Vcs_Time"), + Toolkit.i18nText("Fine-Design_Vcs_User"), + Toolkit.i18nText("Fine-Design_Vcs_Annotation"), + Toolkit.i18nText("Fine-Design_Vcs_Operator") + + }; + + private static final Class[] CLASSES = new Class[]{ + Boolean.class, + UILabel.class, + UILabel.class, + UILabel.class, + UILabel.class, + VcsOperatorPane.class + }; + + + public VcsNewPane(String filePath) { + super(TITLE, (o, columnIndex) -> { + switch (columnIndex) { + case 0: + return o.isSelect(); + case 1: + return o.getVersion(); + case 2: + return o.getTime(); + case 3: + return o.getUserName(); + case 4: + return o.getCommitMsg(); + default: + return o; + } + }, false); + this.filePath = filePath; + initModel(COLUMN_NAMES, CLASSES, OPERATOR_COL); + } + + + private void initModel(String[] columns, Class[] classes, int operatorCol) { + this.model = new DefaultModel(columns, classes) { + @Override + public boolean isCellEditable(int row, int col) { + return col == 0 || col == operatorCol; + } + }; + this.operatorPane = createOperatorPane(); + this.model.setDefaultEditor(VcsOperatorPane.class, operatorPane); + this.model.setDefaultRenderer(VcsOperatorPane.class, operatorPane); + } + + + public VcsNewPane(String title, TableValueOperator operators, boolean needBorder, String[] columns, Class[] classes, int operatorCol) { + super(title, operators, needBorder); + initModel(columns, classes, operatorCol); + } + + /** + * 创建操作面板 + * + * @return 操作面板 + */ + public VcsOperatorPane createOperatorPane() { + restore = new UILabel(RESTORE_ICON); + delete = new UILabel(DELETE_ICON); + preview = new UILabel(PREVIEW_ICON); + initPreviewListener(); + initDeleteListener(); + initRestoreListener(); + List list = new ArrayList<>(); + list.add(restore); + list.add(delete); + list.add(preview); + return new VcsOperatorPane(list); + } + + private void initRestoreListener() { + restore.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + JTable table = tableContentPane.getEditTable(); + Object o = table.getValueAt(table.getEditingRow(), table.getEditingColumn()); + if (o instanceof VcsTableEntity) { + VcsEntity entity = ((VcsTableEntity) o).getEntity(); + int selVal = FineJOptionPane.showConfirmDialog( + VcsNewPane.this, + Toolkit.i18nText("Fine-Design_Vcs_Restore_This_Version_Tips"), + Toolkit.i18nText("Fine-Design_Basic_Confirm"), + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE); + if (selVal == JOptionPane.YES_OPTION) { + restoreEntity(entity); + } + } + } + }); + } + + + private void restoreEntity(VcsEntity entity) { + new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + //step1.设置还原的用户名 + entity.setUsername(VcsHelper.getInstance().getCurrentUsername()); + //step2.rollback到指定版本 + WorkContext.getCurrent().get(VcsOperator.class).rollbackTo(entity); + return null; + } + @Override + protected void done() { + //step3.如果原来原模板已经打开则关闭原模板,打开rollback后的新模板 + List> templateList = HistoryTemplateListCache.getInstance().getHistoryList(); + for (JTemplate template : templateList) { + if (StringUtils.equals(filePath, template.getPath())) { + MultiTemplateTabPane.getInstance().setIsCloseCurrent(HistoryTemplateListCache.getInstance().isCurrentEditingFile(filePath)); + MultiTemplateTabPane.getInstance().closeFormat(template); + MultiTemplateTabPane.getInstance().closeSpecifiedTemplate(template); + break; + } + } + DesignerContext.getDesignerFrame().openTemplate(new FileNodeFILE(new FileNode(filePath, false))); + } + }.execute(); + } + + + private void initDeleteListener() { + delete.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + JTable table = tableContentPane.getEditTable(); + int row = table.getEditingColumn(); + Object o = table.getValueAt(table.getEditingRow(), row); + if (o instanceof VcsTableEntity) { + VcsEntity entity = ((VcsTableEntity) o).getEntity(); + int selVal = FineJOptionPane.showConfirmDialog( + VcsNewPane.this, + Toolkit.i18nText("Fine-Design_Vcs_Delete_This_Version_Tips"), + Toolkit.i18nText("Fine-Design_Basic_Confirm"), + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE); + if (selVal == JOptionPane.YES_OPTION) { + model.getList().remove(o); + model.fireTableDataChanged(); + VcsService.getInstance().deleteEntity(entity.getFilename(), entity.getVersion()); + } + } + } + }); + } + + private void initPreviewListener() { + preview.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + JTable table = tableContentPane.getEditTable(); + Object o = table.getValueAt(table.getEditingRow(), table.getEditingColumn()); + if (o instanceof VcsTableEntity) { + VcsEntity entity = ((VcsTableEntity) o).getEntity(); + previewEntity(entity); + } + } + }); + } + + private void previewEntity(VcsEntity entity) { + new SwingWorker() { + @Override + protected String doInBackground() throws Exception { + //step1.将指定版本的历史文件读取出来,加上前缀放到cache中 + VcsOperator vcsOperator = WorkContext.getCurrent().get(VcsOperator.class); + String fileOfVersion = vcsOperator.getFileOfFileVersion( + entity.getFilename(), + entity.getVersion(), + Toolkit.i18nText("Fine-Design_Vcs_Prefix", entity.getVersion())); + return fileOfVersion; + } + @Override + protected void done() { + try { + //step2.再打开cache中的模板 + DesignerContext.getDesignerFrame().openTemplate(new VcsCacheFileNodeFile(new FileNode(get(), false))); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + }.execute(); + } + + @Override + protected boolean isNeedDeleteAllVersion() { + return false; + } + + @Override + protected List getTableList() { + List entityList = WorkContext.getCurrent().get(VcsOperator.class).getVersions(VcsHelper.getInstance().dealWithFilePath(filePath)); + List tableEntities = new ArrayList<>(); + for (VcsEntity entity : entityList) { + tableEntities.add(new VcsTableEntity(entity)); + } + return tableEntities; + } + + @Override + protected String title4PopupWindow() { + return TITLE; + } + + @Override + protected String getDeleteTip(int size) { + return Toolkit.i18nText("Fine-Design_Vcs_Delete_Versions_Tips", size); + } + + @Override + protected boolean isNeedRestore() { + return false; + } + + @Override + protected boolean isNeedSearch() { + return false; + } + + @Override + protected void initExtraListener4Table(JTable table) { + table.addMouseMotionListener(new MouseMotionAdapter() { + @Override + public void mouseMoved(MouseEvent e) { + Point point = e.getPoint(); + int row = table.rowAtPoint(point); + int col = table.columnAtPoint(point); + if (col != 0) { + table.editCellAt(row, col); + } + } + }); + initEditListener(table); + } + + private void initEditListener(JTable table) { + table.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == DOUBLE_CLICK_COUNT) { + Point point = e.getPoint(); + int row = table.rowAtPoint(point); + int col = table.columnAtPoint(point); + if (col == EDIT_COL) { + Object o = table.getValueAt(row, OPERATOR_COL); + if (o instanceof VcsTableEntity) { + showDialog(((VcsTableEntity) o).getEntity()); + } + } + } + } + }); + } + + private void showDialog(VcsEntity entity) { + EditFileVersionDialog dialog = new EditFileVersionDialog(entity) { + @Override + public void doOK() { + entity.setCommitMsg(getMsgTestArea().getText()); + VcsService.getInstance().updateEntityAnnotation(entity); + setVisible(false); + model.fireTableDataChanged(); + } + }; + dialog.setVisible(true); + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/VcsOperatorPane.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/VcsOperatorPane.java new file mode 100644 index 0000000000..9da20d8a54 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/VcsOperatorPane.java @@ -0,0 +1,63 @@ +package com.fr.design.mainframe.vcs.ui; + + +import com.fr.design.layout.FRGUIPaneFactory; + + +import javax.swing.AbstractCellEditor; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JTable; + +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; +import java.awt.*; +import java.util.List; + + +import static com.fr.design.mainframe.vcs.ui.AbstractSupportSelectTablePane.DEFAULT_SELECT_TABLE_ROW_COLOR; + +/** + * 操作面板,用于置放常用的操作label + *

    例如 删除、还原、预览、打开模板等

    + *
  • 负责表格渲染 与 操作按钮置蓝+设定cursor
  • + * + * @author Destiny.Lin + * @since 11.0 + * Created on 2023/7/13 + */ +public class VcsOperatorPane extends AbstractCellEditor implements TableCellEditor, TableCellRenderer { + private JPanel contentPane; + + private static final Color DETAIL_FONT_COLOR = new Color(65, 155, 249); + + public VcsOperatorPane(List iconJComponentMap) { + init(iconJComponentMap); + } + + private void init(List iconJComponentMap) { + contentPane = new JPanel(FRGUIPaneFactory.createLeftZeroVgapNormalHgapLayout()); + for (JComponent value : iconJComponentMap) { + value.setForeground(DETAIL_FONT_COLOR); + value.setCursor(new Cursor(Cursor.HAND_CURSOR)); + contentPane.add(value); + } + } + + @Override + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + return contentPane; + } + + @Override + public Object getCellEditorValue() { + return contentPane; + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + contentPane.setBackground(isSelected ? DEFAULT_SELECT_TABLE_ROW_COLOR : Color.WHITE); + return contentPane; + } +} diff --git a/designer-base/src/main/resources/com/fr/design/images/buttonicon/new_other_normal.svg b/designer-base/src/main/resources/com/fr/design/images/buttonicon/new_other_normal.svg new file mode 100644 index 0000000000..0b3799c9a9 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/images/buttonicon/new_other_normal.svg @@ -0,0 +1,4 @@ + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_center_manager_normal.svg b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_center_manager_normal.svg new file mode 100644 index 0000000000..8208a290c2 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_center_manager_normal.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_center_open_normal.svg b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_center_open_normal.svg new file mode 100644 index 0000000000..7a3ed4b330 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_center_open_normal.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_operator_delete_normal.svg b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_operator_delete_normal.svg new file mode 100644 index 0000000000..9ae3274c7e --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_operator_delete_normal.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_operator_preview_normal.svg b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_operator_preview_normal.svg new file mode 100644 index 0000000000..79d9883fd0 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_operator_preview_normal.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_operator_restore_normal.svg b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_operator_restore_normal.svg new file mode 100644 index 0000000000..60cf06e303 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_operator_restore_normal.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_delete_disabled.svg b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_delete_disabled.svg new file mode 100644 index 0000000000..1a96116130 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_delete_disabled.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_delete_normal.svg b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_delete_normal.svg new file mode 100644 index 0000000000..730e3493da --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_delete_normal.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_normal.svg b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_normal.svg new file mode 100644 index 0000000000..ab3b97e703 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_normal.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_restore_disabled.svg b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_restore_disabled.svg new file mode 100644 index 0000000000..54afcb1273 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_restore_disabled.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_restore_normal.svg b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_restore_normal.svg new file mode 100644 index 0000000000..f1b4b30359 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_restore_normal.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_search_normal.svg b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_search_normal.svg new file mode 100644 index 0000000000..a557ec1aae --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/vcslist/vcs_recycle_search_normal.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/vcs/move_failed.svg b/designer-base/src/main/resources/com/fr/design/vcs/move_failed.svg new file mode 100644 index 0000000000..6d36a9dc0c --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/vcs/move_failed.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-base/src/main/resources/com/fr/design/vcs/move_success.svg b/designer-base/src/main/resources/com/fr/design/vcs/move_success.svg new file mode 100644 index 0000000000..8b92bd8a04 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/vcs/move_success.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-base/src/main/resources/com/fr/design/vcs/vcs_move_icon.svg b/designer-base/src/main/resources/com/fr/design/vcs/vcs_move_icon.svg new file mode 100644 index 0000000000..a5ad91d09c --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/vcs/vcs_move_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-form/src/main/java/com/fr/design/actions/NewFormAction.java b/designer-form/src/main/java/com/fr/design/actions/NewFormAction.java index aa50c13f69..d619c73007 100644 --- a/designer-form/src/main/java/com/fr/design/actions/NewFormAction.java +++ b/designer-form/src/main/java/com/fr/design/actions/NewFormAction.java @@ -6,16 +6,12 @@ import com.fr.design.widget.ui.designer.NewFormPane; import javax.swing.KeyStroke; import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; - -import static com.fr.design.gui.syntax.ui.rtextarea.RTADefaultInputMap.DEFAULT_MODIFIER; public class NewFormAction extends UpdateAction { public NewFormAction() { this.setMenuKeySet(NEW_FORM); - this.setName(getMenuKeySet().getMenuKeySetName()); - this.setMnemonic(getMenuKeySet().getMnemonic()); + this.setName(getMenuKeySet().getMenuName()); this.setSmallIcon("/com/fr/design/images/buttonicon/new_form3"); this.setAccelerator(getMenuKeySet().getKeyStroke()); } @@ -33,7 +29,7 @@ public class NewFormAction extends UpdateAction { public static final MenuKeySet NEW_FORM = new MenuKeySet() { @Override public char getMnemonic() { - return 'F'; + return 0; } @Override diff --git a/designer-realize/src/main/java/com/fr/start/MainDesigner.java b/designer-realize/src/main/java/com/fr/start/MainDesigner.java index 767e608c15..98a4d4c76e 100644 --- a/designer-realize/src/main/java/com/fr/start/MainDesigner.java +++ b/designer-realize/src/main/java/com/fr/start/MainDesigner.java @@ -213,14 +213,18 @@ public class MainDesigner extends BaseDesigner { ArrayList shortCuts = new ArrayList(); // shortCuts.add(new NewWorkBookXAction()); shortCuts.add(new NewWorkBookAction()); + // 决策报表、聚合报表归入其他 + MenuDef newOtherFileMenuDef = new MenuDef(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_M_New_Other_Template")); + newOtherFileMenuDef.setIconPath("/com/fr/design/images/buttonicon/new_other"); try { if (DesignModuleFactory.getNewFormAction() != null) { - shortCuts.add((ShortCut) DesignModuleFactory.getNewFormAction().newInstance()); + newOtherFileMenuDef.addShortCut((ShortCut) DesignModuleFactory.getNewFormAction().newInstance()); } } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } - shortCuts.add(new NewPolyReportAction()); + newOtherFileMenuDef.addShortCut(new NewPolyReportAction()); + shortCuts.add(newOtherFileMenuDef); return shortCuts.toArray(new ShortCut[0]); } diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_fvs1.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_fvs1.png new file mode 100644 index 0000000000..22584335d4 Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_fvs1.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_fvs2.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_fvs2.png new file mode 100644 index 0000000000..44c751c277 Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_fvs2.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_fvs3.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_fvs3.png new file mode 100644 index 0000000000..10a4e890a1 Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_fvs3.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_fvs4.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_fvs4.png new file mode 100644 index 0000000000..617859e8d6 Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_fvs4.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/template_resource/local_templates.json b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/template_resource/local_templates.json index 4e9265b14e..bbb194adea 100644 --- a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/template_resource/local_templates.json +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/template_resource/local_templates.json @@ -1,110 +1,110 @@ [ { - "id": 20000770, - "name": "PDCA销售运营闭环管理方案", - "uuid": "7975ad73-9aea-4634-96f1-d33ec1b4283c", - "vendor": "Victoria.", - "sellerId": 202, - "cid": "industry-3,purpose-2,purpose-16,purpose-15", + "id": 20000992, + "name": "经营决策财务系统", + "uuid": "0ba003c12aa36e12ee45c1114eb8ff4377ecdc6743070fb2b8209c19723554a7", + "vendor": "帆软可视化", + "sellerId": 213, + "cid": "template_type-2,terminal-1,industry-3,purpose-1,purpose-16,purpose-15,purpose-8", "tag": null, - "pic": "com/fr/design/mainframe/alphafine/images/local_template1.png", + "pic": "com/fr/design/mainframe/alphafine/images/local_fvs1.png", "price": 0, - "fileLoca": null, - "text": "
    1. 此方案共包括14张模板,其中12张可视化分析看板、1张分析报表以及1张填报表,需在10.0及以上版本设计器中预览;

    2. 了解方案详情,可移步-数据分析与可视化指南

    3. 如果您希望在线体验当前方案,可移步-官方DEMO系统:业务场景应用->营销管理

    4. 如果您对此方案感兴趣,希望更加深入了解,可以填写此表单:方案需求录入;后续会有帆软行业顾问将与您联系。

    ", - "description": "将PDCA循环嵌入销售运营管理的全流程中,从决策层、管理层与执行层三个层级,对目标制定、执行过程、复盘分析、问题跟踪进行全方位精细化管理。", - "updateTime": "2022-06-15T06:36:39.000Z", - "downloadTimes": 1152, + "fileLoca": "经营决策财务系统.zip", + "text": "

    文件包共包含2个文件:「经营决策财务系统.fvs」「经营决策财务系统数据.db」,下载即可使用:

    1、本模板要求使用 FR11.0 正式发布版本,且安装「FVS大屏编辑模式插件」,若下载后无法打开模板请更新插件至最新版本。插件介绍文档请参考:FVS大屏编辑模式简介

    2、请将经营决策财务系统.fvs」文件直接放在设计器 \\FineReport_11.0\\webapps\\webroot\\WEB-INF\\reportlets 路径下,进入设计器即可打开使用。

    3、模板中数据依赖于经营决策财务系统数据.db」,请将此数据库文件放在工程的%FR_HOME%\\webapps\\webroot\\help文件夹下,并完成对应的数据连接。具体连接过程请参考文档:JDBC连接数据库

    ", + "description": "该模板通过对企业财务、人资、流程分析,搭建企业关键的经营决策财务系统,辅助决策者分析信息,做出正确的决策。", + "updateTime": "2023-06-25T07:04:05.000Z", + "downloadTimes": 20, "state": 1, - "designVersion": "10.0", - "involvePlugins": null, - "uploadTime": "2022-06-15T06:36:39.000Z", - "style": null, + "designVersion": "11.0", + "involvePlugins": "1162", + "uploadTime": "2023-06-25T07:04:05.000Z", + "style": "4", "link": "template", - "sellerName": "Victoria.", - "pluginName": [], - "pkgsize": 14, - "material": "方案附件.rar", - "tagsName": "制造加工,营销,组织,管理,经营汇报" + "sellerName": "帆软图表", + "pluginName": ["{id: 1162, name: fvs大屏编辑模式}"], + "pkgsize": 0, + "material": null, + "tagsName": "制造加工,财务,组织管理,经营汇报,人力资源" }, { - "id": 20000733, - "name": "库存场景解决方案", - "uuid": "43d1c14b-1a73-41e6-adcc-aaf2872bc8d4", - "vendor": "Victoria.", - "sellerId": 202, - "cid": "industry-3,purpose-5", + "id": 20000973, + "name": "帆软医疗驾驶舱", + "uuid": "1393a2e2610f64ad96726f4a99307168f0d9f921036b70f3e92a6578171cc460", + "vendor": "帆软可视化", + "sellerId": 213, + "cid": "template_type-2,terminal-1,industry-5,purpose-15,purpose-16,purpose-9,purpose-10", "tag": null, - "pic": "com/fr/design/mainframe/alphafine/images/local_template2.png", + "pic": "com/fr/design/mainframe/alphafine/images/local_fvs2.png", "price": 0, - "fileLoca": null, - "text": "

    当前库存管理面临的现状:库存成本劣势明显、库存周转差距较大。如果对此没有合理有效的解决方案,往往会导致企业库存越来越混乱,经营、资金、成本等问题丛生。

    库存管理解决方案从:“盘”、“析”、“管”三个方向开展,彻底解决库存量大、结构复杂、管理混乱等常见问题

    ", - "description": "库存管理解决方案即从:“盘”、“析”、“管”三个方向开展,对导致库存管理问题的原因逐一击破。", - "updateTime": "2022-05-05T03:53:55.000Z", - "downloadTimes": 816, + "fileLoca": "帆软医疗.zip", + "text": "

    此模板压缩包内包「帆软医疗驾驶舱.fvs」和「帆软医疗.db」。

    1、使用该模板需先下载「FVS大屏编辑模式插件」V1.13及以上版本。插件介绍文档请参考:FVS大屏编辑模式简介

    2、将「帆软医疗.db」数据库文件放在工程%FR_HOME%\\webapps\\webroot\\help路径下,并完成对应的数据连接,可参考帮助文档:定义数据连接

    3、将文件夹「帆软医疗驾驶舱.fvs」放在工程%FR_HOME%\\webapps\\webroot\\WEB-INF\\reportlets路径下

    ", + "description": "该模板通过对医院关键业务指标进行展示,对医院的财务情况进行监测与分析、从门诊、住院、科室三个维度,全方位掌握医院经营情况。", + "updateTime": "2023-03-28T07:45:30.000Z", + "downloadTimes": 211, "state": 1, - "designVersion": "10.0", - "involvePlugins": null, - "uploadTime": "2022-05-05T03:53:55.000Z", - "style": null, + "designVersion": "11.0", + "involvePlugins": "1162", + "uploadTime": "2023-03-28T07:45:30.000Z", + "style": "1", "link": "template", - "sellerName": "Victoria.", - "pluginName": [], - "pkgsize": 11, - "material": "", - "tagsName":"制造加工,库存" + "sellerName": "帆软图表", + "pluginName": ["{id: 1162, name: fvs大屏编辑模式}"], + "pkgsize": 0, + "material": null, + "tagsName":" 医疗医药,经营汇报,组织管理,服务,IT部门" }, { - "id": 20000581, - "name": "采购场景解决方案", - "uuid": "7994b01f-5069-4554-83cf-9d3506e30767", - "vendor": "Victoria.", - "sellerId": 202, - "cid": "industry-3,purpose-3", + "id": 20000985, + "name": "生产场景解决方案", + "uuid": "a207681de3be5e7a73e2336274a93908bc5d313a1e61ecbbf669cdd0130ab4e8", + "vendor": "帆软可视化", + "sellerId": 213, + "cid": "template_type-2,terminal-1,industry-3,purpose-4,purpose-7", "tag": null, - "pic": "com/fr/design/mainframe/alphafine/images/local_template3.png", + "pic": "com/fr/design/mainframe/alphafine/images/local_fvs3.png", "price": 0, - "fileLoca": null, - "text": "

    采购场景方案具体细节可参考:可视化指南-采购场景应用

    ", - "description": "采购解决方案采用:“自上而下”的分析思路,针对采购相关的不同角色层级及其不同角度发数据分析需求,产出不同的内容", - "updateTime": "2022-03-10T03:50:17.000Z", - "downloadTimes": 2353, + "fileLoca": "生产场景解决方案.zip", + "text": "

    文件包共包含3个文件:「生产场景综合看板.fvs」「设备日报弹窗.fvs」「生产场景解决方案.db」,下载即可使用:

    1、本模板要求使用 FR11.0 正式发布版本,且安装「FVS大屏编辑模式插件」,若下载后无法打开模板请更新插件至最新版本。插件介绍文档请参考:FVS大屏编辑模式简介

    2、请将「生产场景综合看板.fvs」「设备日报弹窗.fvs」文件夹直接放在设计器 \\FineReport_11.0\\webapps\\webroot\\WEB-INF\\reportlets 路径下,进入设计器即可打开使用。

    3、模板中数据依赖于「生产场景解决方案.db」,请将此数据库文件放在工程的%FR_HOME%\\webapps\\webroot\\help文件夹下,并完成对应的数据连接。具体连接过程请参考文档:JDBC连接数据库

    ", + "description": "该模板方案将生产产量、合格率、计划产量以及完成产量进行系统的数据分析,将产量、质量、设备以日报的形式展现生产情况,并从集团维度展示各地区生产情况。", + "updateTime": "2023-06-06T01:55:56.000Z", + "downloadTimes": 100, "state": 1, - "designVersion": "10.0", - "involvePlugins": null, - "uploadTime": "2022-03-10T03:50:18.000Z", - "style": null, + "designVersion": "11.0", + "involvePlugins": "1162", + "uploadTime": "2023-06-06T01:55:56.000Z", + "style": "1", "link": "template", - "sellerName": "Victoria.", - "pluginName": [], - "pkgsize": 6, - "material": "", - "tagsName": "制造加工,采购" + "sellerName": "帆软图表", + "pluginName": ["{id: 1162, name: fvs大屏编辑模式}"], + "pkgsize": 0, + "material": null, + "tagsName": "制造加工,生产,设备" }, { - "id": 20000747, - "name": "费用预算系统解决方案", - "uuid": "0776533469c3401a8da78706856b6b02", + "id": 20000785, + "name": "人力资源管理综合分析看板", + "uuid": "25ff9ad15d4f4e2b91e79ddb3b426f27", "vendor": "finereport", "sellerId": 1, - "cid": "template_type-1,terminal-1,industry-13,purpose-1", + "cid": "template_type-2,terminal-1,industry-5,purpose-8", "tag": null, - "pic": "com/fr/design/mainframe/alphafine/images/local_template4.png", + "pic": "com/fr/design/mainframe/alphafine/images/local_fvs4.png", "price": 0, - "fileLoca": "费用预算系统解决方案.zip", - "text": "

    模板中使用了数据查询数据集,如果要在本地打开,需要在自己的数据库中建表,并修改数据连接。

    1)建表语句见附件文件夹:SQL语句与表。


    2)数据连接默认为 FRDemo,如果用户表所在的数据库连接不是 FRDemo,将模板中数据连接修改为用户默认的数据连接。


    3)模板中 SQL 语句为 MYSQL 数据库,若用户使用中数据库语句不匹配,可对应修改为匹配的语句。


    4)模板中有大量的超链接,用户存在本地后,预览时注意修改超链接。


    5)费用预算系统的平台沿用人事管理系统的平台配置,如果要在本地体验效果,可先下载人事管理系统部署:人事管理系统

    模板中图标等背景样式见背景附件。 如果数据替换麻烦,可直接使用内置数据集模板,但内置模板只提供了样式,控件、数据等不能联动。如何制作,可查看帮助文档:费用预算系统

    ", - "description": "费用预算系统是通过 FineReport 填报功能和平台功能结合实现的审批流程系统。能够在线采集数据,提供标准数据模板,解决不同部门、子公司之间数据不统一的问题,\n实现记录预算审批与更改的操作,审批与更改记录留痕等。\n详细内容可查看帮助文档:费用预算系统", - "updateTime": "2022-05-15T05:34:45.000Z", - "downloadTimes": 141, + "fileLoca": "人力资源管理综合分析看板.zip", + "text": "

    1.附件中包含1个模板文件、1个数据库。

    \n

    2.本大屏场景要求使用 FR11.0 正式发布版本,且安装「FVS大屏编辑模式插件」,若下载后无法打开模板请更新插件至最新版本:FVS插件安装及配置要求

    \n

    3.模板使用控件联动组件,数据集中定义了数据集参数,需使用「人力资源管理.db」数据库中的表。需将「人力资源管理.db」数据库文件放至工程下 webapps\\webroot\\help 文件夹中。控件使用请参考文档:FVS控件组件

    \n

    4.在将「人力资源管理.db数据库」文件放至相应文件夹后,需在服务器>定义数据库连接中连接「人力资源管理.db」数据库。具体连接过程可参考附件中的使用说明,也可参考文档:JDBC连接数据库

    ", + "description": "作为企业级别的人事智能数据分析的解决方案,该模板通过大屏轻松掌握企业人才流量、人才结构、人才利润等一系列的数据,在管理人才和运营企业的过程中真正做到数据在手,胸有成竹。", + "updateTime": "2022-07-18T08:57:22.000Z", + "downloadTimes": 1212, "state": 1, - "designVersion": "10.0", - "involvePlugins": null, - "uploadTime": "2022-05-15T05:34:45.000Z", - "style": "简约清新", + "designVersion": "11.0", + "involvePlugins": "1162", + "uploadTime": "2022-07-18T08:57:22.000Z", + "style": "1", "link": "template", "sellerName": "finereport", - "pluginName": [], - "pkgsize": 1, + "pluginName": ["{id: 1162, name: fvs大屏编辑模式}"], + "pkgsize": 0, "material": null, - "tagsName": "IT互联网,财务" + "tagsName": " 医疗医药,人力资源" } ] \ No newline at end of file