forwardyy
3 years ago
24 changed files with 3755 additions and 0 deletions
@ -0,0 +1,129 @@
|
||||
|
||||
apply plugin: 'java' |
||||
|
||||
[compileJava,compileTestJava]*.options*.encoding = 'UTF-8' |
||||
|
||||
ext { |
||||
/** |
||||
* 项目中依赖的jar的路径 |
||||
* 1.如果依赖的jar需要打包到zip中,放置在lib根目录下 |
||||
* 2.如果依赖的jar仅仅是编译时需要,防止在lib下子目录下即可 |
||||
*/ |
||||
libPath = "$projectDir/../webroot/WEB-INF/lib" |
||||
|
||||
/** |
||||
* 是否对插件的class进行加密保护,防止反编译 |
||||
*/ |
||||
guard = true |
||||
|
||||
def pluginInfo = getPluginInfo() |
||||
pluginPre = "fine-plugin" |
||||
pluginName = pluginInfo.id |
||||
pluginVersion = pluginInfo.version |
||||
|
||||
outputPath = "$projectDir/../webroot/WEB-INF/plugins/plugin-" + pluginName + "-1.0/classes" |
||||
outputXmlPath = "$projectDir/../webroot/WEB-INF/plugins/plugin-" + pluginName + "-1.0" |
||||
} |
||||
|
||||
group = 'com.fr.plugin' |
||||
version = '10.0' |
||||
sourceCompatibility = '8' |
||||
|
||||
sourceSets { |
||||
main { |
||||
java.outputDir = file(outputPath) |
||||
output.resourcesDir = file(outputPath) |
||||
} |
||||
} |
||||
|
||||
ant.importBuild("encrypt.xml") |
||||
//定义ant变量 |
||||
ant.projectDir = projectDir |
||||
ant.references["compile.classpath"] = ant.path { |
||||
fileset(dir: libPath, includes: '**/*.jar') |
||||
fileset(dir: ".",includes:"**/*.jar" ) |
||||
} |
||||
|
||||
classes.dependsOn('clean') |
||||
|
||||
task copyFiles(type: Copy,dependsOn: 'classes'){ |
||||
from outputPath |
||||
into "$projectDir/classes" |
||||
} |
||||
task copyXmlFiles(type: Copy){ |
||||
from "$projectDir/plugin.xml" |
||||
into file("$outputXmlPath") |
||||
} |
||||
task preJar(type:Copy,dependsOn: guard ? 'compile_encrypt_javas' : 'compile_plain_javas'){ |
||||
from "$projectDir/classes" |
||||
into "$projectDir/transform-classes" |
||||
include "**/*.*" |
||||
} |
||||
jar.dependsOn("preJar") |
||||
classes.dependsOn("copyXmlFiles") |
||||
|
||||
task makeJar(type: Jar,dependsOn: preJar){ |
||||
from fileTree(dir: "$projectDir/transform-classes") |
||||
baseName pluginPre |
||||
appendix pluginName |
||||
version pluginVersion |
||||
destinationDir = file("$buildDir/libs") |
||||
|
||||
doLast(){ |
||||
delete file("$projectDir/classes") |
||||
delete file("$projectDir/transform-classes") |
||||
} |
||||
} |
||||
|
||||
task copyFile(type: Copy,dependsOn: ["makeJar"]){ |
||||
from "$buildDir/libs" |
||||
from("$projectDir/lib") { |
||||
include "*.jar" |
||||
} |
||||
from "$projectDir/plugin.xml" |
||||
into file("$buildDir/temp/plugin") |
||||
} |
||||
|
||||
task zip(type:Zip,dependsOn:["copyFile"]){ |
||||
from "$buildDir/temp/plugin" |
||||
destinationDir file("$buildDir/install") |
||||
baseName pluginPre |
||||
appendix pluginName |
||||
version pluginVersion |
||||
} |
||||
|
||||
//控制build时包含哪些文件,排除哪些文件 |
||||
processResources { |
||||
// exclude everything |
||||
// 用*.css没效果 |
||||
// exclude '**/*.css' |
||||
// except this file |
||||
// include 'xx.xml' |
||||
} |
||||
|
||||
/*读取plugin.xml中的version*/ |
||||
def getPluginInfo(){ |
||||
def xmlFile = file("plugin.xml") |
||||
if (!xmlFile.exists()) { |
||||
return ["id":"none", "version":"1.0.0"] |
||||
} |
||||
def plugin = new XmlParser().parse(xmlFile) |
||||
def version = plugin.version[0].text() |
||||
def id = plugin.id[0].text() |
||||
return ["id":id,"version":version] |
||||
} |
||||
|
||||
repositories { |
||||
mavenLocal() |
||||
maven { |
||||
url = uri('http://mvn.finedevelop.com/repository/maven-public/') |
||||
} |
||||
} |
||||
|
||||
dependencies { |
||||
//使用本地jar |
||||
implementation fileTree(dir: 'lib', include: ['**/*.jar']) |
||||
implementation fileTree(dir: libPath, include: ['**/*.jar']) |
||||
} |
||||
|
||||
|
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
||||
<project> |
||||
<target name="compile_encrypt_javas" depends="copyFiles"> |
||||
<echo message="加密文件"/> |
||||
<echo message="${projectDir}"/> |
||||
<taskdef name="pretreatment" classname="com.fr.plugin.pack.PluginPretreatmentTask"> |
||||
<classpath refid="compile.classpath"/> |
||||
</taskdef> |
||||
<pretreatment baseDir="${projectDir}"/> |
||||
</target> |
||||
<target name="compile_plain_javas" depends="copyFiles"> |
||||
</target> |
||||
</project> |
Binary file not shown.
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><plugin> |
||||
<id>com.fr.plugin.sqy.kj.calendar</id> |
||||
<name><![CDATA[ 农历日历 ]]></name> |
||||
<active>yes</active> |
||||
<version>1.1.1</version> |
||||
<env-version>10.0</env-version> |
||||
<vendor>yy</vendor> |
||||
<jartime>2021-05-26</jartime> |
||||
<description><![CDATA[ 农历日历 ]]></description> |
||||
<change-notes> |
||||
<![CDATA[2021-11-24 初始化工程]]> |
||||
</change-notes> |
||||
<main-package>com.fr.plugin.sqy</main-package> |
||||
<function-recorder class="com.fr.plugin.sqy.kj.calendar.JsFileHandler"/> |
||||
|
||||
<extra-core> |
||||
<!--插件注入国际化--> |
||||
<LocaleFinder class="com.fr.plugin.sqy.kj.calendar.LocaleFinder"/> |
||||
</extra-core> |
||||
|
||||
<extra-designer> |
||||
<CellWidgetOptionProvider class="com.fr.plugin.sqy.kj.calendar.cell.CellWidgetOptionProvider"/> |
||||
<ParameterWidgetOptionProvider class="com.fr.plugin.sqy.kj.calendar.parameter.ParameterWidgetOptionProvider"/> |
||||
<FormWidgetOptionProvider class="com.fr.plugin.sqy.kj.calendar.form.FormWidgetOptionProvider"/> |
||||
</extra-designer> |
||||
|
||||
<extra-report> |
||||
<JavaScriptFileHandler class="com.fr.plugin.sqy.kj.calendar.JsFileHandler"/> |
||||
<CssFileHandler class="com.fr.plugin.sqy.kj.calendar.CssFileHandler"/> |
||||
</extra-report> |
||||
|
||||
<extra-form> |
||||
<JavaScriptFileHandler class="com.fr.plugin.sqy.kj.calendar.JsFileHandler"/> |
||||
<CssFileHandler class="com.fr.plugin.sqy.kj.calendar.CssFileHandler"/> |
||||
</extra-form> |
||||
</plugin> |
@ -0,0 +1,12 @@
|
||||
package com.fr.plugin.sqy.kj.calendar; |
||||
|
||||
import com.fr.stable.fun.impl.AbstractCssFileHandler; |
||||
|
||||
public class CssFileHandler extends AbstractCssFileHandler { |
||||
@Override |
||||
public String[] pathsForFiles() { |
||||
return new String[]{ |
||||
"com/fr/plugin/sqy/kj/calendar/css/plugin_main.css" |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,17 @@
|
||||
package com.fr.plugin.sqy.kj.calendar; |
||||
|
||||
import com.fr.plugin.transform.ExecuteFunctionRecord; |
||||
import com.fr.plugin.transform.FunctionRecorder; |
||||
import com.fr.stable.fun.impl.AbstractJavaScriptFileHandler; |
||||
|
||||
@FunctionRecorder |
||||
public class FormJsFileHandler extends AbstractJavaScriptFileHandler{ |
||||
|
||||
@ExecuteFunctionRecord |
||||
@Override |
||||
public String[] pathsForFiles() { |
||||
return new String[]{ |
||||
"com/fr/plugin/sqy/kj/calendar/js/plugin_form_main.js" |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,17 @@
|
||||
package com.fr.plugin.sqy.kj.calendar; |
||||
|
||||
import com.fr.plugin.transform.ExecuteFunctionRecord; |
||||
import com.fr.plugin.transform.FunctionRecorder; |
||||
import com.fr.stable.fun.impl.AbstractJavaScriptFileHandler; |
||||
|
||||
@FunctionRecorder |
||||
public class JsFileHandler extends AbstractJavaScriptFileHandler{ |
||||
|
||||
@ExecuteFunctionRecord |
||||
@Override |
||||
public String[] pathsForFiles() { |
||||
return new String[]{ |
||||
"com/fr/plugin/sqy/kj/calendar/js/plugin_main.js" |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,17 @@
|
||||
package com.fr.plugin.sqy.kj.calendar; |
||||
|
||||
import com.fr.stable.fun.impl.AbstractLocaleFinder; |
||||
|
||||
public class LocaleFinder extends AbstractLocaleFinder { |
||||
private static final int CURRENT_LEVEL = 1; |
||||
|
||||
@Override |
||||
public int currentAPILevel() { |
||||
return CURRENT_LEVEL; |
||||
} |
||||
|
||||
@Override |
||||
public String find() { |
||||
return "com/fr/plugin/sqy/kj/calendar/locale/plugin"; |
||||
} |
||||
} |
@ -0,0 +1,10 @@
|
||||
package com.fr.plugin.sqy.kj.calendar; |
||||
|
||||
public class PluginConstants { |
||||
public static final String PLUGIN_ID = "com.fr.plugin.sqy.kj.calendar"; |
||||
public static final String KJ_CELL_ID = "sqy_cell_calendar"; |
||||
public static final String KJ_PARAMETER_ID = "sqy_parameter_calendar"; |
||||
public static final String KJ_FORM_ID = "sqy_form_calendar"; |
||||
public static final String START_DATE_NAME = "\u8d77\u59cb\u65e5\u671f"; |
||||
public static final String KJ_NAME = "\u519c\u5386\u65e5\u671f\u63a7\u4ef6"; |
||||
} |
@ -0,0 +1,31 @@
|
||||
package com.fr.plugin.sqy.kj.calendar.cell; |
||||
|
||||
import com.fanruan.api.design.DesignKit; |
||||
import com.fr.design.beans.BasicBeanPane; |
||||
import com.fr.design.fun.impl.AbstractCellWidgetOptionProvider; |
||||
import com.fr.form.ui.Widget; |
||||
import com.fr.plugin.sqy.kj.calendar.PluginConstants; |
||||
import com.fr.plugin.sqy.kj.calendar.pane.CalendarPane; |
||||
import com.fr.plugin.sqy.kj.calendar.widget.CellCalendarWidget; |
||||
|
||||
public class CellWidgetOptionProvider extends AbstractCellWidgetOptionProvider { |
||||
@Override |
||||
public Class<? extends Widget> classForWidget() { |
||||
return CellCalendarWidget.class; |
||||
} |
||||
|
||||
@Override |
||||
public Class<? extends BasicBeanPane<? extends Widget>> appearanceForWidget() { |
||||
return CalendarPane.class; |
||||
} |
||||
|
||||
@Override |
||||
public String iconPathForWidget() { |
||||
return "com/fr/plugin/sqy/kj/calendar/log/calendar.png"; |
||||
} |
||||
|
||||
@Override |
||||
public String nameForWidget() { |
||||
return PluginConstants.KJ_NAME; |
||||
} |
||||
} |
@ -0,0 +1,29 @@
|
||||
package com.fr.plugin.sqy.kj.calendar.form; |
||||
|
||||
import com.fanruan.api.design.DesignKit; |
||||
import com.fr.design.fun.impl.AbstractFormWidgetOptionProvider; |
||||
import com.fr.form.ui.Widget; |
||||
import com.fr.plugin.sqy.kj.calendar.PluginConstants; |
||||
import com.fr.plugin.sqy.kj.calendar.widget.FormCalendarWidget; |
||||
|
||||
public class FormWidgetOptionProvider extends AbstractFormWidgetOptionProvider { |
||||
@Override |
||||
public Class<? extends Widget> classForWidget() { |
||||
return FormCalendarWidget.class; |
||||
} |
||||
|
||||
@Override |
||||
public Class<?> appearanceForWidget() { |
||||
return FormXDateEditor.class; |
||||
} |
||||
|
||||
@Override |
||||
public String iconPathForWidget() { |
||||
return "com/fr/plugin/sqy/kj/calendar/log/calendar.png"; |
||||
} |
||||
|
||||
@Override |
||||
public String nameForWidget() { |
||||
return PluginConstants.KJ_NAME; |
||||
} |
||||
} |
@ -0,0 +1,121 @@
|
||||
package com.fr.plugin.sqy.kj.calendar.form; |
||||
|
||||
|
||||
import com.fanruan.api.design.DesignKit; |
||||
import com.fr.design.designer.creator.CRPropertyDescriptor; |
||||
import com.fr.design.designer.creator.XDirectWriteEditor; |
||||
import com.fr.design.gui.itextfield.UITextField; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.layout.FRGUIPaneFactory; |
||||
import com.fr.design.mainframe.widget.editors.DateFormatEditor; |
||||
import com.fr.design.mainframe.widget.editors.DateRangeEditor; |
||||
import com.fr.design.mainframe.widget.editors.WidgetValueEditor; |
||||
import com.fr.design.mainframe.widget.renderer.DateCellRenderer; |
||||
import com.fr.form.ui.DateEditor; |
||||
import com.fr.form.ui.WidgetValue; |
||||
import com.fr.form.ui.concept.data.ValueInitializer; |
||||
import com.fr.general.DateUtils; |
||||
import com.fr.plugin.sqy.kj.calendar.PluginConstants; |
||||
import com.fr.plugin.sqy.kj.calendar.widget.FormCalendarWidget; |
||||
import com.fr.stable.ArrayUtils; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.stable.core.PropertyChangeAdapter; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
import java.beans.IntrospectionException; |
||||
import java.util.Date; |
||||
|
||||
public class FormXDateEditor extends XDirectWriteEditor { |
||||
private UITextField textField; |
||||
private LimpidButton btn; |
||||
|
||||
public FormXDateEditor(FormCalendarWidget widget, Dimension size) { |
||||
super(widget, size); |
||||
} |
||||
|
||||
@Override |
||||
public String getIconPath() { |
||||
return "com/fr/plugin/sqy/kj/calendar/log/calendar.png"; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public CRPropertyDescriptor[] supportedDescriptor() throws IntrospectionException { |
||||
return (CRPropertyDescriptor[]) |
||||
((CRPropertyDescriptor[])ArrayUtils |
||||
.addAll(super.supportedDescriptor() |
||||
, new CRPropertyDescriptor[]{ |
||||
(new CRPropertyDescriptor("widgetValue", this.data.getClass())) |
||||
.setI18NName(Toolkit.i18nText("Fine-Design_Form_Widget_Value")) |
||||
.setEditorClass(WidgetValueEditor.class) |
||||
.putKeyValue("category", "Fine-Design_Basic_Advanced") |
||||
.setPropertyChangeListener(new PropertyChangeAdapter() { |
||||
public void propertyChange() { |
||||
FormXDateEditor.this.initFieldText(); |
||||
} |
||||
}) |
||||
,(new CRPropertyDescriptor("formatText", this.data.getClass())) |
||||
.setI18NName(Toolkit.i18nText("Fine-Design_Report_Engine_Format")) |
||||
.setEditorClass(this.formatClass()) |
||||
.setRendererClass(DateCellRenderer.class) |
||||
.putKeyValue("category", "Fine-Design_Basic_Advanced") |
||||
, (new CRPropertyDescriptor("startDate", this.data.getClass())) |
||||
.setI18NName(PluginConstants.START_DATE_NAME) |
||||
.putKeyValue("category", "Fine-Design_Basic_Advanced") |
||||
.setEditorClass(DateRangeEditor.class) |
||||
, (new CRPropertyDescriptor("endDate", this.data.getClass())) |
||||
.setI18NName(Toolkit.i18nText("Fine-Design_Report_FS_End_Date")) |
||||
.putKeyValue("category", "Fine-Design_Basic_Advanced") |
||||
.setEditorClass(DateRangeEditor.class) |
||||
, (new CRPropertyDescriptor("waterMark", this.data.getClass())) |
||||
.setI18NName(Toolkit.i18nText("Fine-Design_Form_WaterMark")) |
||||
.putKeyValue("category", "Fine-Design_Basic_Advanced") |
||||
, (new CRPropertyDescriptor("returnDate", this.data.getClass())) |
||||
.setI18NName(Toolkit.i18nText("Fine-Design_Form_Return_Date")) |
||||
.putKeyValue("category", "Fine-Design_Basic_Advanced") |
||||
})); |
||||
} |
||||
|
||||
protected Class formatClass() { |
||||
return DateFormatEditor.class; |
||||
} |
||||
|
||||
private void initFieldText() { |
||||
DateEditor var1 = (DateEditor)this.data; |
||||
if (var1.getWidgetValue() != null) { |
||||
ValueInitializer var2 = var1.getWidgetValue(); |
||||
String var3 = var2.toString(); |
||||
Object var4 = var2.getValue(); |
||||
String var5 = var1.getFormatText(); |
||||
if (var4 instanceof Date) { |
||||
var3 = DateUtils.getDate2Str(var5, (Date)var4); |
||||
} |
||||
|
||||
if (StringUtils.isEmpty(var3)) { |
||||
var3 = DateUtils.getDate2Str(var5, new Date()); |
||||
var1.setWidgetValue(new WidgetValue(new Date())); |
||||
} |
||||
|
||||
this.textField.setText(var3); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected JComponent initEditor() { |
||||
if (this.editor == null) { |
||||
this.editor = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
this.editor.add(this.textField = new UITextField(5), "Center"); |
||||
this.btn = new LimpidButton("", this.getIconPath(), this.toData().isVisible() ? 1.0F : 0.4F); |
||||
this.btn.setPreferredSize(new Dimension(21, 21)); |
||||
this.editor.add(this.btn, "East"); |
||||
this.textField.setOpaque(false); |
||||
this.editor.setBackground(Color.WHITE); |
||||
} |
||||
return this.editor; |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,206 @@
|
||||
package com.fr.plugin.sqy.kj.calendar.pane; |
||||
|
||||
import com.fr.base.FRContext; |
||||
import com.fr.data.core.FormatField; |
||||
import com.fr.design.border.UIRoundedBorder; |
||||
import com.fr.design.constants.UIConstants; |
||||
import com.fr.design.gui.ibutton.UIButtonGroup; |
||||
import com.fr.design.gui.icombobox.UIComboBox; |
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.layout.FRGUIPaneFactory; |
||||
import com.fr.design.layout.TableLayoutHelper; |
||||
import com.fr.design.widget.component.DateValuePane; |
||||
import com.fr.design.widget.component.UIComboBoxNoArrow; |
||||
import com.fr.design.widget.ui.DirectWriteEditorDefinePane; |
||||
import com.fr.plugin.sqy.kj.calendar.widget.CellCalendarWidget; |
||||
import com.fr.stable.ArrayUtils; |
||||
|
||||
import javax.swing.*; |
||||
import javax.swing.border.TitledBorder; |
||||
import javax.swing.event.ChangeEvent; |
||||
import javax.swing.event.ChangeListener; |
||||
import java.awt.*; |
||||
import java.awt.event.ActionEvent; |
||||
import java.awt.event.ActionListener; |
||||
import java.text.SimpleDateFormat; |
||||
import java.util.Date; |
||||
|
||||
public class CalendarPane extends DirectWriteEditorDefinePane<CellCalendarWidget> { |
||||
private UIButtonGroup returnTypeComboBox; |
||||
private DateValuePane startDv; |
||||
private DateValuePane endDv; |
||||
private UIComboBox currentFormatComboBox; |
||||
private com.fr.design.gui.ilable.UILabel currentSamplelabel; |
||||
private UIButtonGroup fomatHeadGroup; |
||||
private static final int SAMPLE_LABEL_PADDING = 4; |
||||
|
||||
public CalendarPane() { |
||||
} |
||||
|
||||
protected String title4PopupWindow() { |
||||
return "Date"; |
||||
} |
||||
|
||||
protected JPanel setSecondContentPane() { |
||||
this.returnTypeComboBox = new UIButtonGroup(new String[]{Toolkit.i18nText("Fine-Design_Basic_Date"), Toolkit.i18nText("Fine-Design_Basic_String")}); |
||||
JPanel var1 = this.createFormatHead(); |
||||
this.startDv = new DateValuePane(); |
||||
this.endDv = new DateValuePane(); |
||||
double var2 = -1.0D; |
||||
double var4 = -2.0D; |
||||
com.fr.design.gui.ilable.UILabel var6 = new com.fr.design.gui.ilable.UILabel(Toolkit.i18nText("Fine-Design_Report_Engine_Format")); |
||||
var6.setVerticalAlignment(1); |
||||
com.fr.design.gui.ilable.UILabel var7 = new com.fr.design.gui.ilable.UILabel(Toolkit.i18nText("Fine-Design_Report_FS_Start_Date")); |
||||
var7.setVerticalAlignment(1); |
||||
com.fr.design.gui.ilable.UILabel var8 = new com.fr.design.gui.ilable.UILabel(Toolkit.i18nText("Fine-Design_Report_FS_End_Date")); |
||||
var8.setVerticalAlignment(1); |
||||
Component[][] var9 = new Component[][]{{var6, var1}, {var7, this.startDv}, {var8, this.endDv}, {this.waterMarkDictPane, null}, {new com.fr.design.gui.ilable.UILabel(Toolkit.i18nText("Fine-Design_Basic_Widget_Date_Selector_Return_Type")), this.returnTypeComboBox}}; |
||||
double[] var10 = new double[]{var4, var4, var4, var4, var4, var4, var4}; |
||||
double[] var11 = new double[]{var4, var2}; |
||||
int[][] var12 = new int[][]{{1, 3}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}; |
||||
JPanel var13 = TableLayoutHelper.createGapTableLayoutPane(var9, var10, var11, var12, 13.0D, 10.0D); |
||||
return var13; |
||||
} |
||||
|
||||
private JPanel createFormatPane(UIComboBox var1, com.fr.design.gui.ilable.UILabel var2) { |
||||
JPanel var3 = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
TitledBorder var4 = new TitledBorder(new UIRoundedBorder(UIConstants.LINE_COLOR, 1, 5), Toolkit.i18nText("Fine-Design_Report_Base_StyleFormat_Sample"), 4, 2, this.getFont(), UIConstants.LINE_COLOR); |
||||
var3.setBorder(var4); |
||||
JPanel var5 = new JPanel(new BorderLayout()); |
||||
var5.setBorder(BorderFactory.createEmptyBorder(0, 4, 4, 4)); |
||||
var5.add(var2, "Center"); |
||||
var3.add(var5, "Center"); |
||||
JPanel var6 = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
var6.add(var3, "North"); |
||||
var6.add(var1, "Center"); |
||||
return var6; |
||||
} |
||||
|
||||
private com.fr.design.gui.ilable.UILabel createSamplePane() { |
||||
com.fr.design.gui.ilable.UILabel var1 = new com.fr.design.gui.ilable.UILabel("") { |
||||
public void setText(String var1) { |
||||
super.setText("<html>" + var1 + "</html>"); |
||||
} |
||||
}; |
||||
var1.setHorizontalAlignment(0); |
||||
var1.setFont(FRContext.getDefaultValues().getFRFont()); |
||||
return var1; |
||||
} |
||||
|
||||
private JPanel createFormatHead() { |
||||
String[] var1 = FormatField.getInstance().getFormatArray(5); |
||||
String[] var2 = FormatField.getInstance().getFormatArray(6); |
||||
final UIComboBoxNoArrow var3 = new UIComboBoxNoArrow(var1); |
||||
final UIComboBoxNoArrow var4 = new UIComboBoxNoArrow(var2); |
||||
var3.addActionListener(new ActionListener() { |
||||
public void actionPerformed(ActionEvent var1) { |
||||
CalendarPane.this.refreshPreviewLabel(); |
||||
} |
||||
}); |
||||
var4.addActionListener(new ActionListener() { |
||||
public void actionPerformed(ActionEvent var1) { |
||||
CalendarPane.this.refreshPreviewLabel(); |
||||
} |
||||
}); |
||||
final com.fr.design.gui.ilable.UILabel var5 = this.createSamplePane(); |
||||
final UILabel var6 = this.createSamplePane(); |
||||
JPanel var7 = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
final CardLayout var8 = new CardLayout(); |
||||
final JPanel var9 = new JPanel(var8); |
||||
JPanel var10 = this.createFormatPane(var3, var5); |
||||
JPanel var11 = this.createFormatPane(var4, var6); |
||||
var9.add(var10, Toolkit.i18nText("Fine-Design_Report_StyleFormat_Date")); |
||||
var9.add(var11, Toolkit.i18nText("Fine-Design_Report_StyleFormat_Time")); |
||||
final String[] var12 = new String[]{Toolkit.i18nText("Fine-Design_Report_StyleFormat_Date"), Toolkit.i18nText("Fine-Design_Report_StyleFormat_Time")}; |
||||
this.fomatHeadGroup = new UIButtonGroup(new String[]{Toolkit.i18nText("Fine-Design_Report_StyleFormat_Date"), Toolkit.i18nText("Fine-Design_Report_StyleFormat_Time")}); |
||||
this.fomatHeadGroup.addChangeListener(new ChangeListener() { |
||||
public void stateChanged(ChangeEvent var1) { |
||||
int var2 = CalendarPane.this.fomatHeadGroup.getSelectedIndex(); |
||||
var8.show(var9, var12[var2]); |
||||
if (var2 == 0) { |
||||
CalendarPane.this.currentFormatComboBox = var3; |
||||
CalendarPane.this.currentSamplelabel = var5; |
||||
} else { |
||||
CalendarPane.this.currentFormatComboBox = var4; |
||||
CalendarPane.this.currentSamplelabel = var6; |
||||
} |
||||
|
||||
CalendarPane.this.refreshPreviewLabel(); |
||||
} |
||||
}); |
||||
var7.add(this.fomatHeadGroup, "North"); |
||||
var7.add(var9, "Center"); |
||||
return var7; |
||||
} |
||||
|
||||
private void refreshPreviewLabel() { |
||||
String var1 = (String)this.currentFormatComboBox.getSelectedItem(); |
||||
if (var1 != null && var1.length() > 0) { |
||||
try { |
||||
SimpleDateFormat var2 = new SimpleDateFormat(var1); |
||||
String var3 = var2.format(new Date()); |
||||
Color var4 = Color.black; |
||||
if (!ArrayUtils.contains(FormatField.getInstance().getDateFormatArray(), var1)) { |
||||
var3 = var3 + " " + Toolkit.i18nText("Fine-Design_Basic_DateFormat_Custom_Warning"); |
||||
var4 = Color.red; |
||||
} |
||||
|
||||
this.currentSamplelabel.setText(var3); |
||||
this.currentSamplelabel.setForeground(var4); |
||||
} catch (Exception var5) { |
||||
this.currentSamplelabel.setForeground(Color.red); |
||||
this.currentSamplelabel.setText(var5.getMessage()); |
||||
} |
||||
} else { |
||||
this.currentSamplelabel.setText((new Date()).toString()); |
||||
} |
||||
|
||||
} |
||||
|
||||
private int getDateType(CellCalendarWidget var1) { |
||||
String[] var2 = FormatField.getInstance().getFormatArray(6); |
||||
if (var1 == null) { |
||||
return 0; |
||||
} else { |
||||
String var3 = var1.getFormatText(); |
||||
return ArrayUtils.contains(var2, var3) ? 1 : 0; |
||||
} |
||||
} |
||||
|
||||
protected void populateSubDirectWriteEditorBean(CellCalendarWidget var1) { |
||||
String var2 = var1.getFormatText(); |
||||
this.fomatHeadGroup.setSelectedIndex(this.getDateType(var1)); |
||||
this.fomatHeadGroup.populateBean(); |
||||
this.currentFormatComboBox.setSelectedItem(var2); |
||||
this.returnTypeComboBox.setSelectedIndex(var1.isReturnDate() ? 0 : 1); |
||||
this.startDv.populate(var1.getStartDate()); |
||||
this.endDv.populate(var1.getEndDate()); |
||||
} |
||||
|
||||
protected CellCalendarWidget updateSubDirectWriteEditorBean() { |
||||
CellCalendarWidget var1 = new CellCalendarWidget(); |
||||
var1.setFormatText(this.getSimpleDateFormat().toPattern()); |
||||
var1.setReturnDate(this.returnTypeComboBox.getSelectedIndex() == 0); |
||||
var1.setStartDate(this.startDv.update()); |
||||
var1.setEndDate(this.endDv.update()); |
||||
return var1; |
||||
} |
||||
|
||||
private SimpleDateFormat getSimpleDateFormat() { |
||||
String var1 = (String)this.currentFormatComboBox.getSelectedItem(); |
||||
SimpleDateFormat var2; |
||||
if (var1 != null && var1.length() > 0) { |
||||
try { |
||||
var2 = new SimpleDateFormat(var1); |
||||
this.currentSamplelabel.setText(var2.format(new Date())); |
||||
} catch (Exception var4) { |
||||
var2 = new SimpleDateFormat(""); |
||||
} |
||||
} else { |
||||
var2 = new SimpleDateFormat(""); |
||||
} |
||||
|
||||
return var2; |
||||
} |
||||
} |
@ -0,0 +1,29 @@
|
||||
package com.fr.plugin.sqy.kj.calendar.parameter; |
||||
|
||||
import com.fanruan.api.design.DesignKit; |
||||
import com.fr.design.fun.impl.AbstractParameterWidgetOptionProvider; |
||||
import com.fr.form.ui.Widget; |
||||
import com.fr.plugin.sqy.kj.calendar.PluginConstants; |
||||
import com.fr.plugin.sqy.kj.calendar.widget.ParameterCalendarWidget; |
||||
|
||||
public class ParameterWidgetOptionProvider extends AbstractParameterWidgetOptionProvider { |
||||
@Override |
||||
public Class<? extends Widget> classForWidget() { |
||||
return ParameterCalendarWidget.class; |
||||
} |
||||
|
||||
@Override |
||||
public Class<?> appearanceForWidget() { |
||||
return ParameterXDateEditor.class; |
||||
} |
||||
|
||||
@Override |
||||
public String iconPathForWidget() { |
||||
return "com/fr/plugin/sqy/kj/calendar/log/calendar.png"; |
||||
} |
||||
|
||||
@Override |
||||
public String nameForWidget() { |
||||
return PluginConstants.KJ_NAME; |
||||
} |
||||
} |
@ -0,0 +1,121 @@
|
||||
package com.fr.plugin.sqy.kj.calendar.parameter; |
||||
|
||||
|
||||
import com.fanruan.api.design.DesignKit; |
||||
import com.fr.design.designer.creator.CRPropertyDescriptor; |
||||
import com.fr.design.designer.creator.XDirectWriteEditor; |
||||
import com.fr.design.gui.itextfield.UITextField; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.layout.FRGUIPaneFactory; |
||||
import com.fr.design.mainframe.widget.editors.DateFormatEditor; |
||||
import com.fr.design.mainframe.widget.editors.DateRangeEditor; |
||||
import com.fr.design.mainframe.widget.editors.WidgetValueEditor; |
||||
import com.fr.design.mainframe.widget.renderer.DateCellRenderer; |
||||
import com.fr.form.ui.DateEditor; |
||||
import com.fr.form.ui.WidgetValue; |
||||
import com.fr.form.ui.concept.data.ValueInitializer; |
||||
import com.fr.general.DateUtils; |
||||
import com.fr.plugin.sqy.kj.calendar.PluginConstants; |
||||
import com.fr.plugin.sqy.kj.calendar.widget.ParameterCalendarWidget; |
||||
import com.fr.stable.ArrayUtils; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.stable.core.PropertyChangeAdapter; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
import java.beans.IntrospectionException; |
||||
import java.util.Date; |
||||
|
||||
public class ParameterXDateEditor extends XDirectWriteEditor { |
||||
private UITextField textField; |
||||
private LimpidButton btn; |
||||
|
||||
public ParameterXDateEditor(ParameterCalendarWidget widget, Dimension size) { |
||||
super(widget, size); |
||||
} |
||||
|
||||
@Override |
||||
public String getIconPath() { |
||||
return "com/fr/plugin/sqy/kj/calendar/log/calendar.png"; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public CRPropertyDescriptor[] supportedDescriptor() throws IntrospectionException { |
||||
return (CRPropertyDescriptor[]) |
||||
((CRPropertyDescriptor[])ArrayUtils |
||||
.addAll(super.supportedDescriptor() |
||||
, new CRPropertyDescriptor[]{ |
||||
(new CRPropertyDescriptor("widgetValue", this.data.getClass())) |
||||
.setI18NName(Toolkit.i18nText("Fine-Design_Form_Widget_Value")) |
||||
.setEditorClass(WidgetValueEditor.class) |
||||
.putKeyValue("category", "Fine-Design_Basic_Advanced") |
||||
.setPropertyChangeListener(new PropertyChangeAdapter() { |
||||
public void propertyChange() { |
||||
ParameterXDateEditor.this.initFieldText(); |
||||
} |
||||
}) |
||||
,(new CRPropertyDescriptor("formatText", this.data.getClass())) |
||||
.setI18NName(Toolkit.i18nText("Fine-Design_Report_Engine_Format")) |
||||
.setEditorClass(this.formatClass()) |
||||
.setRendererClass(DateCellRenderer.class) |
||||
.putKeyValue("category", "Fine-Design_Basic_Advanced") |
||||
, (new CRPropertyDescriptor("startDate", this.data.getClass())) |
||||
.setI18NName(PluginConstants.START_DATE_NAME) |
||||
.putKeyValue("category", "Fine-Design_Basic_Advanced") |
||||
.setEditorClass(DateRangeEditor.class) |
||||
, (new CRPropertyDescriptor("endDate", this.data.getClass())) |
||||
.setI18NName(Toolkit.i18nText("Fine-Design_Report_FS_End_Date")) |
||||
.putKeyValue("category", "Fine-Design_Basic_Advanced") |
||||
.setEditorClass(DateRangeEditor.class) |
||||
, (new CRPropertyDescriptor("waterMark", this.data.getClass())) |
||||
.setI18NName(Toolkit.i18nText("Fine-Design_Form_WaterMark")) |
||||
.putKeyValue("category", "Fine-Design_Basic_Advanced") |
||||
, (new CRPropertyDescriptor("returnDate", this.data.getClass())) |
||||
.setI18NName(Toolkit.i18nText("Fine-Design_Form_Return_Date")) |
||||
.putKeyValue("category", "Fine-Design_Basic_Advanced") |
||||
})); |
||||
} |
||||
|
||||
protected Class formatClass() { |
||||
return DateFormatEditor.class; |
||||
} |
||||
|
||||
private void initFieldText() { |
||||
DateEditor var1 = (DateEditor)this.data; |
||||
if (var1.getWidgetValue() != null) { |
||||
ValueInitializer var2 = var1.getWidgetValue(); |
||||
String var3 = var2.toString(); |
||||
Object var4 = var2.getValue(); |
||||
String var5 = var1.getFormatText(); |
||||
if (var4 instanceof Date) { |
||||
var3 = DateUtils.getDate2Str(var5, (Date)var4); |
||||
} |
||||
|
||||
if (StringUtils.isEmpty(var3)) { |
||||
var3 = DateUtils.getDate2Str(var5, new Date()); |
||||
var1.setWidgetValue(new WidgetValue(new Date())); |
||||
} |
||||
|
||||
this.textField.setText(var3); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected JComponent initEditor() { |
||||
if (this.editor == null) { |
||||
this.editor = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||
this.editor.add(this.textField = new UITextField(5), "Center"); |
||||
this.btn = new LimpidButton("", this.getIconPath(), this.toData().isVisible() ? 1.0F : 0.4F); |
||||
this.btn.setPreferredSize(new Dimension(21, 21)); |
||||
this.editor.add(this.btn, "East"); |
||||
this.textField.setOpaque(false); |
||||
this.editor.setBackground(Color.WHITE); |
||||
} |
||||
return this.editor; |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,52 @@
|
||||
package com.fr.plugin.sqy.kj.calendar.widget; |
||||
|
||||
import com.fr.form.ui.DateEditor; |
||||
import com.fr.general.ComparatorUtils; |
||||
import com.fr.json.JSONException; |
||||
import com.fr.json.JSONObject; |
||||
import com.fr.plugin.sqy.kj.calendar.PluginConstants; |
||||
import com.fr.script.Calculator; |
||||
import com.fr.stable.core.NodeVisitor; |
||||
import com.fr.stable.web.Repository; |
||||
import com.fr.stable.xml.XMLPrintWriter; |
||||
import com.fr.stable.xml.XMLableReader; |
||||
|
||||
public class CellCalendarWidget extends DateEditor { |
||||
private String config1 = null; |
||||
|
||||
@Override |
||||
public String getXType() { |
||||
return PluginConstants.KJ_CELL_ID; |
||||
} |
||||
|
||||
@Override |
||||
public JSONObject createJSONConfig(Repository repository, Calculator calculator, NodeVisitor nodeVisitor) throws JSONException { |
||||
return super.createJSONConfig(repository, calculator, nodeVisitor).put("config1",config1); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void readXML(XMLableReader xmLableReader) { |
||||
super.readXML(xmLableReader); |
||||
if(xmLableReader.isChildNode()){ |
||||
String tagName = xmLableReader.getTagName(); |
||||
if(ComparatorUtils.equals(tagName,"config1")){ |
||||
this.config1 = xmLableReader.getAttrAsString("config1",""); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void writeXML(XMLPrintWriter xmlPrintWriter) { |
||||
super.writeXML(xmlPrintWriter); |
||||
xmlPrintWriter.startTAG("config1").textNode(config1).end(); |
||||
} |
||||
|
||||
public String getConfig1() { |
||||
return config1; |
||||
} |
||||
|
||||
public void setConfig1(String config1) { |
||||
this.config1 = config1; |
||||
} |
||||
} |
@ -0,0 +1,52 @@
|
||||
package com.fr.plugin.sqy.kj.calendar.widget; |
||||
|
||||
import com.fr.form.ui.DateEditor; |
||||
import com.fr.general.ComparatorUtils; |
||||
import com.fr.json.JSONException; |
||||
import com.fr.json.JSONObject; |
||||
import com.fr.plugin.sqy.kj.calendar.PluginConstants; |
||||
import com.fr.script.Calculator; |
||||
import com.fr.stable.core.NodeVisitor; |
||||
import com.fr.stable.web.Repository; |
||||
import com.fr.stable.xml.XMLPrintWriter; |
||||
import com.fr.stable.xml.XMLableReader; |
||||
|
||||
public class FormCalendarWidget extends DateEditor { |
||||
private String config1 = null; |
||||
|
||||
@Override |
||||
public String getXType() { |
||||
return PluginConstants.KJ_FORM_ID; |
||||
} |
||||
|
||||
@Override |
||||
public JSONObject createJSONConfig(Repository repository, Calculator calculator, NodeVisitor nodeVisitor) throws JSONException { |
||||
return super.createJSONConfig(repository, calculator, nodeVisitor).put("config1",config1); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void readXML(XMLableReader xmLableReader) { |
||||
super.readXML(xmLableReader); |
||||
if(xmLableReader.isChildNode()){ |
||||
String tagName = xmLableReader.getTagName(); |
||||
if(ComparatorUtils.equals(tagName,"config1")){ |
||||
this.config1 = xmLableReader.getAttrAsString("config1",""); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void writeXML(XMLPrintWriter xmlPrintWriter) { |
||||
super.writeXML(xmlPrintWriter); |
||||
xmlPrintWriter.startTAG("config1").textNode(config1).end(); |
||||
} |
||||
|
||||
public String getConfig1() { |
||||
return config1; |
||||
} |
||||
|
||||
public void setConfig1(String config1) { |
||||
this.config1 = config1; |
||||
} |
||||
} |
@ -0,0 +1,52 @@
|
||||
package com.fr.plugin.sqy.kj.calendar.widget; |
||||
|
||||
import com.fr.form.ui.DateEditor; |
||||
import com.fr.general.ComparatorUtils; |
||||
import com.fr.json.JSONException; |
||||
import com.fr.json.JSONObject; |
||||
import com.fr.plugin.sqy.kj.calendar.PluginConstants; |
||||
import com.fr.script.Calculator; |
||||
import com.fr.stable.core.NodeVisitor; |
||||
import com.fr.stable.web.Repository; |
||||
import com.fr.stable.xml.XMLPrintWriter; |
||||
import com.fr.stable.xml.XMLableReader; |
||||
|
||||
public class ParameterCalendarWidget extends DateEditor { |
||||
private String config1 = null; |
||||
|
||||
@Override |
||||
public String getXType() { |
||||
return PluginConstants.KJ_PARAMETER_ID; |
||||
} |
||||
|
||||
@Override |
||||
public JSONObject createJSONConfig(Repository repository, Calculator calculator, NodeVisitor nodeVisitor) throws JSONException { |
||||
return super.createJSONConfig(repository, calculator, nodeVisitor).put("config1",config1); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void readXML(XMLableReader xmLableReader) { |
||||
super.readXML(xmLableReader); |
||||
if(xmLableReader.isChildNode()){ |
||||
String tagName = xmLableReader.getTagName(); |
||||
if(ComparatorUtils.equals(tagName,"config1")){ |
||||
this.config1 = xmLableReader.getAttrAsString("config1",""); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void writeXML(XMLPrintWriter xmlPrintWriter) { |
||||
super.writeXML(xmlPrintWriter); |
||||
xmlPrintWriter.startTAG("config1").textNode(config1).end(); |
||||
} |
||||
|
||||
public String getConfig1() { |
||||
return config1; |
||||
} |
||||
|
||||
public void setConfig1(String config1) { |
||||
this.config1 = config1; |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
.fr-sqydatepicker{color:#000;font-family:"microsoft yahei",simsun,arial,tahoma,helvetica,sans-serif;position:absolute;box-shadow:3px 3px 4px #999;-moz-box-shadow:3px 3px 4px #999;-webkit-box-shadow:3px 3px 4px #999}.fr-sqydatepicker table{background-color:#fff;width:318px} |
||||
.fr-sqydatepicker table.dt,.fr-sqydatepicker table.mt{border:#98c0f4 1px solid;cursor:default}.fr-sqydatepicker table.dt thead tr{height:25px}.fr-sqydatepicker td{text-align:center;padding:2px;width:20px;height:20px}.fr-sqydatepicker tbody td{font-size:13px;padding:3px}.fr-sqydatepicker tbody td.day,.fr-sqydatepicker tbody td.year,.fr-sqydatepicker tbody td.month{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fr-sqydatepicker td.name{font-size:13px;font-weight:bold}.fr-sqydatepicker td.btn{font-size:18px;font-weight:bold} |
||||
.fr-sqydatepicker thead td.title{font-weight:bold;font-size:14px;text-align:center;padding:2px}.fr-sqydatepicker table.mt td{height: 24px;*height:24px;width:60px;}.fr-sqydatepicker thead tr.mainhead td{background-color:#dfecfb;border-bottom:1px solid #98c0f4}.fr-sqydatepicker thead tr.mainhead td.hover,.fr-sqydatepicker td.nexty.hover,.fr-sqydatepicker td.prevy.hover,.fr-sqydatepicker td.fd.hover{background-color:#7bacff;border:1px solid #007bff;padding:1px}.fr-sqydatepicker table td.wn{padding:2px 3px 2px 2px;border-right:1px solid #98c0f4;background:#dfecfb} |
||||
.fr-sqydatepicker table td.weekend{color:#ae565f}.fr-sqydatepicker tbody td.hover{background-color:#eee;padding:1px 1px 1px 1px;border:1px solid #c6c6c6}.fr-sqydatepicker tbody td.today{font-weight:bold;color:#00f}.fr-sqydatepicker tbody td.selected{color:#fff;font-weight:bold;border:1px solid #007bff;padding:1px 1px 1px 1px;background:rgb(24,104,214)}.fr-sqydatepicker td.month,.fr-sqydatepicker td.year{border:0 none;vertical-align:middle;width:25%}.fr-sqydatepicker tfoot tr.optbtns td.btn{cursor:pointer;background-color:#dfecfb;font-size:13px;border-top:1px solid #98c0f4;padding:7px 2px 2px 2px} |
||||
.fr-sqydatepicker tfoot tr.optbtns td.hover{background-color:#7bacff;border:1px solid #007bff;padding:7px 1px 1px 1px}.fr-sqydatepicker td.oday{color:#d7d7d7}.fr-sqydatepicker td.disabled{color:#999}.fr-sqydatepicker table.tt{border:#a3bad9 1px solid;cursor:default;background-color:#dfecfb}.fr-sqydatepicker td.time{border-top:#98c0f4 1px solid;background-color:#dfecfb;border-left:0;border-right:0}.fr-sqydatepicker table.tt td{font-weight:bold;width:40px;height:15px}.fr-sqydatepicker table.tt td.common{width:5px}.fr-sqydatepicker table.tt td.hover{background-color:#7bacff;border:1px solid #007bff;padding:2px} |
||||
.fr-sqydatepicker table.tt input{height:20px;margin:0 auto;overflow:hidden;width:35px;text-align:center;line-height:20px;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle}.fr-sqydatepicker td.time table{border:0;cursor:default} |
||||
.fr-sqydatepicker .lunar{font-size: 12px;font-weight: normal;}.fr-sqydatepicker .solar{font-size: 13px;font-weight: bold;} |
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,2 @@
|
||||
Fine-Design_plugin_sqy_calendar_name=Fine-Design_plugin_sqy_calendar_name |
||||
Fine-Design_plugin_sqy_calendar_start=Fine-Design_plugin_sqy_calendar_start |
@ -0,0 +1,2 @@
|
||||
Fine-Design_plugin_sqy_calendar_name=\u519C\u5386\u65E5\u5386 |
||||
Fine-Design_plugin_sqy_calendar_start=\u8D77\u59CB\u65E5\u671F |
After Width: | Height: | Size: 196 B |
Loading…
Reference in new issue