@ -0,0 +1,202 @@
|
||||
package com.fr.design.mainframe.toast; |
||||
|
||||
import com.fr.concurrent.NamedThreadFactory; |
||||
import com.fr.design.dialog.UIDialog; |
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.design.layout.FRGUIPaneFactory; |
||||
import com.fr.module.ModuleContext; |
||||
|
||||
import javax.swing.BorderFactory; |
||||
import javax.swing.Icon; |
||||
import javax.swing.JPanel; |
||||
import javax.swing.SwingConstants; |
||||
import javax.swing.SwingUtilities; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.Dimension; |
||||
import java.awt.Point; |
||||
import java.awt.Window; |
||||
import java.util.concurrent.ScheduledExecutorService; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
/** |
||||
* |
||||
* alphafine - 下载模板时的toast弹窗 |
||||
* |
||||
* @author Link |
||||
* @version 10.0 |
||||
* Created by Link on 2022/9/22 |
||||
* */ |
||||
public class SimpleToast extends UIDialog { |
||||
private static final int MIN_HEIGHT = 36; |
||||
private static final String TOAST_MSG_TIMER = "TOAST_MSG_TIMER"; |
||||
private static final long DEFAULT_DISAPPEAR_DELAY = 5000; |
||||
private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.MILLISECONDS; |
||||
|
||||
|
||||
private ScheduledExecutorService timer; |
||||
private int hideHeight = 0; |
||||
private JPanel contentPane; |
||||
private boolean show = false; |
||||
private Window parent; |
||||
private boolean autoDisappear; |
||||
|
||||
public SimpleToast(Window parent, Icon icon, String text, boolean autoDisappear) { |
||||
super(parent); |
||||
this.parent = parent; |
||||
this.autoDisappear = autoDisappear; |
||||
JPanel panel = createContentPane(icon, text); |
||||
init(panel); |
||||
} |
||||
|
||||
private JPanel createContentPane(Icon icon, String text) { |
||||
JPanel pane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
|
||||
UILabel iconLabel = new UILabel(icon); |
||||
iconLabel.setVerticalAlignment(SwingConstants.TOP); |
||||
iconLabel.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0)); |
||||
|
||||
|
||||
UILabel textLabel = new UILabel(text); |
||||
pane.add(iconLabel, BorderLayout.WEST); |
||||
pane.add(textLabel, BorderLayout.CENTER); |
||||
pane.setBorder(BorderFactory.createEmptyBorder(8, 15, 8, 15)); |
||||
|
||||
return pane; |
||||
} |
||||
|
||||
|
||||
private void init(JPanel panel) { |
||||
setFocusable(false); |
||||
setAutoRequestFocus(false); |
||||
setUndecorated(true); |
||||
contentPane = panel; |
||||
initComponent(); |
||||
} |
||||
|
||||
private void initComponent() { |
||||
this.getContentPane().setLayout(null); |
||||
this.getContentPane().add(contentPane); |
||||
Dimension dimension = calculatePreferSize(); |
||||
hideHeight = dimension.height; |
||||
setSize(new Dimension(dimension.width, 0)); |
||||
contentPane.setSize(dimension); |
||||
setRelativeLocation(dimension); |
||||
if (autoDisappear) { |
||||
disappear(contentPane); |
||||
} |
||||
} |
||||
|
||||
private void setRelativeLocation(Dimension dimension) { |
||||
int positionX = parent.getLocationOnScreen().x + (parent.getWidth() - dimension.width) / 2; |
||||
int positionY = parent.getLocationOnScreen().y + 10; |
||||
this.setLocation(positionX, positionY); |
||||
} |
||||
|
||||
private Dimension calculatePreferSize() { |
||||
Dimension contentDimension = contentPane.getPreferredSize(); |
||||
int height = Math.max(MIN_HEIGHT, contentDimension.height); |
||||
return new Dimension(contentDimension.width, height); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 显示toast |
||||
* */ |
||||
public void display(JPanel outerPanel) { |
||||
show = true; |
||||
outerPanel.setLocation(0, -hideHeight); |
||||
ScheduledExecutorService tipToolTimer = createToastScheduleExecutorService(); |
||||
tipToolTimer.scheduleAtFixedRate(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
SwingUtilities.invokeLater(()->{ |
||||
displayStep(outerPanel, tipToolTimer); |
||||
}); |
||||
} |
||||
}, 0, 50, TimeUnit.MILLISECONDS); |
||||
|
||||
} |
||||
|
||||
void displayStep(JPanel outerPanel, ScheduledExecutorService timer) { |
||||
Point point = outerPanel.getLocation(); |
||||
if (point.y >= 0 && !timer.isShutdown()) { |
||||
timer.shutdown(); |
||||
} |
||||
int showDistance = 5 + point.y < 0 ? 5 : -point.y; |
||||
outerPanel.setLocation(point.x, point.y + showDistance); |
||||
Dimension dimension = SimpleToast.this.getSize(); |
||||
SimpleToast.this.setSize(new Dimension(dimension.width, dimension.height + showDistance)); |
||||
} |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private void disappear(JPanel outerPanel, long delay, TimeUnit timeUnit) { |
||||
timer = createToastScheduleExecutorService(); |
||||
timer.schedule(new DisappearMotion(outerPanel), delay, timeUnit); |
||||
} |
||||
|
||||
/** |
||||
* toast消失的动画效果 |
||||
* */ |
||||
class DisappearMotion implements Runnable { |
||||
JPanel panel; |
||||
|
||||
DisappearMotion(JPanel panel) { |
||||
this.panel = panel; |
||||
} |
||||
|
||||
@Override |
||||
public void run() { |
||||
ScheduledExecutorService tipToolTimer = createToastScheduleExecutorService(); |
||||
tipToolTimer.scheduleAtFixedRate(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
SwingUtilities.invokeLater(()->{ |
||||
disappearStep(tipToolTimer); |
||||
}); |
||||
} |
||||
}, 0, 50, TimeUnit.MILLISECONDS); |
||||
} |
||||
|
||||
void disappearStep(ScheduledExecutorService timer) { |
||||
Point point = panel.getLocation(); |
||||
if (point.y <= -hideHeight && !timer.isShutdown()) { |
||||
timer.shutdown(); |
||||
SimpleToast.this.setVisible(false); |
||||
SimpleToast.this.dispose(); |
||||
SimpleToast.this.show = false; |
||||
} |
||||
panel.setLocation(point.x, point.y - 5); |
||||
Dimension dimension = SimpleToast.this.getSize(); |
||||
SimpleToast.this.setSize(new Dimension(dimension.width, dimension.height - 5)); |
||||
} |
||||
} |
||||
|
||||
private void disappear(JPanel outerPanel) { |
||||
disappear(outerPanel, DEFAULT_DISAPPEAR_DELAY, DEFAULT_TIME_UNIT); |
||||
} |
||||
|
||||
private ScheduledExecutorService createToastScheduleExecutorService() { |
||||
return ModuleContext.getExecutor().newSingleThreadScheduledExecutor(new NamedThreadFactory(TOAST_MSG_TIMER)); |
||||
} |
||||
|
||||
@Override |
||||
public void checkValid() throws Exception { |
||||
} |
||||
|
||||
public void setVisible(boolean visible) { |
||||
super.setVisible(visible); |
||||
if (visible) { |
||||
display(contentPane); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void dispose() { |
||||
super.dispose(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,108 @@
|
||||
package com.fr.design.utils; |
||||
|
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.stable.StringUtils; |
||||
|
||||
import javax.swing.JComponent; |
||||
import java.awt.Color; |
||||
import java.awt.Font; |
||||
|
||||
/** |
||||
* |
||||
* 链接字符串工具类 |
||||
* |
||||
* @author Harrison |
||||
* @version 10.0 |
||||
* created by Harrison on 2022/05/24 |
||||
**/ |
||||
public class LinkStrUtils { |
||||
|
||||
public static final UILabel LABEL = new UILabel(); |
||||
|
||||
/** |
||||
* 创建链接label |
||||
* */ |
||||
public static UILabel generateLabel(String html, JComponent templateLabel) { |
||||
|
||||
String style = generateStyle(templateLabel.getBackground(), templateLabel.getFont(), templateLabel.getForeground()); |
||||
String fullHtml = generateHtmlTag(style, html); |
||||
return new UILabel(fullHtml); |
||||
} |
||||
|
||||
/** |
||||
* 创建链接字符串,html格式 |
||||
* */ |
||||
public static String generateHtmlTag(String html) { |
||||
|
||||
String defaultStyle = generateDefaultStyle(); |
||||
return generateHtmlTag(defaultStyle, html); |
||||
} |
||||
|
||||
/** |
||||
* 创建链接字符串,html格式 |
||||
* */ |
||||
public static String generateHtmlTag(String style, String html) { |
||||
|
||||
if (StringUtils.isEmpty(style)) { |
||||
throw new NullPointerException("style"); |
||||
} |
||||
if (StringUtils.isEmpty(html)) { |
||||
throw new NullPointerException("html"); |
||||
} |
||||
return "<html><body style=\"" + style + "\">" + html + "</body></html>"; |
||||
} |
||||
|
||||
/** |
||||
* 创建链接字符串,html格式 |
||||
* */ |
||||
public static String generateLinkTag(String link, String text) { |
||||
|
||||
return "<a href=\"" + link + "\">" + text + "</a>"; |
||||
} |
||||
|
||||
/** |
||||
* 创建链接字符串,无下划线,html格式 |
||||
* */ |
||||
public static String generateLinkTagWithoutUnderLine(String link, String text) { |
||||
return "<a style=\"text-decoration:none;\" href=\"" + link + "\">" + text + "</a>"; |
||||
} |
||||
|
||||
/** |
||||
* 创建链接字符串的html style |
||||
* */ |
||||
public static String generateStyle(Color backgroundColor, Font font, Color fontColor) { |
||||
|
||||
// 构建相同风格样式
|
||||
StringBuilder style = new StringBuilder("font-family:" + font.getFamily() + ";"); |
||||
|
||||
style.append("font-weight:").append(font.isBold() ? "bold" : "normal").append(";"); |
||||
style.append("font-size:").append(font.getSize()).append("pt;"); |
||||
style.append("color:rgb(").append(fontColor.getRed()).append(",").append(fontColor.getGreen()).append(",").append(fontColor.getBlue()).append(");"); |
||||
style.append("background-color: rgb(").append(backgroundColor.getRed()).append(",").append(backgroundColor.getGreen()).append(",").append(backgroundColor.getBlue()).append(");"); |
||||
|
||||
return style.toString(); |
||||
} |
||||
|
||||
/** |
||||
* 创建链接字符串的html style |
||||
* */ |
||||
public static String generateStyle(Font font, Color fontColor) { |
||||
|
||||
// 构建相同风格样式
|
||||
StringBuilder style = new StringBuilder("font-family:" + font.getFamily() + ";"); |
||||
|
||||
style.append("font-weight:").append(font.isBold() ? "bold" : "normal").append(";"); |
||||
style.append("font-size:").append(font.getSize()).append("pt;"); |
||||
style.append("color:rgb(").append(fontColor.getRed()).append(",").append(fontColor.getGreen()).append(",").append(fontColor.getBlue()).append(");"); |
||||
return style.toString(); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 创建默认style |
||||
* */ |
||||
public static String generateDefaultStyle() { |
||||
|
||||
return generateStyle(LABEL.getBackground(), LABEL.getFont(), LABEL.getForeground()); |
||||
} |
||||
} |
@ -0,0 +1,102 @@
|
||||
package com.fr.design.mainframe.alphafine.action; |
||||
|
||||
import com.fr.common.util.Strings; |
||||
import com.fr.design.mainframe.DesignerContext; |
||||
import com.fr.design.mainframe.alphafine.AlphaFineHelper; |
||||
import com.fr.design.mainframe.alphafine.download.FineMarketConstants; |
||||
import com.fr.design.mainframe.alphafine.download.FineMarketDownloadManager; |
||||
import com.fr.design.mainframe.alphafine.model.TemplateResourceDetail; |
||||
import com.fr.file.FileFILE; |
||||
import com.fr.log.FineLoggerFactory; |
||||
|
||||
import javax.swing.SwingWorker; |
||||
import java.awt.Desktop; |
||||
import java.awt.event.ActionEvent; |
||||
import java.awt.event.ActionListener; |
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* 点击后跳转至帆软市场下载对应模板资源 |
||||
* |
||||
* @author Link |
||||
* @version 11.0 |
||||
* Created by Link on 2022/9/22 |
||||
* |
||||
* TODO:可以参考mini组件商城的下载@ComponentsPackageInstallation#install |
||||
* */ |
||||
public class StartUseAction implements ActionListener { |
||||
|
||||
TemplateResourceDetail resourceDetail; |
||||
|
||||
public StartUseAction(TemplateResourceDetail detail) { |
||||
this.resourceDetail = detail; |
||||
} |
||||
|
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
new SwingWorker<String, Void>() { |
||||
|
||||
@Override |
||||
protected String doInBackground() throws Exception { |
||||
return FineMarketDownloadManager.getInstance().installResource(resourceDetail.getRoot(), AlphaFineHelper.getAlphaFineDialog()); |
||||
} |
||||
|
||||
@Override |
||||
protected void done() { |
||||
try { |
||||
open(get()); |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e, e.getMessage()); |
||||
} |
||||
super.done(); |
||||
} |
||||
}.execute(); |
||||
} |
||||
|
||||
/** |
||||
* 打开模板并打开文件目录 |
||||
* */ |
||||
void open(String fileName) throws IOException { |
||||
if (Strings.isEmpty(fileName)) { |
||||
return; |
||||
} |
||||
// 打开模板
|
||||
openInDesigner(fileName); |
||||
|
||||
// 打开系统文件夹
|
||||
File parentDir = new File(fileName).getParentFile(); |
||||
Desktop.getDesktop().open(parentDir); |
||||
} |
||||
|
||||
void openInDesigner(String fileName) { |
||||
File fileNeedOpen = new File(fileName); |
||||
if (fileName.endsWith(FineMarketConstants.ZIP)) { |
||||
File[] files = fileNeedOpen.getParentFile().listFiles(); |
||||
fileNeedOpen = getFirstCptOrFrm(files); |
||||
} else if (fileName.endsWith(FineMarketConstants.RAR)) { |
||||
// rar资源没有解压,所以不用打开模板
|
||||
return; |
||||
} |
||||
|
||||
// 打开模板
|
||||
if (fileNeedOpen == null) { |
||||
//有可能压缩包解压出来还是压缩包
|
||||
FineLoggerFactory.getLogger().error("AlphaFine open resource error: " + fileName); |
||||
} else { |
||||
DesignerContext.getDesignerFrame().openTemplate(new FileFILE(fileNeedOpen)); |
||||
} |
||||
} |
||||
|
||||
|
||||
private File getFirstCptOrFrm(File[] files) { |
||||
for (File f : files) { |
||||
if (f.getName().endsWith(FineMarketConstants.CPT) || f.getName().endsWith(FineMarketConstants.FRM)) { |
||||
return f; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
} |
@ -0,0 +1,82 @@
|
||||
package com.fr.design.mainframe.alphafine.component; |
||||
|
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.mainframe.alphafine.AlphaFineHelper; |
||||
import com.fr.design.mainframe.alphafine.model.TemplateResource; |
||||
|
||||
import javax.swing.BorderFactory; |
||||
import javax.swing.JLabel; |
||||
import javax.swing.JPanel; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.Color; |
||||
import java.awt.FlowLayout; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* |
||||
* alphafine - 推荐搜索面板 |
||||
* |
||||
* @author Link |
||||
* @version 10.0 |
||||
* Created by Link on 2022/9/22 |
||||
* */ |
||||
public class RecommendSearchPane extends TemplateResourcePanel { |
||||
|
||||
private static final Color BORDER_WHITE = new Color(0xe8e8e9); |
||||
private static final Color RECOMMEND_SEARCH_KEY_BLUE = new Color(0x419bf9); |
||||
|
||||
public RecommendSearchPane(TemplateResource templateResource) { |
||||
super(); |
||||
setTemplateResource(templateResource); |
||||
initComponent(); |
||||
this.setLayout(new BorderLayout()); |
||||
|
||||
this.setBorder(BorderFactory.createLineBorder(BORDER_WHITE, 1)); |
||||
this.add(getNorthPane(), BorderLayout.NORTH); |
||||
this.add(getCenterPane(), BorderLayout.CENTER); |
||||
} |
||||
|
||||
private void initComponent() { |
||||
createNorthPane(); |
||||
createCenterPane(); |
||||
} |
||||
|
||||
|
||||
private void createCenterPane() { |
||||
setCenterPane(new JPanel(new FlowLayout(FlowLayout.LEFT))); |
||||
JPanel centerPane = getCenterPane(); |
||||
centerPane.setBackground(Color.WHITE); |
||||
JLabel recommend = new JLabel(Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Resource_Recommend_For_You")); |
||||
centerPane.add(recommend); |
||||
|
||||
List<String> searchKeys = getTemplateResource().getRecommendSearchKey(); |
||||
|
||||
for (String key : searchKeys) { |
||||
JLabel keyLabel = new SearchKeyLabel(key); |
||||
centerPane.add(keyLabel); |
||||
} |
||||
} |
||||
|
||||
|
||||
class SearchKeyLabel extends JLabel { |
||||
String searchKey; |
||||
|
||||
SearchKeyLabel(String searchKey) { |
||||
this.searchKey = searchKey; |
||||
setText(searchKey); |
||||
setBackground(Color.WHITE); |
||||
setForeground(RECOMMEND_SEARCH_KEY_BLUE); |
||||
addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
AlphaFineHelper.getAlphaFineDialog().fireSearch(searchKey); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,90 @@
|
||||
package com.fr.design.mainframe.alphafine.component; |
||||
|
||||
import com.fr.base.GraphHelper; |
||||
import com.fr.design.mainframe.alphafine.model.TemplateResource; |
||||
import com.fr.design.utils.DesignUtils; |
||||
import com.fr.general.FRFont; |
||||
import com.fr.third.jodd.util.StringUtil; |
||||
|
||||
import javax.swing.JPanel; |
||||
import java.awt.AlphaComposite; |
||||
import java.awt.Color; |
||||
import java.awt.Dimension; |
||||
import java.awt.Font; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.Image; |
||||
import java.awt.RenderingHints; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* alphafine - 模板资源图片面板 |
||||
* |
||||
* @author Link |
||||
* @version 10.0 |
||||
* Created by Link on 2022/9/22 |
||||
* */ |
||||
public class TemplateResourceImagePanel extends JPanel { |
||||
|
||||
private static final int BACKGROUND_HEIGHT = 20; |
||||
|
||||
private static final Color BACKGROUND_COLOR = new Color(0x419BF9); |
||||
|
||||
private static final Font TAG_FONT = DesignUtils.getDefaultGUIFont().applySize(12); |
||||
|
||||
private static final Color COVER_COLOR = new Color(116, 181, 249, 26); |
||||
|
||||
private TemplateResource templateResource; |
||||
|
||||
private int width = 200; |
||||
private int height = 100; |
||||
|
||||
public TemplateResourceImagePanel(TemplateResource templateResource) { |
||||
this.templateResource = templateResource; |
||||
} |
||||
|
||||
public TemplateResourceImagePanel(TemplateResource templateResource, int width, int height) { |
||||
this(templateResource); |
||||
this.width = width; |
||||
this.height = height; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected void paintComponent(Graphics g) { |
||||
super.paintComponent(g); |
||||
Graphics2D g2 = (Graphics2D) g; |
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); |
||||
Color defaultColor = g2.getColor(); |
||||
|
||||
Image image = templateResource.getImage(); |
||||
if (image != null) { |
||||
g2.drawImage(templateResource.getImage(), 0, 0, getWidth(), getHeight(), this); |
||||
} else { |
||||
g2.setColor(COVER_COLOR); |
||||
g2.fillRect(0, 0, getWidth(), getHeight()); |
||||
} |
||||
|
||||
String tagName = templateResource.getType().getName(); |
||||
|
||||
if (!StringUtil.isEmpty(tagName)) { |
||||
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, .8f)); |
||||
g2.setColor(BACKGROUND_COLOR); |
||||
g2.fillRect(0, getHeight() - BACKGROUND_HEIGHT, getWidth(), BACKGROUND_HEIGHT); |
||||
g2.setColor(Color.WHITE); |
||||
int x = (getWidth() - GraphHelper.getWidth(tagName, g2.getFont())) / 2; |
||||
g2.setFont(TAG_FONT); |
||||
g2.drawString(tagName, x, getHeight() - 5); |
||||
} |
||||
g2.setColor(defaultColor); |
||||
} |
||||
|
||||
@Override |
||||
public Dimension getPreferredSize() { |
||||
return new Dimension(width, height); |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,231 @@
|
||||
package com.fr.design.mainframe.alphafine.component; |
||||
|
||||
import com.fr.base.svg.IconUtils; |
||||
import com.fr.design.gui.icontainer.UIScrollPane; |
||||
import com.fr.design.layout.TableLayoutHelper; |
||||
import com.fr.design.mainframe.alphafine.model.TemplateResource; |
||||
|
||||
import javax.swing.BorderFactory; |
||||
import javax.swing.JButton; |
||||
import javax.swing.JLabel; |
||||
import javax.swing.JPanel; |
||||
import javax.swing.JTextField; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.CardLayout; |
||||
import java.awt.Color; |
||||
import java.awt.Component; |
||||
import java.awt.Dimension; |
||||
import java.awt.FlowLayout; |
||||
import java.awt.Label; |
||||
import java.awt.event.KeyAdapter; |
||||
import java.awt.event.KeyEvent; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* |
||||
* alphafine - 模板资源最外层面板, 卡片布局,每个卡片里塞了scrollpanel |
||||
* |
||||
* @author Link |
||||
* @version 10.0 |
||||
* Created by Link on 2022/9/22 |
||||
* */ |
||||
public class TemplateResourcePageGridPane extends JPanel { |
||||
|
||||
private List<TemplateResource> data; |
||||
private CardLayout cardLayout; |
||||
private List<Page> pages; |
||||
private int totalPage; |
||||
|
||||
List<UIScrollPane> scrollPanes = new ArrayList<>(); |
||||
|
||||
private static final int PAGE_MAX_SIZE = 12; |
||||
private static final int TABLE_MAX_ROW_COUNT = 4; |
||||
private static final int TABLE_COL_COUNT = 3; |
||||
private static final int TABLE_VGAP = 15; |
||||
private static final int TABLE_HGAP = 15; |
||||
private static final int RESOURCE_WIDTH = 197; |
||||
private static final int RESOURCE_HEIGHT = 128; |
||||
|
||||
public TemplateResourcePageGridPane(List<TemplateResource> templateResourceList) { |
||||
this.data = templateResourceList; |
||||
totalPage = (int) Math.ceil((double)data.size() / PAGE_MAX_SIZE); |
||||
createPages(); |
||||
initComponents(); |
||||
this.setBackground(Color.WHITE); |
||||
this.setBorder(BorderFactory.createEmptyBorder(10, 20, 0, 20)); |
||||
switchPage(1); |
||||
} |
||||
|
||||
private void initComponents() { |
||||
cardLayout = new CardLayout(); |
||||
this.setLayout(cardLayout); |
||||
this.setBackground(Color.WHITE); |
||||
for (int i = 0; i < pages.size(); i++) { |
||||
UIScrollPane scrollPane = new UIScrollPane(pages.get(i)); |
||||
scrollPanes.add(scrollPane); |
||||
this.add(scrollPane, String.valueOf(i + 1)); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 构建分页,将资源切分到每一页,并在每一页尾部添加分页按钮 |
||||
* */ |
||||
private void createPages() { |
||||
int dataCnt = data.size(); |
||||
List<TemplateResource>[] slice = new ArrayList[totalPage]; |
||||
for (int i = 0; i < dataCnt; i++) { |
||||
int index = i / PAGE_MAX_SIZE; |
||||
if (slice[index] == null) { |
||||
slice[index] = new ArrayList<>(); |
||||
} |
||||
slice[index].add(data.get(i)); |
||||
} |
||||
pages = new ArrayList<>(); |
||||
for (int i = 0; i < totalPage; i++) { |
||||
pages.add(new Page(slice[i], i + 1)); |
||||
} |
||||
} |
||||
|
||||
|
||||
private void switchPage(int pageNumber) { |
||||
if (pageNumber < 1 || pageNumber > this.totalPage) { |
||||
return; |
||||
} |
||||
cardLayout.show(TemplateResourcePageGridPane.this, String.valueOf(pageNumber)); |
||||
scrollPanes.get(pageNumber - 1).getVerticalScrollBar().setValue(0); |
||||
// 坑,切换页面会刷新失败,需要手动滚动一下才能刷新
|
||||
scrollPanes.get(pageNumber - 1).getVerticalScrollBar().setValue(1); |
||||
scrollPanes.get(pageNumber - 1).getVerticalScrollBar().setValue(0); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 分页panel,borderlayout布局,north为信息页,south为分页按钮区 |
||||
* */ |
||||
private class Page extends JPanel { |
||||
List<TemplateResource> pageData; |
||||
Component[][] comps; |
||||
|
||||
JPanel contentPane; |
||||
|
||||
JPanel pageButtonPane; |
||||
JButton prev, next; |
||||
JTextField pageNumberField; |
||||
JLabel pageCnt; |
||||
|
||||
int pageNumber; |
||||
|
||||
Page(List<TemplateResource> pageData, int pageNumber) { |
||||
super(); |
||||
this.pageData = pageData; |
||||
this.pageNumber = pageNumber; |
||||
initComponents(); |
||||
this.setLayout(new BorderLayout()); |
||||
this.add(contentPane, BorderLayout.NORTH); |
||||
if (totalPage > 1) { |
||||
this.add(pageButtonPane, BorderLayout.SOUTH); |
||||
} |
||||
this.setBackground(Color.WHITE); |
||||
this.setBorder(BorderFactory.createEmptyBorder()); |
||||
} |
||||
|
||||
private void initComponents() { |
||||
createContentPane(); |
||||
createPageButtonPane(); |
||||
} |
||||
|
||||
void createContentPane() { |
||||
int dataCnt = pageData.size(); |
||||
int rowCnt = (int) Math.ceil((double)dataCnt / 3); |
||||
double[] rowHeight = new double[rowCnt]; |
||||
double[] colWidth = new double[TABLE_COL_COUNT]; |
||||
Arrays.fill(rowHeight, RESOURCE_HEIGHT); |
||||
Arrays.fill(colWidth, RESOURCE_WIDTH); |
||||
comps = new Component[rowCnt][TABLE_COL_COUNT]; |
||||
|
||||
for (int i = 0; i < rowCnt; i++) { |
||||
for (int j = 0; j < TABLE_COL_COUNT; j++) { |
||||
int which = i * 3 + j; |
||||
if (which >= dataCnt) { |
||||
Label empty = new Label(); |
||||
empty.setPreferredSize(new Dimension(RESOURCE_WIDTH, RESOURCE_HEIGHT)); |
||||
empty.setVisible(false); |
||||
comps[i][j] = empty; |
||||
} else { |
||||
TemplateResourcePanel resource = TemplateResourcePanel.create(pageData.get(which)); |
||||
resource.setPreferredSize(new Dimension(RESOURCE_WIDTH, RESOURCE_HEIGHT)); |
||||
comps[i][j] = resource; |
||||
} |
||||
} |
||||
} |
||||
contentPane = TableLayoutHelper.createGapTableLayoutPane(comps, rowHeight, colWidth, TABLE_HGAP, TABLE_VGAP); |
||||
contentPane.setBackground(Color.WHITE); |
||||
} |
||||
|
||||
void createPageButtonPane() { |
||||
prev = new JButton(IconUtils.readIcon("/com/fr/design/mainframe/alphafine/images/prev.svg")); |
||||
next = new JButton(IconUtils.readIcon("/com/fr/design/mainframe/alphafine/images/next.svg")); |
||||
pageNumberField = new JTextField((int) Math.log10(totalPage) + 1); |
||||
pageNumberField.setText(String.valueOf(this.pageNumber)); |
||||
pageCnt = new JLabel("/ " + totalPage); |
||||
|
||||
pageButtonPane = new JPanel(new FlowLayout(FlowLayout.RIGHT)); |
||||
pageButtonPane.add(prev); |
||||
pageButtonPane.add(pageNumberField); |
||||
pageButtonPane.add(pageCnt); |
||||
pageButtonPane.add(next); |
||||
|
||||
addPageAction(); |
||||
} |
||||
|
||||
// 添加翻页按钮事件
|
||||
void addPageAction() { |
||||
addPrevPageAction(); |
||||
addNextPageAction(); |
||||
addGotoPageAction(); |
||||
} |
||||
|
||||
void addPrevPageAction() { |
||||
prev.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
super.mouseClicked(e); |
||||
if (pageNumber > 1) { |
||||
switchPage(pageNumber - 1); |
||||
} |
||||
} |
||||
}); |
||||
}; |
||||
void addNextPageAction() { |
||||
next.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
super.mouseClicked(e); |
||||
if (pageNumber < totalPage) { |
||||
switchPage(pageNumber + 1); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
} |
||||
void addGotoPageAction() { |
||||
pageNumberField.addKeyListener(new KeyAdapter() { |
||||
@Override |
||||
public void keyPressed(KeyEvent e) { |
||||
super.keyPressed(e); |
||||
String numb = pageNumberField.getText(); |
||||
if (numb != null && !numb.equals(pageNumber)) { |
||||
if (e.getKeyCode() == KeyEvent.VK_ENTER) { |
||||
switchPage(Integer.parseInt(numb)); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,138 @@
|
||||
package com.fr.design.mainframe.alphafine.component; |
||||
|
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.mainframe.alphafine.model.TemplateResource; |
||||
import com.fr.design.mainframe.alphafine.preview.TemplateShopPane; |
||||
import com.fr.design.utils.BrowseUtils; |
||||
import com.fr.design.utils.DesignUtils; |
||||
import com.fr.log.FineLoggerFactory; |
||||
|
||||
import javax.swing.BorderFactory; |
||||
import javax.swing.JLabel; |
||||
import javax.swing.JPanel; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.Color; |
||||
import java.awt.Dimension; |
||||
import java.awt.Font; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
|
||||
/** |
||||
* |
||||
* alphafine - 模板资源面板 |
||||
* |
||||
* @author Link |
||||
* @version 10.0 |
||||
* Created by Link on 2022/9/22 |
||||
* */ |
||||
public class TemplateResourcePanel extends JPanel { |
||||
|
||||
private JPanel northPane; |
||||
private JPanel centerPane; |
||||
private TemplateResource templateResource; |
||||
|
||||
private static final Color PANEL_BORDER_COLOR = new Color(0xe8e8e9); |
||||
private static final Color DEMO_LABEL_FOREGROUND = new Color(0x419bf9); |
||||
private static final Font RESOURCE_NAME_FONT = DesignUtils.getDefaultGUIFont().applySize(12); |
||||
private static final Color RESOURCE_NAME_COLOR = new Color(0x5c5c5d); |
||||
|
||||
protected TemplateResourcePanel() { |
||||
|
||||
} |
||||
|
||||
protected TemplateResourcePanel(TemplateResource templateResource) { |
||||
this.templateResource = templateResource; |
||||
initComponent(); |
||||
this.setLayout(new BorderLayout()); |
||||
this.setBorder(BorderFactory.createLineBorder(PANEL_BORDER_COLOR, 1)); |
||||
this.add(northPane, BorderLayout.NORTH); |
||||
this.add(centerPane, BorderLayout.CENTER); |
||||
addAction(); |
||||
} |
||||
|
||||
private void addAction() { |
||||
this.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
super.mouseClicked(e); |
||||
TemplateShopPane.getInstance().searchAndShowDetailPane(templateResource); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* 通过数据构造面板 |
||||
* */ |
||||
public static TemplateResourcePanel create(TemplateResource templateResource) { |
||||
if (TemplateResource.Type.RECOMMEND_SEARCH.equals(templateResource.getType())) { |
||||
return new RecommendSearchPane(templateResource); |
||||
} else { |
||||
return new TemplateResourcePanel(templateResource); |
||||
} |
||||
} |
||||
|
||||
public JPanel getNorthPane() { |
||||
return northPane; |
||||
} |
||||
public JPanel getCenterPane() { |
||||
return centerPane; |
||||
} |
||||
public TemplateResource getTemplateResource() { |
||||
return templateResource; |
||||
} |
||||
|
||||
public void setNorthPane(JPanel northPane) { |
||||
this.northPane = northPane; |
||||
} |
||||
|
||||
public void setCenterPane(JPanel centerPane) { |
||||
this.centerPane = centerPane; |
||||
} |
||||
|
||||
public void setTemplateResource(TemplateResource templateResource) { |
||||
this.templateResource = templateResource; |
||||
} |
||||
|
||||
private void initComponent() { |
||||
createNorthPane(); |
||||
createCenterPane(); |
||||
} |
||||
|
||||
protected void createNorthPane() { |
||||
northPane = new TemplateResourceImagePanel(templateResource); |
||||
} |
||||
|
||||
private void createCenterPane() { |
||||
JLabel nameLabel = new JLabel(templateResource.getName()); |
||||
nameLabel.setFont(RESOURCE_NAME_FONT); |
||||
nameLabel.setForeground(RESOURCE_NAME_COLOR); |
||||
nameLabel.setBackground(Color.WHITE); |
||||
nameLabel.setBorder(BorderFactory.createEmptyBorder()); |
||||
|
||||
JLabel demoLabel = new JLabel(); |
||||
if (templateResource.hasDemoUrl()) { |
||||
demoLabel.setText(Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Resource_Demo")); |
||||
demoLabel.setForeground(DEMO_LABEL_FOREGROUND); |
||||
demoLabel.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
try { |
||||
BrowseUtils.browser(templateResource.getDemoUrl()); |
||||
} catch (Exception ex) { |
||||
FineLoggerFactory.getLogger().error(ex, ex.getMessage()); |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
|
||||
centerPane = new JPanel(new BorderLayout()); |
||||
centerPane.setBackground(Color.WHITE); |
||||
centerPane.add(nameLabel, BorderLayout.WEST); |
||||
centerPane.add(demoLabel, BorderLayout.EAST); |
||||
} |
||||
|
||||
@Override |
||||
public Dimension getPreferredSize() { |
||||
return new Dimension(180, 90); |
||||
} |
||||
} |
@ -0,0 +1,19 @@
|
||||
package com.fr.design.mainframe.alphafine.download; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* alphafine - 帆软市场常量 |
||||
* |
||||
* @author Link |
||||
* @version 10.0 |
||||
* Created by Link on 2022/9/22 |
||||
* */ |
||||
public class FineMarketConstants { |
||||
|
||||
public static final String REPORTLETS = "/reportlets"; |
||||
public static final String ZIP = ".zip"; |
||||
public static final String RAR = ".rar"; |
||||
public static final String CPT = ".cpt"; |
||||
public static final String FRM = ".frm"; |
||||
} |
@ -0,0 +1,159 @@
|
||||
package com.fr.design.mainframe.alphafine.download; |
||||
|
||||
import com.fr.base.svg.IconUtils; |
||||
import com.fr.common.util.Strings; |
||||
import com.fr.design.DesignerEnvManager; |
||||
import com.fr.design.extra.Process; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.login.DesignerLoginHelper; |
||||
import com.fr.design.login.DesignerLoginSource; |
||||
import com.fr.design.mainframe.alphafine.AlphaFineHelper; |
||||
import com.fr.design.mainframe.alphafine.model.TemplateResource; |
||||
import com.fr.design.mainframe.alphafine.search.helper.FineMarketClientHelper; |
||||
import com.fr.design.mainframe.toast.SimpleToast; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.third.jodd.io.ZipUtil; |
||||
|
||||
import javax.swing.SwingUtilities; |
||||
import javax.swing.filechooser.FileSystemView; |
||||
import java.awt.Window; |
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.util.HashMap; |
||||
|
||||
|
||||
/** |
||||
* 在这里统一管理帆软市场的下载 |
||||
* 下载的流程控制尽量都在这个类内部完成 |
||||
* 通过Process类来实现下载流程控制 |
||||
* |
||||
* @author Link |
||||
* @version 10.0 |
||||
* Created by Link on 2022/9/22 |
||||
* */ |
||||
public class FineMarketDownloadManager { |
||||
|
||||
private static final FineMarketDownloadManager INSTANCE = new FineMarketDownloadManager(); |
||||
public static final FineMarketDownloadManager getInstance() { |
||||
return INSTANCE; |
||||
} |
||||
|
||||
|
||||
public static final double PROCESS_SUCCESS = 1d; |
||||
public static final double PROCESS_FAILED = -1d; |
||||
public static final double OPENING_FILE = 2d; |
||||
|
||||
private static final String OPENING_PLEASE_WAIT = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Resource_Opening"); |
||||
private static final String DOWNLOAD_FAILED = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Resource_Download_Failed_Check_Network"); |
||||
|
||||
|
||||
/** |
||||
* 下载资源并解压 |
||||
* */ |
||||
public String installResource(TemplateResource resource, Window parentWindow){ |
||||
// 验证登录
|
||||
String token = DesignerEnvManager.getEnvManager().getDesignerLoginRefreshToken(); |
||||
if (Strings.isEmpty(token)) { |
||||
DesignerLoginHelper.showLoginDialog(DesignerLoginSource.NORMAL, new HashMap<>(), AlphaFineHelper.getAlphaFineDialog()); |
||||
return null; |
||||
} |
||||
return install(resource, parentWindow); |
||||
} |
||||
|
||||
private String install(TemplateResource resource, Window parentWindow) { |
||||
// 默认下载到桌面
|
||||
String workDir = FileSystemView.getFileSystemView().getHomeDirectory().getPath(); |
||||
File destDir = new File(workDir); |
||||
|
||||
DownloadProcess downloadProcess = new DownloadProcess(parentWindow); |
||||
String fileName = null; |
||||
try { |
||||
fileName = FineMarketClientHelper.getInstance().download(resource, destDir, downloadProcess); |
||||
unzip(fileName, downloadProcess); |
||||
return fileName; |
||||
} catch (Exception e) { |
||||
downloadProcess.process(FineMarketDownloadManager.PROCESS_FAILED); |
||||
FineLoggerFactory.getLogger().error(e, e.getMessage()); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
void unzip(String fileName, DownloadProcess process) throws IOException { |
||||
process.process(OPENING_FILE); |
||||
|
||||
if (fileName.endsWith(FineMarketConstants.ZIP)) { |
||||
File file = new File(fileName); |
||||
File parentDir = file.getParentFile(); |
||||
ZipUtil.unzip(file, parentDir); |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
|
||||
/** |
||||
* 下载流程控制,主要控制ui的显示 |
||||
* */ |
||||
class DownloadProcess implements Process<Double> { |
||||
|
||||
SimpleToast downloadingToast; |
||||
SimpleToast openingToast; |
||||
SimpleToast failedToast; |
||||
Window parent; |
||||
|
||||
public DownloadProcess(Window parentWindow) { |
||||
this.parent = parentWindow; |
||||
init(); |
||||
} |
||||
|
||||
void init() { |
||||
showLoadingToast(); |
||||
} |
||||
|
||||
@Override |
||||
public void process(Double aDouble) { |
||||
SwingUtilities.invokeLater(()->{ |
||||
if (aDouble == PROCESS_FAILED) { |
||||
downloadFailed(); |
||||
} else if (aDouble == PROCESS_SUCCESS) { |
||||
downloadSuccess(); |
||||
} else if (aDouble == OPENING_FILE) { |
||||
openingFile(); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* 下载失败 |
||||
*/ |
||||
public void downloadFailed() { |
||||
downloadingToast.setVisible(false); |
||||
showFailedToast(); |
||||
} |
||||
|
||||
/** |
||||
* 下载成功 |
||||
*/ |
||||
public void downloadSuccess() { |
||||
downloadingToast.setVisible(false); |
||||
} |
||||
|
||||
|
||||
private void openingFile() { |
||||
downloadingToast.setVisible(false); |
||||
openingToast = new SimpleToast(AlphaFineHelper.getAlphaFineDialog(), IconUtils.readIcon("/com/fr/design/mainframe/alphafine/images/loading.svg"), OPENING_PLEASE_WAIT, true); |
||||
openingToast.setVisible(true); |
||||
} |
||||
|
||||
private void showLoadingToast() { |
||||
downloadingToast = new SimpleToast(AlphaFineHelper.getAlphaFineDialog(), IconUtils.readIcon("/com/fr/design/mainframe/alphafine/images/loading.svg"), OPENING_PLEASE_WAIT, false); |
||||
downloadingToast.setVisible(true); |
||||
} |
||||
|
||||
private void showFailedToast() { |
||||
failedToast = new SimpleToast(AlphaFineHelper.getAlphaFineDialog(), IconUtils.readIcon("/com/fr/design/mainframe/alphafine/images/caution.svg"), DOWNLOAD_FAILED, true); |
||||
failedToast.setVisible(true); |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,238 @@
|
||||
package com.fr.design.mainframe.alphafine.model; |
||||
|
||||
import com.fr.common.util.Strings; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.mainframe.alphafine.download.FineMarketConstants; |
||||
import com.fr.design.mainframe.alphafine.search.manager.impl.TemplateResourceSearchManager; |
||||
import com.fr.general.IOUtils; |
||||
import com.fr.json.JSONArray; |
||||
import com.fr.json.JSONObject; |
||||
import com.fr.third.jodd.util.StringUtil; |
||||
|
||||
import java.awt.Image; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* alphafine - 模板资源数据 |
||||
* |
||||
* @author Link |
||||
* @version 10.0 |
||||
* Created by Link on 2022/9/22 |
||||
* */ |
||||
public class TemplateResource { |
||||
|
||||
/*** |
||||
* 模板资源类型:模板,解决方案,推荐搜索 |
||||
*/ |
||||
public enum Type { |
||||
// 单个模板
|
||||
SINGLE_TEMPLATE(Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Resource_Single_Template")), |
||||
// 场景解决方案
|
||||
SCENARIO_SOLUTION(Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Resource_Scenario_Solution")), |
||||
// 推荐搜索
|
||||
RECOMMEND_SEARCH; |
||||
|
||||
private String name; |
||||
|
||||
Type() { |
||||
} |
||||
|
||||
Type(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public String getName() { |
||||
return name; |
||||
} |
||||
} |
||||
|
||||
// 模板资源搜索接口返回值字段名
|
||||
public static final String ID = "id"; |
||||
public static final String UUID = "uuid"; |
||||
public static final String NAME = "name"; |
||||
public static final String IMAGE_URL = "pic"; |
||||
public static final String DEMO_URL = "demoUrl"; |
||||
public static final String PKG_SIZE = "pkgsize"; |
||||
public static final String FILE_NAME = "fileLoca"; |
||||
private static final String RECOMMEND_SEARCH_IMG_URL = "com/fr/design/mainframe/alphafine/images/more.png"; |
||||
|
||||
|
||||
// 模板资源属性
|
||||
private String id; |
||||
private String uuid; |
||||
private Type type; |
||||
private String imageUrl; |
||||
private Image image; |
||||
private String name; |
||||
private String demoUrl; |
||||
private String fileName; |
||||
private int pkgSize; |
||||
private List<String> recommendSearchKey; |
||||
private boolean embed; |
||||
|
||||
/** |
||||
* json转obj |
||||
* */ |
||||
public static List<TemplateResource> createByJson(JSONArray jsonArray) { |
||||
List<TemplateResource> list = new ArrayList<>(); |
||||
if (jsonArray != null) { |
||||
for (int i = jsonArray.length() - 1; i >= 0; i--) { |
||||
list.add(createByJson(jsonArray.getJSONObject(i))); |
||||
} |
||||
} |
||||
return list; |
||||
} |
||||
|
||||
/** |
||||
* json转obj |
||||
* */ |
||||
public static TemplateResource createByJson(JSONObject jsonObject) { |
||||
|
||||
TemplateResource templateResource = new TemplateResource().setId(jsonObject.getString(ID)).setUuid(jsonObject.getString(UUID)).setName(jsonObject.getString(NAME)) |
||||
.setDemoUrl(jsonObject.getString(DEMO_URL)).setPkgSize(jsonObject.getInt(PKG_SIZE)).setFileName(jsonObject.getString(FILE_NAME)); |
||||
int pkgSize = templateResource.getPkgSize(); |
||||
if (pkgSize == 0) { |
||||
templateResource.type = Type.SINGLE_TEMPLATE; |
||||
} else { |
||||
templateResource.type = Type.SCENARIO_SOLUTION; |
||||
} |
||||
|
||||
templateResource.setImageUrl(parseUrl(jsonObject)); |
||||
|
||||
templateResource.setImage(IOUtils.readImage(templateResource.imageUrl)); |
||||
return templateResource; |
||||
} |
||||
|
||||
/** |
||||
* 商城接口传过来的图片url是特殊格式,需要特殊处理下 |
||||
* */ |
||||
static String parseUrl(JSONObject jsonObject) { |
||||
String imgUrl = jsonObject.getString(IMAGE_URL); |
||||
int index = imgUrl.indexOf(","); |
||||
if (index != -1) { |
||||
imgUrl = imgUrl.substring(0, imgUrl.indexOf(",")); |
||||
} |
||||
return imgUrl; |
||||
} |
||||
|
||||
public static TemplateResource getRecommendSearch() { |
||||
TemplateResource recommend = new TemplateResource(); |
||||
recommend.setType(Type.RECOMMEND_SEARCH); |
||||
recommend.setImageUrl(RECOMMEND_SEARCH_IMG_URL); |
||||
recommend.setImage(IOUtils.readImage(RECOMMEND_SEARCH_IMG_URL)); |
||||
recommend.setRecommendSearchKey(TemplateResourceSearchManager.getInstance().getRecommendSearchKeys()); |
||||
return recommend; |
||||
} |
||||
|
||||
|
||||
|
||||
public String getFileName() { |
||||
return fileName; |
||||
} |
||||
|
||||
public TemplateResource setFileName(String fileName) { |
||||
if (Strings.isEmpty(fileName)) { |
||||
this.fileName = getName() + FineMarketConstants.ZIP; |
||||
} else { |
||||
this.fileName = fileName; |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
public Type getType() { |
||||
return type; |
||||
} |
||||
|
||||
public void setType(Type type) { |
||||
this.type = type; |
||||
} |
||||
|
||||
public List<String> getRecommendSearchKey() { |
||||
return recommendSearchKey; |
||||
} |
||||
|
||||
public void setRecommendSearchKey(List<String> recommendSearchKey) { |
||||
this.recommendSearchKey = recommendSearchKey; |
||||
} |
||||
|
||||
public TemplateResource setImage(Image image) { |
||||
this.image = image; |
||||
return this; |
||||
} |
||||
|
||||
public Image getImage() { |
||||
return image; |
||||
} |
||||
|
||||
public TemplateResource setImageUrl(String imageUrl) { |
||||
this.imageUrl = imageUrl; |
||||
return this; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 判断是否为内置模板资源 |
||||
* */ |
||||
public boolean isEmbed() { |
||||
return embed; |
||||
} |
||||
|
||||
public void setEmbed(boolean embed) { |
||||
this.embed = embed; |
||||
} |
||||
|
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
public TemplateResource setName(String name) { |
||||
this.name = name; |
||||
return this; |
||||
} |
||||
|
||||
public String getDemoUrl() { |
||||
return demoUrl; |
||||
} |
||||
|
||||
/** |
||||
* 有无在线演示 |
||||
* */ |
||||
public boolean hasDemoUrl() { |
||||
return !StringUtil.isEmpty(demoUrl); |
||||
} |
||||
|
||||
public TemplateResource setDemoUrl(String demoUrl) { |
||||
this.demoUrl = demoUrl; |
||||
return this; |
||||
} |
||||
|
||||
public int getPkgSize() { |
||||
return pkgSize; |
||||
} |
||||
|
||||
public TemplateResource setPkgSize(int pkgSize) { |
||||
this.pkgSize = pkgSize; |
||||
return this; |
||||
} |
||||
|
||||
public String getId() { |
||||
return id; |
||||
} |
||||
|
||||
public TemplateResource setId(String id) { |
||||
this.id = id; |
||||
return this; |
||||
} |
||||
|
||||
public String getUuid() { |
||||
return uuid; |
||||
} |
||||
|
||||
public TemplateResource setUuid(String uuid) { |
||||
this.uuid = uuid; |
||||
return this; |
||||
} |
||||
} |
@ -0,0 +1,251 @@
|
||||
package com.fr.design.mainframe.alphafine.model; |
||||
|
||||
|
||||
import com.fr.design.mainframe.alphafine.search.helper.FineMarketClientHelper; |
||||
import com.fr.design.mainframe.alphafine.search.manager.impl.TemplateResourceSearchManager; |
||||
import com.fr.json.JSONArray; |
||||
import com.fr.json.JSONObject; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.stable.StringUtils; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.regex.Matcher; |
||||
import java.util.regex.Pattern; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* alphafine - 模板资源详细数据 |
||||
* |
||||
* @author Link |
||||
* @version 10.0 |
||||
* Created by Link on 2022/9/22 |
||||
* */ |
||||
public class TemplateResourceDetail { |
||||
|
||||
// 与对应的模板资源关联
|
||||
private final TemplateResource root; |
||||
private String info; |
||||
private String vendor; |
||||
private String htmlText; |
||||
private List<String> detailInfo; |
||||
private String[] tagsId; |
||||
private List<String> tagsName; |
||||
private double price; |
||||
private String parentPkgName = ""; |
||||
private String parentPkgUrl; |
||||
private String resourceUrl; |
||||
|
||||
public static final String ID = "id"; |
||||
public static final String INFO = "description"; |
||||
public static final String VENDOR = "vendor"; |
||||
public static final String DETAIL_INFO = "text"; |
||||
public static final String TAGS_ID = "cid"; |
||||
public static final String PRICE = "price"; |
||||
public static final String NAME = "name"; |
||||
public static final String PARENT_NAME = "parentName"; |
||||
public static final String PARENT_URL = "parentUrl"; |
||||
public static final String TAGS_NAME = "tagsName"; |
||||
public static final String URL = "url"; |
||||
|
||||
|
||||
|
||||
public TemplateResourceDetail(TemplateResource resource) { |
||||
this.root = resource; |
||||
} |
||||
|
||||
|
||||
public String getVendor() { |
||||
return vendor; |
||||
} |
||||
|
||||
public void setVendor(String vendor) { |
||||
this.vendor = vendor; |
||||
} |
||||
|
||||
public TemplateResource getRoot() { |
||||
return root; |
||||
} |
||||
|
||||
public String getInfo() { |
||||
return info; |
||||
} |
||||
|
||||
public void setInfo(String info) { |
||||
this.info = info; |
||||
} |
||||
|
||||
public List<String> getDetailInfo() { |
||||
return detailInfo; |
||||
} |
||||
|
||||
public void setDetailInfo(List<String> detailInfo) { |
||||
this.detailInfo = detailInfo; |
||||
} |
||||
|
||||
public String[] getTagsId() { |
||||
return tagsId; |
||||
} |
||||
|
||||
public void setTagsId(String[] tagsId) { |
||||
this.tagsId = tagsId; |
||||
} |
||||
|
||||
public List<String> getTagsName() { |
||||
return tagsName; |
||||
} |
||||
|
||||
public String getTagsString() { |
||||
StringBuilder sb = new StringBuilder(); |
||||
if (tagsName != null) { |
||||
for (String tag : tagsName) { |
||||
sb.append(tag + " "); |
||||
} |
||||
} |
||||
return sb.toString(); |
||||
} |
||||
|
||||
public void setTagsName(List<String> tagsName) { |
||||
this.tagsName = tagsName; |
||||
} |
||||
|
||||
public double getPrice() { |
||||
return price; |
||||
} |
||||
|
||||
public void setPrice(double price) { |
||||
this.price = price; |
||||
} |
||||
|
||||
public String getParentPkgName() { |
||||
return parentPkgName; |
||||
} |
||||
|
||||
public void setParentPkgName(String parentPkgName) { |
||||
if (StringUtils.isEmpty(parentPkgName)) { |
||||
this.parentPkgName = ""; |
||||
} else { |
||||
this.parentPkgName = parentPkgName; |
||||
} |
||||
} |
||||
|
||||
public String getResourceUrl() { |
||||
return resourceUrl; |
||||
} |
||||
|
||||
public void setResourceUrl(String resourceUrl) { |
||||
this.resourceUrl = resourceUrl; |
||||
} |
||||
|
||||
public String getHtmlText() { |
||||
return htmlText; |
||||
} |
||||
|
||||
public void setHtmlText(String htmlText) { |
||||
this.htmlText = htmlText; |
||||
} |
||||
|
||||
public String getParentPkgUrl() { |
||||
return parentPkgUrl; |
||||
} |
||||
|
||||
public void setParentPkgUrl(String parentPkgUrl) { |
||||
this.parentPkgUrl = parentPkgUrl; |
||||
} |
||||
|
||||
/** |
||||
* 通过模板资源数据构建详细数据 |
||||
* */ |
||||
public static TemplateResourceDetail createByTemplateResource(TemplateResource root) { |
||||
return Builder.buildByResource(root); |
||||
} |
||||
|
||||
/** |
||||
* 通过内置模板资源数据构建详细数据 |
||||
* */ |
||||
public static TemplateResourceDetail createFromEmbedResource(TemplateResource root) { |
||||
return Builder.buildFromEmbedResource(root); |
||||
} |
||||
|
||||
static class Builder { |
||||
|
||||
static FineMarketClientHelper helper = FineMarketClientHelper.getInstance(); |
||||
|
||||
static TemplateResourceDetail buildFromEmbedResource(TemplateResource templateResource) { |
||||
TemplateResourceDetail detail = new TemplateResourceDetail(templateResource); |
||||
String resourceId = templateResource.getId(); |
||||
JSONArray embedResources = TemplateResourceSearchManager.getInstance().getEmbedResourceJSONArray(); |
||||
for (int i = 0; i < embedResources.length(); i++) { |
||||
JSONObject resource = embedResources.getJSONObject(i); |
||||
if (resourceId.equals(resource.getString(ID))) { |
||||
detail.setInfo(resource.getString(INFO)); |
||||
detail.setHtmlText(resource.getString(DETAIL_INFO)); |
||||
detail.setVendor(resource.getString(VENDOR)); |
||||
detail.setPrice(resource.getDouble(PRICE)); |
||||
detail.setResourceUrl(resource.getString(URL)); |
||||
detail.setParentPkgName(resource.getString(PARENT_NAME)); |
||||
detail.setParentPkgUrl(resource.getString(PARENT_URL)); |
||||
detail.setTagsName(Arrays.asList(resource.getString(TAGS_NAME).split(","))); |
||||
break; |
||||
} |
||||
} |
||||
return detail; |
||||
} |
||||
|
||||
static TemplateResourceDetail buildByResource(TemplateResource templateResource) { |
||||
TemplateResourceDetail detail = new TemplateResourceDetail(templateResource); |
||||
String resourceId = templateResource.getId(); |
||||
|
||||
// 获取模板详情页的信息一共需要三次请求
|
||||
try { |
||||
// 1请求详细信息
|
||||
JSONObject info = helper.getTemplateInfoById(resourceId); |
||||
detail.setInfo(info.getString(INFO)); |
||||
detail.setHtmlText(info.getString(DETAIL_INFO)); |
||||
detail.setVendor(info.getString(VENDOR)); |
||||
detail.setTagsId(info.getString(TAGS_ID).split(",")); |
||||
detail.setPrice(info.getDouble(PRICE)); |
||||
detail.setResourceUrl(helper.getTemplateUrlById(templateResource.getId())); |
||||
|
||||
// 2请求所属模板包信息
|
||||
JSONObject parentPkginfo = helper.getTemplateParentPackageByTemplateId(resourceId); |
||||
if (parentPkginfo != null) { |
||||
detail.setParentPkgName(parentPkginfo.getString(NAME)); |
||||
detail.setParentPkgUrl(FineMarketClientHelper.getInstance().getTemplateUrlById(parentPkginfo.getString(ID))); |
||||
} |
||||
|
||||
// 3请求标签信息
|
||||
detail.setTagsName(helper.getTemplateTagsByTemplateTagIds(detail.getTagsId())); |
||||
return detail; |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e, e.getMessage()); |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* 这里做下数据转换 |
||||
* 原始数据是html标签写的,如下 |
||||
* "<ol style="list-style-type: decimal;" class=" list-paddingleft-2"><li><p>该模板需用10.0及以上版本设计器预览<br/></p></li><li><p>该模板为库存场景解决方案的部分内容,全部内容可下载<a href="https://market.fanruan.com/template/20000733" target="_self">库存场景解决方案</a>查看</p></li><li><p>为保障模板预览效果,建议安装<a href="https://help.fanruan.com/finereport10.0/doc-view-3665.html" target="_self">新自适应插件</a>(FR11.0版本插件已内置,无需手动安装),有使用需求或疑问,请联系帆软技术支持咨询<br/></p></li></ol>",
|
||||
* |
||||
* 转换的后的数据 是原始数据中所有<p></p>标签内的(包括标签)的字符串(List<String>),如上字符串会转为如下 |
||||
* List [<p>该模板需用10.0及以上版本设计器预览<br/></p>, |
||||
* <p>该模板为库存场景解决方案的部分内容,全部内容可下载<a href="https://market.fanruan.com/template/20000733" target="_self">库存场景解决方案</a>查看</p>, |
||||
* <p>为保障模板预览效果,建议安装<a href="https://help.fanruan.com/finereport10.0/doc-view-3665.html" target="_self">新自适应插件</a>(FR11.0版本插件已内置,无需手动安装),有使用需求或疑问,请联系帆软技术支持咨询<br/></p> |
||||
* ] |
||||
* */ |
||||
static final Pattern HTML_PATTERN = Pattern.compile("<p>(.+?)</p>"); |
||||
static List<String> parseDetailInfo(String htmlDetailInfo) { |
||||
List<String> infos = new ArrayList<>(); |
||||
Matcher matcher = HTML_PATTERN.matcher(htmlDetailInfo); |
||||
while (matcher.find()) { |
||||
infos.add(matcher.group()); |
||||
} |
||||
return infos; |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,241 @@
|
||||
package com.fr.design.mainframe.alphafine.preview; |
||||
|
||||
import com.fr.design.constants.UIConstants; |
||||
import com.fr.design.dialog.link.MessageWithLink; |
||||
import com.fr.design.gui.icontainer.UIScrollPane; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.mainframe.alphafine.AlphaFineConstants; |
||||
import com.fr.design.mainframe.alphafine.action.StartUseAction; |
||||
import com.fr.design.mainframe.alphafine.component.TemplateResourceImagePanel; |
||||
import com.fr.design.mainframe.alphafine.model.TemplateResourceDetail; |
||||
import com.fr.design.utils.BrowseUtils; |
||||
import com.fr.design.utils.DesignUtils; |
||||
import com.fr.design.utils.LinkStrUtils; |
||||
import com.fr.stable.StringUtils; |
||||
|
||||
import javax.swing.BorderFactory; |
||||
import javax.swing.JButton; |
||||
import javax.swing.JLabel; |
||||
import javax.swing.JPanel; |
||||
import javax.swing.SwingUtilities; |
||||
import java.awt.Color; |
||||
import java.awt.Dimension; |
||||
import java.awt.FlowLayout; |
||||
import java.awt.Font; |
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.RenderingHints; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
|
||||
/** |
||||
* |
||||
* alphafine - 模板资源详情页 |
||||
* |
||||
* @author Link |
||||
* @version 10.0 |
||||
* Created by Link on 2022/9/22 |
||||
* */ |
||||
public class TemplateResourceDetailPane extends JPanel { |
||||
|
||||
|
||||
private TemplateResourceDetail data; |
||||
|
||||
private TemplateResourceImagePanel imagePane; |
||||
private JPanel contentPane; |
||||
private UIScrollPane infoScrollPane; |
||||
private JPanel operatePane; |
||||
private UIScrollPane detailInfoPane; |
||||
|
||||
|
||||
private static final int IMAGE_HEIGHT = 170; |
||||
private static final int IMAGE_WIDTH = 310; |
||||
private static final int SCROLL_PANE_WIDTH = 315; |
||||
private static final int SCROLL_PANE_HEIGHT = 135; |
||||
private static final int CONTENT_PANE_WIDTH = 320; |
||||
private static final int CONTENT_PANE_HEIGHT = 180; |
||||
private static final int DETAIL_PANE_HEIGHT = 95; |
||||
private static final int TEXT_SCROLL_PANE_HEIGHT = 500; |
||||
private static final int PANE_WIDTH = 635; |
||||
private static final int BUTTON_WIDTH = 68; |
||||
private static final int BUTTON_HEIGHT = 20; |
||||
|
||||
private static final String GOTO_DETAIL = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Detail_GOTO_DETAIL"); |
||||
private static final String START_USE = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Detail_START_USE"); |
||||
private static final String VENDOR = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Detail_Vendor"); |
||||
private static final String TAGS = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Detail_Tags"); |
||||
private static final String PARENT_PACKAGE = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Detail_Parent_Package"); |
||||
private static final String DETAIL_INFO = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Detail_Info"); |
||||
private static final String FREE = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Detail_Price_Free"); |
||||
private static final String SPACE = " "; |
||||
private static final String LF = "<br>"; |
||||
private static final String RMB = "¥"; |
||||
|
||||
|
||||
private static final Color INFO_PANE_BACKGROUND = new Color(0xf9f9f9); |
||||
private static final Color INFO_PANE_FOREGROUND = new Color(0x5b5b5c); |
||||
private static final Color MORE_INFO_LINK = new Color(0x419bf9); |
||||
|
||||
private static final Font HTML_FONT = DesignUtils.getDefaultGUIFont().applySize(12); |
||||
private static final Color HTML_COLOR = new Color(0x5c5c5d); |
||||
private static final String HTML_FORMAT = "<html><style>a {color: #419BF9;text-decoration:none;}</style><body style=\"line-height: 20px;"+ LinkStrUtils.generateStyle(HTML_FONT, HTML_COLOR) +"\">%s</body></html>"; |
||||
private static final String DETAIL_INFO_HTML_FORMAT = "<html><style>a {color: #419BF9;text-decoration:none;}</style><body style=\"line-height: 20px;" + LinkStrUtils.generateStyle(HTML_FONT, HTML_COLOR) + "\"><p>" + DETAIL_INFO + "</p>%s</body></html>"; |
||||
private static final String HTML_P_TAG_FORMAT = "<p style=\"margin-top:5pt;\">%s</p>"; |
||||
|
||||
|
||||
|
||||
public TemplateResourceDetailPane(TemplateResourceDetail detail) { |
||||
this.data = detail; |
||||
initComponent(); |
||||
this.setLayout(new FlowLayout(FlowLayout.LEFT)); |
||||
this.setBorder(BorderFactory.createEmptyBorder(10, 20, 0, 20)); |
||||
this.add(imagePane); |
||||
this.add(contentPane); |
||||
this.add(detailInfoPane); |
||||
this.setBackground(Color.WHITE); |
||||
SwingUtilities.invokeLater(()->{scrollToTop();}); |
||||
} |
||||
|
||||
/** |
||||
* scrollPane创建后会拉到最底下,初始化的时候手动拉到顶 |
||||
*/ |
||||
public void scrollToTop() { |
||||
infoScrollPane.getVerticalScrollBar().setValue(0); |
||||
detailInfoPane.getVerticalScrollBar().setValue(0); |
||||
} |
||||
|
||||
private void initComponent() { |
||||
createImagePane(); |
||||
createContentPane(); |
||||
createDetailInfoScrollPane(); |
||||
} |
||||
|
||||
private void createContentPane() { |
||||
createInfoScrollPane(); |
||||
createOperatePane(); |
||||
contentPane = new JPanel(); |
||||
contentPane.setLayout(new FlowLayout(FlowLayout.LEFT)); |
||||
contentPane.setPreferredSize(new Dimension(CONTENT_PANE_WIDTH, CONTENT_PANE_HEIGHT)); |
||||
contentPane.add(infoScrollPane); |
||||
contentPane.add(operatePane); |
||||
contentPane.setBackground(Color.WHITE); |
||||
} |
||||
|
||||
/** |
||||
* 操作区:查看详情,立即使用 |
||||
*/ |
||||
private void createOperatePane() { |
||||
operatePane = new JPanel(new FlowLayout(FlowLayout.LEFT)); |
||||
|
||||
JLabel emptyLabel = new JLabel(); |
||||
emptyLabel.setPreferredSize(new Dimension(145, 25)); |
||||
JLabel priceLabel = new JLabel(); |
||||
priceLabel.setForeground(Color.RED); |
||||
if (data.getPrice() == 0) { |
||||
priceLabel.setText(FREE); |
||||
} else { |
||||
priceLabel.setText(RMB + SPACE + data.getPrice()); |
||||
} |
||||
|
||||
operatePane.add(createLinkLabel()); |
||||
operatePane.add(emptyLabel); |
||||
operatePane.add(priceLabel); |
||||
operatePane.add(createStartUseButton()); |
||||
operatePane.setBackground(Color.WHITE); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 查看详情 |
||||
*/ |
||||
JLabel createLinkLabel() { |
||||
JLabel linkLabel = new JLabel(GOTO_DETAIL); |
||||
linkLabel.setBackground(Color.WHITE); |
||||
linkLabel.setForeground(MORE_INFO_LINK); |
||||
linkLabel.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
super.mouseClicked(e); |
||||
openResourceUrl(data.getResourceUrl()); |
||||
} |
||||
}); |
||||
return linkLabel; |
||||
} |
||||
|
||||
/** |
||||
* 方便埋点 |
||||
*/ |
||||
void openResourceUrl(String url) { |
||||
BrowseUtils.browser(url); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* “立即使用” 按钮 |
||||
*/ |
||||
JButton createStartUseButton() { |
||||
JButton starUseButton = new JButton(START_USE) { |
||||
@Override |
||||
public void paintComponent(Graphics g) { |
||||
Graphics2D g2d = (Graphics2D) g; |
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||
g2d.setColor(UIConstants.FLESH_BLUE); |
||||
g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 4, 4); |
||||
super.paintComponent(g2d); |
||||
} |
||||
}; |
||||
starUseButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); |
||||
starUseButton.setForeground(Color.WHITE); |
||||
starUseButton.setFont(AlphaFineConstants.MEDIUM_FONT); |
||||
starUseButton.addActionListener(new StartUseAction(data)); |
||||
starUseButton.setPreferredSize(new Dimension(BUTTON_WIDTH, BUTTON_HEIGHT)); |
||||
starUseButton.setContentAreaFilled(false); |
||||
return starUseButton; |
||||
} |
||||
|
||||
private void createImagePane() { |
||||
imagePane = new TemplateResourceImagePanel(data.getRoot(), IMAGE_WIDTH, IMAGE_HEIGHT); |
||||
} |
||||
|
||||
/** |
||||
* 基本信息页 |
||||
*/ |
||||
private void createInfoScrollPane() { |
||||
|
||||
StringBuilder sb = new StringBuilder(); |
||||
|
||||
// 开发者
|
||||
sb.append(String.format(HTML_P_TAG_FORMAT, VENDOR + data.getVendor())); |
||||
// 标签
|
||||
sb.append(String.format(HTML_P_TAG_FORMAT, TAGS + data.getTagsString())); |
||||
// 所属模板包
|
||||
if (!StringUtils.isEmpty(data.getParentPkgName())) { |
||||
sb.append(String.format(HTML_P_TAG_FORMAT, PARENT_PACKAGE + LinkStrUtils.generateLinkTagWithoutUnderLine(data.getParentPkgUrl(), data.getParentPkgName()))); |
||||
} |
||||
// 信息
|
||||
sb.append(String.format(HTML_P_TAG_FORMAT, data.getInfo())); |
||||
|
||||
|
||||
MessageWithLink content = new MessageWithLink(String.format(HTML_FORMAT, sb)); |
||||
content.setBackground(INFO_PANE_BACKGROUND); |
||||
content.setForeground(INFO_PANE_FOREGROUND); |
||||
infoScrollPane = new UIScrollPane(content); |
||||
infoScrollPane.setForeground(INFO_PANE_FOREGROUND); |
||||
infoScrollPane.setPreferredSize(new Dimension(SCROLL_PANE_WIDTH, SCROLL_PANE_HEIGHT)); |
||||
infoScrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); |
||||
} |
||||
|
||||
/** |
||||
* 详细信息页 |
||||
*/ |
||||
private void createDetailInfoScrollPane() { |
||||
MessageWithLink content = new MessageWithLink(String.format(DETAIL_INFO_HTML_FORMAT, data.getHtmlText())); |
||||
detailInfoPane = new UIScrollPane(content); |
||||
detailInfoPane.setPreferredSize(new Dimension(PANE_WIDTH, DETAIL_PANE_HEIGHT)); |
||||
detailInfoPane.setBackground(Color.WHITE); |
||||
detailInfoPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); |
||||
} |
||||
|
||||
|
||||
|
||||
} |
@ -0,0 +1,212 @@
|
||||
package com.fr.design.mainframe.alphafine.preview; |
||||
|
||||
import com.fr.common.util.Strings; |
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.design.mainframe.alphafine.AlphaFineConstants; |
||||
import com.fr.design.mainframe.alphafine.AlphaFineHelper; |
||||
import com.fr.design.mainframe.alphafine.component.TemplateResourcePageGridPane; |
||||
import com.fr.design.mainframe.alphafine.model.TemplateResource; |
||||
import com.fr.design.mainframe.alphafine.model.TemplateResourceDetail; |
||||
import com.fr.design.mainframe.alphafine.search.manager.impl.TemplateResourceSearchManager; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import org.jooq.tools.StringUtils; |
||||
|
||||
|
||||
import javax.swing.JPanel; |
||||
import javax.swing.SwingWorker; |
||||
import java.awt.CardLayout; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
import java.util.List; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* alphafine - 模板商城面板 |
||||
* |
||||
* @author Link |
||||
* @version 10.0 |
||||
* Created by Link on 2022/9/22 |
||||
* */ |
||||
public class TemplateShopPane extends JPanel { |
||||
|
||||
private static final TemplateShopPane INSTANCE = new TemplateShopPane(); |
||||
public static TemplateShopPane getInstance() { |
||||
return INSTANCE; |
||||
} |
||||
|
||||
// public 方便埋点
|
||||
public static final String DEFAULT_PAGE_PANEL = "defaultPagePane"; |
||||
public static final String PAGE_PANEL = "pagePane"; |
||||
public static final String DETAIL_PANEL = "detailPane"; |
||||
public static final String LOADING_PANEL = "loadingPane"; |
||||
public static final String FAILED_PANEL = "failedPane"; |
||||
private String currentCard = StringUtils.EMPTY; |
||||
private static final String SLASH = "/"; |
||||
|
||||
private CardLayout cardLayout = new CardLayout(); |
||||
private JPanel defaultPagePane; |
||||
private JPanel pagePane; |
||||
private JPanel detailPane; |
||||
private JPanel loadingPane; |
||||
private JPanel failedPane; |
||||
|
||||
private TemplateShopPane() { |
||||
setLayout(cardLayout); |
||||
initComponents(); |
||||
this.add(defaultPagePane, DEFAULT_PAGE_PANEL); |
||||
this.add(loadingPane, LOADING_PANEL); |
||||
this.setPreferredSize(AlphaFineConstants.PREVIEW_SIZE); |
||||
switchCard(DEFAULT_PAGE_PANEL); |
||||
} |
||||
|
||||
private void switchCard(String flag) { |
||||
cardLayout.show(this, flag); |
||||
currentCard = flag; |
||||
} |
||||
|
||||
private void initComponents() { |
||||
defaultPagePane = createDefaultResourcePane(); |
||||
loadingPane = createLoadingPane(); |
||||
} |
||||
|
||||
/** |
||||
* 刷新 |
||||
* */ |
||||
public void refreshPagePane(List<TemplateResource> resourceList) { |
||||
pagePane = createContentPane(resourceList); |
||||
this.add(pagePane, PAGE_PANEL); |
||||
switchCard(PAGE_PANEL); |
||||
} |
||||
|
||||
/** |
||||
* 退出搜索结果面板 |
||||
* */ |
||||
public void quitSearchResultPane() { |
||||
if (StringUtils.equals(currentCard,PAGE_PANEL)) { |
||||
switchCard(DEFAULT_PAGE_PANEL); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 显示结果 |
||||
* */ |
||||
public void showResult() { |
||||
if (Strings.isEmpty(AlphaFineHelper.getAlphaFineDialog().getSearchText())) { |
||||
switchCard(DEFAULT_PAGE_PANEL); |
||||
} else { |
||||
switchCard(PAGE_PANEL); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 打开二级页面,显示详细信息 |
||||
* */ |
||||
public void searchAndShowDetailPane(TemplateResource resource) { |
||||
|
||||
changeLabel(resource.getName()); |
||||
|
||||
switchCard(LOADING_PANEL); |
||||
|
||||
new SwingWorker<TemplateResourceDetail, Void>() { |
||||
@Override |
||||
protected TemplateResourceDetail doInBackground(){ |
||||
// 搜搜
|
||||
TemplateResourceDetail detail = TemplateResourceSearchManager.getInstance().getDetailSearchResult(resource); |
||||
return detail; |
||||
} |
||||
|
||||
@Override |
||||
protected void done() { |
||||
TemplateResourceDetail detail = null; |
||||
try { |
||||
detail = get(); |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e, e.getMessage()); |
||||
} |
||||
|
||||
if (detail == null) { |
||||
setRetryAction(resource); |
||||
switchCard(FAILED_PANEL); |
||||
} else { |
||||
// detailpane初始化
|
||||
detailPane = new TemplateResourceDetailPane(detail); |
||||
// 切换
|
||||
INSTANCE.add(detailPane, DETAIL_PANEL); |
||||
switchCard(DETAIL_PANEL); |
||||
} |
||||
} |
||||
|
||||
|
||||
}.execute(); |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
private void changeLabel(String resourceName) { |
||||
JPanel labelNamePane = AlphaFineHelper.getAlphaFineDialog().getLabelWestPane(); |
||||
UILabel tabLabel = AlphaFineHelper.getAlphaFineDialog().getTabLabel(); |
||||
tabLabel.setForeground(AlphaFineConstants.DARK_GRAY); |
||||
|
||||
UILabel slash = new UILabel(SLASH); |
||||
slash.setForeground(AlphaFineConstants.DARK_GRAY); |
||||
|
||||
UILabel resourceLabel = new UILabel(resourceName); |
||||
resourceLabel.setForeground(AlphaFineConstants.LABEL_SELECTED); |
||||
|
||||
|
||||
labelNamePane.removeAll(); |
||||
labelNamePane.add(tabLabel); |
||||
labelNamePane.add(slash); |
||||
labelNamePane.add(resourceLabel); |
||||
|
||||
|
||||
tabLabel.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
super.mouseClicked(e); |
||||
switchCard(PAGE_PANEL); |
||||
tabLabel.setForeground(AlphaFineConstants.LABEL_SELECTED); |
||||
labelNamePane.remove(slash); |
||||
labelNamePane.remove(resourceLabel); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* 方便埋点,勿删 |
||||
* */ |
||||
public String getCurrentCard() { |
||||
return currentCard; |
||||
} |
||||
|
||||
private JPanel createContentPane(List<TemplateResource> templateResources) { |
||||
return new TemplateResourcePageGridPane(templateResources); |
||||
} |
||||
|
||||
private JPanel createDefaultResourcePane() { |
||||
return createContentPane(TemplateResourceSearchManager.getInstance().getDefaultResourceList()); |
||||
} |
||||
|
||||
|
||||
private JPanel createLoadingPane() { |
||||
return new SearchLoadingPane(); |
||||
} |
||||
|
||||
private void setRetryAction(TemplateResource resource) { |
||||
if (failedPane != null) { |
||||
INSTANCE.remove(failedPane); |
||||
} |
||||
failedPane = createFailedPane(resource); |
||||
INSTANCE.add(failedPane, FAILED_PANEL); |
||||
} |
||||
|
||||
private JPanel createFailedPane(TemplateResource resource) { |
||||
return new NetWorkFailedPane(()->{this.searchAndShowDetailPane(resource);}); |
||||
} |
||||
|
||||
|
||||
|
||||
} |
@ -0,0 +1,116 @@
|
||||
package com.fr.design.mainframe.alphafine.search; |
||||
|
||||
import com.fr.design.mainframe.alphafine.AlphaFineConstants; |
||||
import com.fr.design.mainframe.alphafine.AlphaFineHelper; |
||||
import com.fr.design.mainframe.alphafine.CellType; |
||||
import com.fr.design.mainframe.alphafine.component.AlphaFineFrame; |
||||
import com.fr.design.mainframe.alphafine.model.TemplateResource; |
||||
import com.fr.design.mainframe.alphafine.preview.TemplateShopPane; |
||||
import com.fr.log.FineLoggerFactory; |
||||
|
||||
import javax.swing.SwingWorker; |
||||
import java.util.List; |
||||
import java.util.function.Function; |
||||
|
||||
/** |
||||
* |
||||
* alphafine - 模板资源搜索管理 |
||||
* |
||||
* @author Link |
||||
* @version 10.0 |
||||
* Created by Link on 2022/9/22 |
||||
* */ |
||||
public class TemplateResourceSearchWorkerManager implements SearchManager { |
||||
|
||||
private final CellType cellType; |
||||
|
||||
private SwingWorker<List<TemplateResource>, Void> searchWorker; |
||||
|
||||
private Function<SearchTextBean, List<TemplateResource>> searchFunction; |
||||
|
||||
private AlphaFineFrame alphaFineFrame; |
||||
|
||||
private volatile boolean searchResult = true; |
||||
|
||||
private volatile boolean searchOver = false; |
||||
|
||||
private volatile boolean networkError = false; |
||||
|
||||
public TemplateResourceSearchWorkerManager(CellType cellType, Function<SearchTextBean, List<TemplateResource>> searchFunction, AlphaFineFrame alphaFineFrame) { |
||||
this.cellType = cellType; |
||||
this.searchFunction = searchFunction; |
||||
this.alphaFineFrame = alphaFineFrame; |
||||
} |
||||
|
||||
@Override |
||||
public void doSearch(SearchTextBean searchTextBean) { |
||||
checkSearchWork(); |
||||
searchOver = false; |
||||
|
||||
this.searchWorker = new SwingWorker<List<TemplateResource>, Void>() { |
||||
@Override |
||||
protected List<TemplateResource> doInBackground() { |
||||
List<TemplateResource> list; |
||||
if (!AlphaFineHelper.isNetworkOk()) { |
||||
networkError = true; |
||||
FineLoggerFactory.getLogger().warn("alphaFine network error"); |
||||
} |
||||
list = searchFunction.apply(searchTextBean); |
||||
return list; |
||||
} |
||||
|
||||
@Override |
||||
protected void done() { |
||||
searchOver = true; |
||||
if (!isCancelled()) { |
||||
try { |
||||
List<TemplateResource> list = get(); |
||||
searchResult = !list.isEmpty(); |
||||
showResult(list); |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
this.searchWorker.execute(); |
||||
} |
||||
|
||||
void showResult(List<TemplateResource> list) { |
||||
if (networkError && !searchResult) { |
||||
alphaFineFrame.showResult(AlphaFineConstants.NETWORK_ERROR); |
||||
return; |
||||
} |
||||
|
||||
if (alphaFineFrame.getSelectedType() == cellType) { |
||||
if (!searchResult) { |
||||
alphaFineFrame.showResult(CellType.NO_RESULT.getFlagStr4None()); |
||||
} else { |
||||
TemplateShopPane.getInstance().refreshPagePane(list); |
||||
AlphaFineHelper.getAlphaFineDialog().showResult(cellType.getFlagStr4None()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public boolean hasSearchResult() { |
||||
return searchResult; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isSearchOver() { |
||||
return searchOver; |
||||
} |
||||
|
||||
private void checkSearchWork() { |
||||
if (this.searchWorker != null && !this.searchWorker.isDone()) { |
||||
this.searchWorker.cancel(true); |
||||
this.searchWorker = null; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public boolean isNetWorkError() { |
||||
return networkError; |
||||
} |
||||
} |
@ -0,0 +1,358 @@
|
||||
package com.fr.design.mainframe.alphafine.search.helper; |
||||
|
||||
import com.fr.design.DesignerEnvManager; |
||||
import com.fr.design.extra.PluginConstants; |
||||
import com.fr.design.mainframe.alphafine.download.FineMarketDownloadManager; |
||||
import com.fr.design.mainframe.alphafine.model.TemplateResource; |
||||
import com.fr.file.FileCommonUtils; |
||||
import com.fr.general.CloudCenter; |
||||
import com.fr.general.http.HttpToolbox; |
||||
import com.fr.json.JSONArray; |
||||
import com.fr.json.JSONObject; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.market.key.FineMarketPublicKeyHolder; |
||||
import com.fr.security.SecurityToolbox; |
||||
import com.fr.stable.StableUtils; |
||||
import com.fr.third.org.apache.http.HttpEntity; |
||||
import com.fr.third.org.apache.http.HttpException; |
||||
import com.fr.third.org.apache.http.HttpStatus; |
||||
import com.fr.third.org.apache.http.client.config.CookieSpecs; |
||||
import com.fr.third.org.apache.http.client.config.RequestConfig; |
||||
import com.fr.third.org.apache.http.client.methods.CloseableHttpResponse; |
||||
import com.fr.third.org.apache.http.client.methods.HttpUriRequest; |
||||
import com.fr.third.org.apache.http.client.methods.RequestBuilder; |
||||
import com.fr.third.org.apache.http.impl.client.BasicCookieStore; |
||||
import com.fr.third.org.apache.http.impl.client.CloseableHttpClient; |
||||
import com.fr.third.org.apache.http.impl.client.HttpClients; |
||||
import org.jetbrains.annotations.Nullable; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileOutputStream; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
import java.util.UUID; |
||||
import java.util.regex.Matcher; |
||||
import java.util.regex.Pattern; |
||||
|
||||
/** |
||||
* |
||||
* alphafine - 帆软市场助手 |
||||
* |
||||
* @author Link |
||||
* @version 10.0 |
||||
* Created by Link on 2022/9/22 |
||||
* */ |
||||
public class FineMarketClientHelper { |
||||
private static final FineMarketClientHelper INSTANCE = new FineMarketClientHelper(); |
||||
public static FineMarketClientHelper getInstance() { |
||||
return INSTANCE; |
||||
} |
||||
|
||||
private static final String CERTIFICATE_PUBLIC_KEY = FineMarketPublicKeyHolder.getInstance().getDefaultKey(); |
||||
public static final String FINE_MARKET_TEMPLATE_INFO = CloudCenter.getInstance().acquireUrlByKind("market.template.info"); |
||||
public static final String FINE_MARKET_TEMPLATE_URL = CloudCenter.getInstance().acquireUrlByKind("market.template.url"); |
||||
public static final String FILE_DOWNLOAD = "file/download/"; |
||||
public static final String PACKAGE_DOWNLOAD = "package/download/"; |
||||
public static final String TEMPLATES_PARENT_PACKAGE = "parent/"; |
||||
public static final String TEMPLATES_TAGS = "filter"; |
||||
public static final String NAME_SEARCH = "?searchKeyword="; |
||||
|
||||
public static final String RESPONSE_STATE = "state"; |
||||
public static final String RESPONSE_SUCCESS = "ok"; |
||||
public static final String RESPONSE_RESULT = "result"; |
||||
public static final String TAGS_KEY = "key"; |
||||
public static final String TAGS_ITEMS = "items"; |
||||
public static final String TAG_NAME = "name"; |
||||
public static final String TAG_ID = "id"; |
||||
private static final String FILENAME_FORMAT = ".+?(.zip|.rar|.cpt|.frm)"; |
||||
private static final Pattern FILENAME_PATTERN = Pattern.compile(FILENAME_FORMAT); |
||||
|
||||
// 缓存下所有tag标签
|
||||
private Map<String, String> tags; |
||||
|
||||
|
||||
/** |
||||
* 获取模板资源的下载链接 |
||||
* */ |
||||
public String getResourceDownloadUrl(TemplateResource templateResource) { |
||||
if (TemplateResource.Type.SCENARIO_SOLUTION.equals(templateResource.getType())) { |
||||
return getPackageDownloadUrl(); |
||||
} else { |
||||
return getFileDownLoadUrl(); |
||||
} |
||||
|
||||
} |
||||
|
||||
private String getPackageDownloadUrl() { |
||||
return FINE_MARKET_TEMPLATE_INFO + PACKAGE_DOWNLOAD; |
||||
} |
||||
|
||||
private String getFileDownLoadUrl() { |
||||
return FINE_MARKET_TEMPLATE_INFO + FILE_DOWNLOAD; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 下载 |
||||
* */ |
||||
public String download(TemplateResource resource, File destDir, com.fr.design.extra.Process<Double> process) throws Exception { |
||||
String resourceId = resource.getId(); |
||||
|
||||
CloseableHttpResponse fileRes = getFileResponse(resource, resourceId); |
||||
|
||||
if (fileRes.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { |
||||
|
||||
File destFile = createDestFile(destDir, resource); |
||||
|
||||
StableUtils.makesureFileExist(destFile); |
||||
|
||||
InputStream content = null; |
||||
FileOutputStream writer = null; |
||||
try { |
||||
writer = new FileOutputStream(FileCommonUtils.getAbsolutePath(destFile)); |
||||
|
||||
HttpEntity entity = fileRes.getEntity(); |
||||
long totalSize = entity.getContentLength(); |
||||
content = entity.getContent(); |
||||
|
||||
|
||||
byte[] data = new byte[PluginConstants.BYTES_NUM]; |
||||
int bytesRead; |
||||
int totalBytesRead = 0; |
||||
|
||||
while ((bytesRead = content.read(data)) > 0) { |
||||
writer.write(data, 0, bytesRead); |
||||
data = new byte[PluginConstants.BYTES_NUM]; |
||||
totalBytesRead += bytesRead; |
||||
process.process(totalBytesRead / (double) totalSize); |
||||
} |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e, e.getMessage()); |
||||
} finally { |
||||
content.close(); |
||||
writer.flush(); |
||||
writer.close(); |
||||
} |
||||
|
||||
|
||||
FineLoggerFactory.getLogger().info("download resource{} success", resourceId); |
||||
process.process(FineMarketDownloadManager.PROCESS_SUCCESS); |
||||
|
||||
return FileCommonUtils.getAbsolutePath(destFile); |
||||
} else { |
||||
FineLoggerFactory.getLogger().info("download resource{} failed", resourceId); |
||||
process.process(FineMarketDownloadManager.PROCESS_FAILED); |
||||
throw new HttpException(); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
private CloseableHttpResponse getFileResponse(TemplateResource resource, String resourceId) throws Exception { |
||||
CloseableHttpResponse fileRes = postDownloadHttpResponse(getResourceDownloadUrl(resource), resourceId); |
||||
if (fileRes.getStatusLine().getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY) { |
||||
fileRes = getDownloadHttpResponse(fileRes.getHeaders("Location")[0].getValue()); |
||||
} |
||||
return fileRes; |
||||
} |
||||
|
||||
/** |
||||
* 在目标路径下(destDir)创建与资源名称相同的文件夹 (finalDir) |
||||
* 将资源下载到 destDir/finalDir |
||||
* 如果文件重复,则在文件名前加自增id |
||||
* */ |
||||
private File createDestFile(File destDir, TemplateResource resource) { |
||||
String fileName = resource.getName(); |
||||
File finalDir = new File(StableUtils.pathJoin(FileCommonUtils.getAbsolutePath(destDir), fileName)); |
||||
try { |
||||
if (!finalDir.exists()) { |
||||
finalDir.mkdir(); |
||||
} |
||||
|
||||
fileName = resource.getFileName(); |
||||
fileName = rename(fileName, finalDir); |
||||
|
||||
File destFile = new File(StableUtils.pathJoin(FileCommonUtils.getAbsolutePath(finalDir), fileName)); |
||||
return destFile; |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e, e.getMessage()); |
||||
} |
||||
fileName = UUID.randomUUID() + fileName; |
||||
File dest = new File(StableUtils.pathJoin(FileCommonUtils.getAbsolutePath(finalDir), fileName)); |
||||
return dest; |
||||
} |
||||
|
||||
/** |
||||
* 处理下文件名,比如重复下载需要处理重名的情况 |
||||
* */ |
||||
String rename(String fileName, File parentDir) throws Exception { |
||||
|
||||
if (!FILENAME_PATTERN.matcher(fileName).matches()) { |
||||
throw new Exception("fileName format error: " + fileName); |
||||
} |
||||
|
||||
// 获取文件名(含后缀)
|
||||
String prefix = fileName.substring(0, fileName.length() - 4); |
||||
String suffix = fileName.substring(fileName.length() - 4); |
||||
|
||||
|
||||
File file = new File(StableUtils.pathJoin(FileCommonUtils.getAbsolutePath(parentDir), fileName)); |
||||
// 处理重复文件名
|
||||
if (file.exists()) { |
||||
String fileNameFormat = prefix + "(%d)" + suffix; |
||||
Pattern pattern = Pattern.compile(prefix + "\\((\\d)\\)" + suffix); |
||||
int cnt = 0; |
||||
|
||||
File[] files = parentDir.listFiles(); |
||||
for (File f : files) { |
||||
Matcher matcher = pattern.matcher(f.getName()); |
||||
if (matcher.find()) { |
||||
cnt = Math.max(cnt, Integer.parseInt(matcher.group(1))); |
||||
} |
||||
} |
||||
cnt++; |
||||
fileName = String.format(fileNameFormat, cnt); |
||||
} |
||||
return fileName; |
||||
|
||||
} |
||||
|
||||
private static CloseableHttpResponse getDownloadHttpResponse(String url) throws Exception { |
||||
//先登录一下。不然可能失败
|
||||
CloseableHttpClient client = createClient(); |
||||
HttpUriRequest file = RequestBuilder.get() |
||||
.setUri(url) |
||||
.build(); |
||||
return client.execute(file); |
||||
} |
||||
|
||||
private static CloseableHttpResponse postDownloadHttpResponse(String url, String id) throws Exception { |
||||
return postDownloadHttpResponse(url, id, new HashMap<>()); |
||||
} |
||||
|
||||
private static CloseableHttpResponse postDownloadHttpResponse(String url, String id, Map<String, String> params) throws Exception { |
||||
//先登录一下。不然可能失败
|
||||
CloseableHttpClient client = createClient(); |
||||
FineLoggerFactory.getLogger().info("login fr-market"); |
||||
FineLoggerFactory.getLogger().info("start download resource {}", id); |
||||
RequestBuilder builder = RequestBuilder.post() |
||||
.setHeader("User-Agent", "Mozilla/5.0") |
||||
.setUri(url) |
||||
.addParameter("id", SecurityToolbox.encrypt(id, CERTIFICATE_PUBLIC_KEY)) |
||||
.addParameter("userId", String.valueOf(DesignerEnvManager.getEnvManager().getDesignerLoginUid())); |
||||
|
||||
if (params != null) { |
||||
Set<String> keys = params.keySet(); |
||||
for (String key: keys) { |
||||
builder.addParameter(key, params.get(key)); |
||||
} |
||||
} |
||||
return client.execute(builder.build()); |
||||
} |
||||
|
||||
|
||||
private static CloseableHttpClient createClient() { |
||||
|
||||
BasicCookieStore cookieStore = new BasicCookieStore(); |
||||
return HttpClients.custom() |
||||
.setDefaultRequestConfig(RequestConfig.custom() |
||||
.setCookieSpec(CookieSpecs.STANDARD).build()) |
||||
.setDefaultCookieStore(cookieStore) |
||||
.build(); |
||||
} |
||||
|
||||
public @Nullable JSONObject getTemplateInfoById(String id) throws IOException { |
||||
String url = FINE_MARKET_TEMPLATE_INFO + id; |
||||
String jsonString = HttpToolbox.get(url); |
||||
JSONObject jsonObject = new JSONObject(jsonString); |
||||
String responseState = (String) jsonObject.get(RESPONSE_STATE); |
||||
if (RESPONSE_SUCCESS.equals(responseState)) { |
||||
return jsonObject.getJSONObject(RESPONSE_RESULT); |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public @Nullable JSONArray getTemplateInfoByName(String name) throws IOException { |
||||
String url = FINE_MARKET_TEMPLATE_INFO + NAME_SEARCH + name; |
||||
String jsonString = HttpToolbox.get(url); |
||||
JSONObject jsonObject = new JSONObject(jsonString); |
||||
String responseState = (String) jsonObject.get(RESPONSE_STATE); |
||||
if (RESPONSE_SUCCESS.equals(responseState)) { |
||||
return jsonObject.getJSONArray(RESPONSE_RESULT); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public String getTemplateUrlById(String id) { |
||||
return FINE_MARKET_TEMPLATE_URL + id; |
||||
} |
||||
|
||||
public @Nullable JSONObject getTemplateParentPackageByTemplateId(String id) throws IOException { |
||||
String url = FINE_MARKET_TEMPLATE_INFO + TEMPLATES_PARENT_PACKAGE + id; |
||||
String jsonString = HttpToolbox.get(url); |
||||
JSONObject jsonObject = new JSONObject(jsonString); |
||||
String responseState = (String) jsonObject.get(RESPONSE_STATE); |
||||
if (RESPONSE_SUCCESS.equals(responseState)) { |
||||
JSONArray jsonArray = jsonObject.getJSONArray(RESPONSE_RESULT); |
||||
if (!jsonArray.isEmpty()) { |
||||
return jsonObject.getJSONArray(RESPONSE_RESULT).getJSONObject(0); |
||||
} |
||||
} |
||||
return null; |
||||
|
||||
} |
||||
|
||||
|
||||
/** |
||||
* 根据模板资源的tagid,获取tagName |
||||
* */ |
||||
public List<String> getTemplateTagsByTemplateTagIds(String[] tagIds) throws IOException { |
||||
List<String> list = new ArrayList<>(); |
||||
|
||||
initTags(); |
||||
|
||||
if (tagIds != null) { |
||||
for (String tagId : tagIds) { |
||||
String tagName = tags.get(tagId); |
||||
if (tagName != null) { |
||||
list.add(tagName); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return list; |
||||
} |
||||
|
||||
/** |
||||
* 请求帆软市场,获取所有tag信息,并构建tagid - tagname的map |
||||
* */ |
||||
private void initTags() throws IOException { |
||||
tags = new HashMap<>(); |
||||
String url = FINE_MARKET_TEMPLATE_INFO + TEMPLATES_TAGS; |
||||
String jsonString = HttpToolbox.get(url); |
||||
JSONObject jsonObject = new JSONObject(jsonString); |
||||
String responseState = (String) jsonObject.get(RESPONSE_STATE); |
||||
if (RESPONSE_SUCCESS.equals(responseState)) { |
||||
JSONArray resultArray = jsonObject.getJSONArray(RESPONSE_RESULT); |
||||
for (int i = 1; i < resultArray.size(); i++) { |
||||
JSONObject result = resultArray.getJSONObject(i); |
||||
String key = result.getString(TAGS_KEY); |
||||
key = key.substring(key.indexOf('@') + 1); |
||||
JSONArray items = result.getJSONArray(TAGS_ITEMS); |
||||
for (int j = 0; j < items.length(); j++) { |
||||
JSONObject item = items.getJSONObject(j); |
||||
String id = item.getString(TAG_ID); |
||||
String name = item.getString(TAG_NAME); |
||||
tags.put(key + '-' + id, name); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,111 @@
|
||||
package com.fr.design.mainframe.alphafine.search.manager.impl; |
||||
|
||||
|
||||
import com.fr.design.mainframe.alphafine.AlphaFineHelper; |
||||
import com.fr.design.mainframe.alphafine.model.TemplateResource; |
||||
import com.fr.design.mainframe.alphafine.model.TemplateResourceDetail; |
||||
import com.fr.design.mainframe.alphafine.search.helper.FineMarketClientHelper; |
||||
import com.fr.general.CloudCenter; |
||||
import com.fr.general.IOUtils; |
||||
import com.fr.json.JSONArray; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* alphafine - 模板资源搜索助手 |
||||
* |
||||
* @author Link |
||||
* @version 10.0 |
||||
* Created by Link on 2022/9/22 |
||||
* */ |
||||
public class TemplateResourceSearchManager { |
||||
|
||||
private static final TemplateResourceSearchManager INSTANCE = new TemplateResourceSearchManager(); |
||||
public static TemplateResourceSearchManager getInstance() { |
||||
return INSTANCE; |
||||
} |
||||
|
||||
public static final String LOCAL_RESOURCE_URL = "/com/fr/design/mainframe/alphafine/template_resource/local_templates.json"; |
||||
private static final FineMarketClientHelper HELPER = FineMarketClientHelper.getInstance(); |
||||
|
||||
|
||||
/** |
||||
* 帆软市场暂时没有分页搜索接口,先全量搜,分页展示 |
||||
* */ |
||||
public List<TemplateResource> getSearchResult(String searchText) { |
||||
List<TemplateResource> resourceList = new ArrayList<>(); |
||||
|
||||
// 联网搜索
|
||||
try { |
||||
JSONArray jsonArray = HELPER.getTemplateInfoByName(searchText); |
||||
if (jsonArray != null && !jsonArray.isEmpty()) { |
||||
resourceList.addAll(TemplateResource.createByJson(jsonArray)); |
||||
} |
||||
} catch (Exception e) { |
||||
|
||||
} |
||||
|
||||
// 本地搜索
|
||||
if (resourceList.isEmpty()) { |
||||
List<TemplateResource> localResource = getEmbedResourceList(); |
||||
localResource.stream().forEach(resource->{ |
||||
if (resource.getName().toLowerCase().contains(searchText)) { |
||||
resourceList.add(resource); |
||||
} |
||||
}); |
||||
} |
||||
return resourceList; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 返回默认资源 |
||||
* */ |
||||
public List<TemplateResource> getDefaultResourceList() { |
||||
List<TemplateResource> resourceList = getEmbedResourceList(); |
||||
// 添加推荐搜索卡片
|
||||
resourceList.add(TemplateResource.getRecommendSearch()); |
||||
return resourceList; |
||||
} |
||||
|
||||
/** |
||||
* 返回内置资源 |
||||
* */ |
||||
public List<TemplateResource> getEmbedResourceList() { |
||||
List<TemplateResource> resourceList = new ArrayList<>(); |
||||
JSONArray jsonArray = getEmbedResourceJSONArray(); |
||||
for (int i = 0; i < jsonArray.size(); i++) { |
||||
TemplateResource resource = TemplateResource.createByJson(jsonArray.getJSONObject(i)); |
||||
resource.setEmbed(true); |
||||
resourceList.add(resource); |
||||
} |
||||
return resourceList; |
||||
} |
||||
|
||||
public JSONArray getEmbedResourceJSONArray() { |
||||
String jsonString = IOUtils.readResourceAsString(LOCAL_RESOURCE_URL); |
||||
return new JSONArray(jsonString); |
||||
} |
||||
|
||||
public List<String> getRecommendSearchKeys() { |
||||
List<String> searchKey = new ArrayList<>(); |
||||
String[] keys = CloudCenter.getInstance().acquireConf("alphafine.tempalte.recommend", "跑马灯,填报,地图").split(","); |
||||
for (String k : keys) { |
||||
searchKey.add(k); |
||||
} |
||||
return searchKey; |
||||
} |
||||
|
||||
|
||||
public TemplateResourceDetail getDetailSearchResult(TemplateResource resource) { |
||||
if (AlphaFineHelper.isNetworkOk()) { |
||||
return TemplateResourceDetail.createByTemplateResource(resource); |
||||
} else { |
||||
return TemplateResourceDetail.createFromEmbedResource(resource); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,73 @@
|
||||
package com.fr.market.key; |
||||
|
||||
import com.fr.general.IOUtils; |
||||
import com.fr.io.utils.ResourceIOUtils; |
||||
import com.fr.log.FineLoggerFactory; |
||||
|
||||
import java.io.ByteArrayInputStream; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.Properties; |
||||
|
||||
/** |
||||
* |
||||
* 帆软市场公钥Properties |
||||
* |
||||
* @author Link |
||||
* @version 11.0 |
||||
* Created by Yvan on 2022/8/25 |
||||
*/ |
||||
public class FineMarketDefaultKeyProperties { |
||||
|
||||
private Properties properties = new Properties(); |
||||
|
||||
private Map<String, String> publicKeyMap = new HashMap<>(); |
||||
|
||||
private String propertyPath; |
||||
|
||||
private FineMarketDefaultKeyProperties(String propertyPath) { |
||||
this.propertyPath = propertyPath; |
||||
load(); |
||||
} |
||||
|
||||
/** |
||||
* 构造方法 |
||||
*/ |
||||
public static FineMarketDefaultKeyProperties create(String propertyPath) { |
||||
return new FineMarketDefaultKeyProperties(propertyPath); |
||||
} |
||||
|
||||
private void load() { |
||||
try (InputStream inputStream = IOUtils.readResource(getPropertyPath())) { |
||||
byte[] data = ResourceIOUtils.inputStream2Bytes(inputStream); |
||||
properties.load(new ByteArrayInputStream(data)); |
||||
trims(properties); |
||||
publicKeyMap.put(FineMarketPublicKeyConstants.DEFAULT_KEY_KEY, properties.getProperty(FineMarketPublicKeyConstants.DEFAULT_KEY_KEY)); |
||||
} catch (IOException e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
} |
||||
|
||||
private String getPropertyPath() { |
||||
return this.propertyPath; |
||||
} |
||||
|
||||
public String getPublicKey() { |
||||
return publicKeyMap.get(FineMarketPublicKeyConstants.DEFAULT_KEY_KEY); |
||||
} |
||||
|
||||
/** |
||||
* 去除properties中value末尾的空格 |
||||
* @param properties |
||||
*/ |
||||
public static void trims(Properties properties) { |
||||
for (String key : properties.stringPropertyNames()) { |
||||
String value = properties.getProperty(key); |
||||
if (value != null) { |
||||
properties.put(key, value.trim()); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,32 @@
|
||||
package com.fr.market.key; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* 帆软市场公钥常量 |
||||
* |
||||
* @author Link |
||||
* @version 11.0 |
||||
* Created by Link on 2022/8/25 |
||||
*/ |
||||
public class FineMarketPublicKeyConstants { |
||||
|
||||
public static final String DEFAULT_KEY_KEY = "defaultKey"; |
||||
|
||||
public static final String DEFAULT_KEY_DIRECTORY = "/com/fr/market/key"; |
||||
|
||||
/** |
||||
* 公钥第一段 |
||||
*/ |
||||
public static final String FIRST_PROPERTY = "76c1/default"; |
||||
|
||||
/** |
||||
* 公钥第二段 |
||||
*/ |
||||
public static final String SECOND_PROPERTY = "943f/default"; |
||||
|
||||
/** |
||||
* 公钥第三段 |
||||
*/ |
||||
public static final String THIRD_PROPERTY = "d8a3/default"; |
||||
} |
@ -0,0 +1,48 @@
|
||||
package com.fr.market.key; |
||||
|
||||
import com.fr.stable.StableUtils; |
||||
|
||||
/** |
||||
* 帆软市场公钥Holder |
||||
* @author Link |
||||
* @version 10.0 |
||||
* Created by Link on 2022/8/25 |
||||
*/ |
||||
public class FineMarketPublicKeyHolder { |
||||
|
||||
private static FineMarketPublicKeyHolder instance = null; |
||||
|
||||
private String defaultKey; |
||||
|
||||
public static FineMarketPublicKeyHolder getInstance() { |
||||
|
||||
if (instance == null) { |
||||
synchronized (FineMarketPublicKeyHolder.class) { |
||||
if (instance == null) { |
||||
instance = new FineMarketPublicKeyHolder(); |
||||
} |
||||
} |
||||
} |
||||
return instance; |
||||
} |
||||
|
||||
private FineMarketPublicKeyHolder() { |
||||
init(); |
||||
} |
||||
|
||||
private void init() { |
||||
// 读取三个default.properties文件,组成公钥
|
||||
String firstPart = FineMarketDefaultKeyProperties.create(StableUtils.pathJoin(FineMarketPublicKeyConstants.DEFAULT_KEY_DIRECTORY, FineMarketPublicKeyConstants.FIRST_PROPERTY)).getPublicKey(); |
||||
String secondPart = FineMarketDefaultKeyProperties.create(StableUtils.pathJoin(FineMarketPublicKeyConstants.DEFAULT_KEY_DIRECTORY, FineMarketPublicKeyConstants.SECOND_PROPERTY)).getPublicKey(); |
||||
String thirdPart = FineMarketDefaultKeyProperties.create(StableUtils.pathJoin(FineMarketPublicKeyConstants.DEFAULT_KEY_DIRECTORY, FineMarketPublicKeyConstants.THIRD_PROPERTY)).getPublicKey(); |
||||
this.defaultKey = firstPart + secondPart + thirdPart; |
||||
} |
||||
|
||||
/** |
||||
* 获取默认公钥 |
||||
* @return 公钥 |
||||
*/ |
||||
public String getDefaultKey() { |
||||
return this.defaultKey; |
||||
} |
||||
} |
After Width: | Height: | Size: 672 B |
After Width: | Height: | Size: 685 B |
After Width: | Height: | Size: 820 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 620 B |
After Width: | Height: | Size: 637 B |
After Width: | Height: | Size: 936 B |
After Width: | Height: | Size: 424 KiB |
After Width: | Height: | Size: 1.2 MiB |
After Width: | Height: | Size: 206 KiB |
After Width: | Height: | Size: 591 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 453 B |
After Width: | Height: | Size: 447 B |
After Width: | Height: | Size: 668 B |
After Width: | Height: | Size: 684 B |
After Width: | Height: | Size: 573 B |
After Width: | Height: | Size: 589 B |
@ -0,0 +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", |
||||
"tag": null, |
||||
"pic": "com/fr/design/mainframe/alphafine/images/local_template1.png", |
||||
"price": 0, |
||||
"fileLoca": null, |
||||
"text": "<ol style=\"list-style-type: decimal;\" class=\" list-paddingleft-2\"><li><p>此方案共包括14张模板,其中12张可视化分析看板、1张分析报表以及1张填报表,需在10.0及以上版本设计器中预览;</p></li><li><p>了解方案详情,可移步-数据分析与可视化指南</p></li><li><p>如果您希望在线体验当前方案,可移步-<a href=\"https://demo.finereport.com/\" target=\"_self\">官方DEMO系统</a>:业务场景应用->营销管理<br/></p></li><li><p>如果您对此方案感兴趣,希望更加深入了解,可以填写此表单:<a href=\"https://t6ixa9nyl6.jiandaoyun.com/f/60dac9bd282e8e000796eac3\" target=\"_self\">方案需求录入</a>;后续会有帆软行业顾问将与您联系。<br/></p></li></ol>", |
||||
"description": "将PDCA循环嵌入销售运营管理的全流程中,从决策层、管理层与执行层三个层级,对目标制定、执行过程、复盘分析、问题跟踪进行全方位精细化管理。", |
||||
"updateTime": "2022-06-15T06:36:39.000Z", |
||||
"downloadTimes": 1152, |
||||
"state": 1, |
||||
"designVersion": "10.0", |
||||
"involvePlugins": null, |
||||
"uploadTime": "2022-06-15T06:36:39.000Z", |
||||
"style": null, |
||||
"link": "template", |
||||
"sellerName": "Victoria.", |
||||
"pluginName": [], |
||||
"pkgsize": 14, |
||||
"material": "方案附件.rar", |
||||
"tagsName": "制造加工,营销,组织,管理,经营汇报" |
||||
}, |
||||
{ |
||||
"id": 20000733, |
||||
"name": "库存场景解决方案", |
||||
"uuid": "43d1c14b-1a73-41e6-adcc-aaf2872bc8d4", |
||||
"vendor": "Victoria.", |
||||
"sellerId": 202, |
||||
"cid": "industry-3,purpose-5", |
||||
"tag": null, |
||||
"pic": "com/fr/design/mainframe/alphafine/images/local_template2.png", |
||||
"price": 0, |
||||
"fileLoca": null, |
||||
"text": "<p>当前库存管理面临的现状:库存成本劣势明显、库存周转差距较大。如果对此没有合理有效的解决方案,往往会导致企业库存越来越混乱,经营、资金、成本等问题丛生。</p><p>库存管理解决方案从:“盘”、“析”、“管”三个方向开展,彻底解决库存量大、结构复杂、管理混乱等常见问题</p>", |
||||
"description": "库存管理解决方案即从:“盘”、“析”、“管”三个方向开展,对导致库存管理问题的原因逐一击破。", |
||||
"updateTime": "2022-05-05T03:53:55.000Z", |
||||
"downloadTimes": 816, |
||||
"state": 1, |
||||
"designVersion": "10.0", |
||||
"involvePlugins": null, |
||||
"uploadTime": "2022-05-05T03:53:55.000Z", |
||||
"style": null, |
||||
"link": "template", |
||||
"sellerName": "Victoria.", |
||||
"pluginName": [], |
||||
"pkgsize": 11, |
||||
"material": "", |
||||
"tagsName":"制造加工,库存" |
||||
}, |
||||
{ |
||||
"id": 20000581, |
||||
"name": "采购场景解决方案", |
||||
"uuid": "7994b01f-5069-4554-83cf-9d3506e30767", |
||||
"vendor": "Victoria.", |
||||
"sellerId": 202, |
||||
"cid": "industry-3,purpose-3", |
||||
"tag": null, |
||||
"pic": "com/fr/design/mainframe/alphafine/images/local_template3.png", |
||||
"price": 0, |
||||
"fileLoca": null, |
||||
"text": "<p>采购场景方案具体细节可参考:<a href=\"https://help.fanruan.com/dvg/doc-view-151.html\" target=\"_self\">可视化指南-采购场景应用</a><br/></p>", |
||||
"description": "采购解决方案采用:“自上而下”的分析思路,针对采购相关的不同角色层级及其不同角度发数据分析需求,产出不同的内容", |
||||
"updateTime": "2022-03-10T03:50:17.000Z", |
||||
"downloadTimes": 2353, |
||||
"state": 1, |
||||
"designVersion": "10.0", |
||||
"involvePlugins": null, |
||||
"uploadTime": "2022-03-10T03:50:18.000Z", |
||||
"style": null, |
||||
"link": "template", |
||||
"sellerName": "Victoria.", |
||||
"pluginName": [], |
||||
"pkgsize": 6, |
||||
"material": "", |
||||
"tagsName": "制造加工,采购" |
||||
}, |
||||
{ |
||||
"id": 20000747, |
||||
"name": "费用预算系统解决方案", |
||||
"uuid": "0776533469c3401a8da78706856b6b02", |
||||
"vendor": "finereport", |
||||
"sellerId": 1, |
||||
"cid": "template_type-1,terminal-1,industry-13,purpose-1", |
||||
"tag": null, |
||||
"pic": "com/fr/design/mainframe/alphafine/images/local_template4.png", |
||||
"price": 0, |
||||
"fileLoca": "费用预算系统解决方案.zip", |
||||
"text": "<p>模板中使用了数据查询数据集,如果要在本地打开,需要在自己的数据库中建表,并修改数据连接。</p><p>1)建表语句见附件文件夹:SQL语句与表。</p><p><br>2)数据连接默认为 FRDemo,如果用户表所在的数据库连接不是 FRDemo,将模板中数据连接修改为用户默认的数据连接。</p><p><br>3)模板中 SQL 语句为 MYSQL 数据库,若用户使用中数据库语句不匹配,可对应修改为匹配的语句。</p><p><br>4)模板中有大量的超链接,用户存在本地后,预览时注意修改超链接。</p><p><br>5)费用预算系统的平台沿用人事管理系统的平台配置,如果要在本地体验效果,可先下载人事管理系统部署:</span><a href=\"https://help.fanruan.com/dvg/doc-view-6.html?source=0&from=demoshop\" target=\"_blank\" style=\"text-decoration: underline; color: rgb(28, 133, 231); font-family: arial, helvetica, sans-serif;\"><span style=\"font-family: arial, helvetica, sans-serif;\">人事管理系统</span></a><span style=\"font-family: arial, helvetica, sans-serif;\"><br></p><p>模板中图标等背景样式见背景附件。 如果数据替换麻烦,可直接使用内置数据集模板,但内置模板只提供了样式,控件、数据等不能联动。如何制作,可查看帮助文档:</span><a href=\"https://help.fanruan.com/dvg/doc-view-124.html?source=0&from=demoshop\" target=\"_blank\" style=\"text-decoration: underline; color: rgb(28, 133, 231); font-family: arial, helvetica, sans-serif;\"><span style=\"font-family: arial, helvetica, sans-serif;\">费用预算系统</span></a><span style=\"font-family: arial, helvetica, sans-serif;\"></p>", |
||||
"description": "费用预算系统是通过 FineReport 填报功能和平台功能结合实现的审批流程系统。能够在线采集数据,提供标准数据模板,解决不同部门、子公司之间数据不统一的问题,\n实现记录预算审批与更改的操作,审批与更改记录留痕等。\n详细内容可查看帮助文档:</span><a href=\"https://help.fanruan.com/dvg/doc-view-124.html?source=0&from=demoshop\" target=\"_blank\" style=\"text-decoration: underline; color: rgb(28, 133, 231); font-family: arial, helvetica, sans-serif;\"><span style=\"font-family: arial, helvetica, sans-serif;\">费用预算系统</span></a><span style=\"font-family: arial, helvetica, sans-serif;\">", |
||||
"updateTime": "2022-05-15T05:34:45.000Z", |
||||
"downloadTimes": 141, |
||||
"state": 1, |
||||
"designVersion": "10.0", |
||||
"involvePlugins": null, |
||||
"uploadTime": "2022-05-15T05:34:45.000Z", |
||||
"style": "简约清新", |
||||
"link": "template", |
||||
"sellerName": "finereport", |
||||
"pluginName": [], |
||||
"pkgsize": 1, |
||||
"material": null, |
||||
"tagsName": "IT互联网,财务" |
||||
} |
||||
] |
@ -0,0 +1 @@
|
||||
defaultKey=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCtsz62CPSWXZE/IYZRiAuTSZkw |
@ -0,0 +1 @@
|
||||
defaultKey=1WOwer8+JFktK0uKLAUuQoBr+UjAMFtRA8W7JgKMDwZy/2liEAiXEOSPU/hrdV8D |