拆分模块

This commit is contained in:
2025-09-03 20:56:44 +08:00
parent 08cc2c29a5
commit a2f5e4864b
939 changed files with 14227 additions and 9607 deletions

84
client/pom.xml Normal file
View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ecep.contract</groupId>
<artifactId>Contract-Manager</artifactId>
<version>0.0.49-SNAPSHOT</version>
</parent>
<groupId>com.ecep.contract</groupId>
<artifactId>client</artifactId>
<version>0.0.49-SNAPSHOT</version>
<properties>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.ecep.contract</groupId>
<artifactId>common</artifactId>
<version>0.0.49-SNAPSHOT</version>
</dependency>
<!-- JavaFX -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.controlsfx</groupId>
<artifactId>controlsfx</artifactId>
<version>11.2.0</version>
</dependency>
<!-- Caffeine Cache -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version>
</dependency>
<!-- Spring Context Support for CaffeineCacheManager -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-data-commons</artifactId>
<version>3.3.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<configuration>
<mainClass>com.ecep.contract.manager.AppV2</mainClass>
<launcher>app</launcher>
<jlinkZipName>app-jlink</jlinkZipName>
<jlinkImageName>app-jlink-image</jlinkImageName>
<noManPages>true</noManPages>
<stripDebug>true</stripDebug>
<compress>2</compress>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,29 @@
package com.ecep.contract;
import javafx.application.Application;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Created by Administrator on 2017/4/16.
*/
public class AppV2 {
private static final Logger logger = LoggerFactory.getLogger(AppV2.class);
public static void main(String[] args) {
Application.launch(Desktop.class, args);
done();
}
private static void done() {
logger.info("done");
try {
Desktop.shutdown();
} catch (Exception e) {
throw new RuntimeException(e);
}
System.out.println("AppV2.done");
}
public static final String DEFAULT_HOST = "10.84.209.154";
}

View File

@@ -0,0 +1,266 @@
package com.ecep.contract;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import com.ecep.contract.controller.BaseController;
import com.ecep.contract.controller.LoginWidowController;
import com.ecep.contract.task.TaskMonitorCenter;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CurrentEmployee;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import lombok.Getter;
/**
* JavaFx 应用程序
*
* @author ecep
* Created by ecep on 2017/05/08.
*/
public class Desktop extends Application {
public static final Logger logger = LoggerFactory.getLogger(Desktop.class);
public static Desktop instance;
public static void shutdown() {
if (logger.isDebugEnabled()) {
logger.debug("shutdown");
}
if (instance != null) {
try {
instance.stop();
} catch (Throwable e) {
logger.error("shutdown error", e);
}
}
}
private ScheduledExecutorService scheduledExecutorService = null;
private final TaskMonitorCenter taskMonitorCenter = new TaskMonitorCenter();
private final SimpleIntegerProperty sessionId = new SimpleIntegerProperty(0);
@Getter
private final CurrentEmployee activeEmployee = new CurrentEmployee();
public void setActiveEmployeeId(int activeEmployeeId) {
activeEmployee.getId().set(activeEmployeeId);
}
public int getActiveEmployeeId() {
return activeEmployee.getId().get();
}
public int getSessionId() {
return sessionId.get();
}
public void setSessionId(int sessionId) {
this.sessionId.set(sessionId);
}
public ScheduledExecutorService getExecutorService() {
if (scheduledExecutorService == null) {
scheduledExecutorService = Executors.newScheduledThreadPool(3);
}
return scheduledExecutorService;
}
public TaskMonitorCenter getTaskMonitorCenter() {
return taskMonitorCenter;
}
@Override
public void start(Stage primaryStage) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("start");
}
if (instance != null) {
logger.error("Desktop already started");
}
instance = this;
URL resource = getClass().getResource("/ui/start_lamp.fxml");
FXMLLoader loader = new FXMLLoader(resource);
primaryStage.setTitle("CMS");
primaryStage.initStyle(StageStyle.TRANSPARENT);
Parent root = loader.load();
Scene scene = new Scene(root);
scene.getStylesheets().add("/ui/start_lamp.css");
primaryStage.setScene(scene);
primaryStage.setOnShown(e -> {
System.out.println("primaryStage#OnShown");
});
primaryStage.show();
System.out.println("Desktop.start -> primaryStage.show()");
try {
startSpringApp(primaryStage, root, loader);
} catch (Exception e) {
UITools.showExceptionAndWait("启动失败", e);
}
}
public CompletableFuture<Void> runAsync(Runnable runnable) {
return CompletableFuture.runAsync(runnable, getExecutorService());
}
private void startSpringApp(Stage primaryStage, Parent root, FXMLLoader loader) {
System.out.println("Desktop.startSpringApp");
// 更新窗口标题
Node titleNode = root.lookup("#title");
if (titleNode != null) {
primaryStage.setTitle(((Text) titleNode).getText());
}
Node lookup = root.lookup("#logBox");
if (!(lookup instanceof VBox logBox)) {
throw new RuntimeException("启动界面加载失败, #logger 类型错误");
}
ScrollPane logPane = (ScrollPane) root.lookup("#logPane");
logBox.getChildren().clear();
MessageHolder holder = (level, message) -> {
Text text = new Text(message);
if (Level.WARNING == level) { // warning
text.setFill(Color.YELLOW);
} else if (Level.SEVERE == level) {// error
text.setFill(Color.RED);
} else if (Level.FINE == level) { // debug
text.setFill(Color.GRAY);
} else {
text.setFill(Color.WHITE);
}
Platform.runLater(() -> {
logBox.getChildren().add(text);
logPane.layout();
logPane.setVvalue(1.0);
});
};
holder.info("启动中,请稍后...");
runAsync(() -> {
try {
//
holder.info("读取配置文件...");
Properties properties = new Properties();
File configFile = new File("config.properties");
if (configFile.exists()) {
holder.debug("读取配置文件 " + configFile.getName() + "...");
try (FileInputStream input = new FileInputStream(configFile)) {
properties.load(input);
holder.info("配置文件读取成功.");
} catch (IOException e) {
holder.error("读取失败:" + e.getMessage());
logger.error(e.getMessage(), e);
return;
}
}
runAsync(() -> {
SpringApp.launch(properties, holder);
ConfigurableListableBeanFactory beanFactory = SpringApp.context.getBeanFactory();
beanFactory.registerSingleton("scheduledExecutorService", getExecutorService());
beanFactory.registerSingleton("taskMonitorCenter", taskMonitorCenter);
});
try {
LoginWidowController controller = new LoginWidowController();
controller.setHolder(holder);
controller.setPrimaryStage(primaryStage);
controller.setProperties(properties);
while (true) {
controller.tryLogin();
break;
}
if (logger.isDebugEnabled()) {
logger.debug("login in");
}
} catch (Exception e) {
holder.error("登录失败:" + e.getMessage());
logger.error(e.getMessage(), e);
}
} catch (Exception e) {
holder.error(e.getMessage());
logger.error(e.getMessage(), e);
}
});
System.out.println("Desktop.startSpringApp.");
}
@Override
public void stop() throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("stop");
}
CompletableFuture<Void> future = BaseController.shutdown();
future.orTimeout(5, TimeUnit.SECONDS);
future.exceptionally(e -> {
logger.error(e.getMessage(), e);
return null;
});
future.thenRun(() -> {
SpringApp.shutdown();
try {
shutdownExecutorService();
super.stop();
if (logger.isDebugEnabled()) {
logger.debug("stopped");
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
});
}
private void shutdownExecutorService() {
List<Runnable> runnableList = scheduledExecutorService.shutdownNow();
for (Runnable runnable : runnableList) {
if (logger.isDebugEnabled()) {
logger.debug("shutdown runnable = {}", runnable);
}
if (runnable instanceof FutureTask<?> future) {
if (logger.isDebugEnabled()) {
logger.debug("runnable as future, isCancelled() = {}, isDone() = {}", future.isCancelled(),
future.isDone());
}
if (future.cancel(true)) {
if (logger.isDebugEnabled()) {
logger.debug("runnable as future canceled");
}
}
}
}
scheduledExecutorService.close();
}
}

View File

@@ -0,0 +1,76 @@
package com.ecep.contract;
import java.io.File;
import java.io.IOException;
import java.util.function.Consumer;
import org.springframework.util.StringUtils;
public class DesktopUtils {
/**
* 在默认浏览器中打开指定的URL。
* <p>
* 该函数使用JavaFX的HostServices类来调用系统默认的浏览器并打开传入的URL。
*
* @param url 要在浏览器中打开的URL字符串。该参数不能为空且应为有效的URL格式。
*/
public static void showInBrowse(String url) {
Desktop.instance.getHostServices().showDocument(url);
}
/**
* 在系统的文件资源管理器中打开指定的文件夹。
* <p>
* 该方法首先尝试使用 java.awt.Desktop API 打开文件夹。如果该 API 不支持,
* 则在 Windows 系统中使用 explorer.exe 打开文件夹。
*
* @param dir 要打开的文件夹对象。如果为 null 或无效路径,可能会抛出异常。
* @throws RuntimeException 如果使用 java.awt.Desktop 打开文件夹时发生 IOException
* 则将其包装为 RuntimeException 抛出。
*/
public static void showInExplorer(File dir) {
if (java.awt.Desktop.isDesktopSupported()) {
try {
java.awt.Desktop.getDesktop().open(dir);
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
try {
// 在Windows中使用explorer.exe打开文件夹路径用双引号括起来
Process process = Runtime.getRuntime().exec(
new String[] { "explorer.exe", "\"" + dir.getAbsolutePath() + "\"" }, null, new File("."));
// process.waitFor();
} catch (IOException e) {
if (Desktop.logger.isDebugEnabled()) {
Desktop.logger.debug("Unable open {}", dir.getAbsolutePath(), e);
}
}
}
}
public static void checkAndShowInExplorer(String path, Consumer<String> consumer) {
if (!StringUtils.hasText(path)) {
consumer.accept("文件/目录为空,无法打开");
return;
}
File file = new File(path);
if (!file.exists()) {
if (file.isFile()) {
consumer.accept("文件 " + file.getAbsolutePath() + " 不存在,请确认");
} else {
consumer.accept("目录 " + file.getAbsolutePath() + " 不存在,请确认");
}
return;
}
try {
showInExplorer(file);
consumer.accept("打开文件/目录 " + path);
} catch (Exception e) {
consumer.accept("打开文件错误:" + e.getMessage());
}
}
}

View File

@@ -0,0 +1,7 @@
package com.ecep.contract;
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}

View File

@@ -0,0 +1,252 @@
package com.ecep.contract;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationHook;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
import org.springframework.boot.context.metrics.buffering.StartupTimeline;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.metrics.StartupStep;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import com.ecep.contract.util.UITools;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
@SpringBootApplication
@EnableScheduling
@EnableAsync
@EnableCaching
public class SpringApp {
private static final Logger logger = LoggerFactory.getLogger(SpringApp.class);
public static SpringApplication application;
public static ConfigurableApplicationContext context;
public static <T> T getBean(Class<T> requiredType) throws BeansException {
return context.getBean(requiredType);
}
public static void launch(Properties properties, MessageHolder holder) {
application = new SpringApplication(SpringApp.class);
BufferingApplicationStartup startup = new BufferingApplicationStartup(2000);
application.setApplicationStartup(startup);
//
holder.debug("应用程序环境准备中...");
SpringApplication.withHook(new Hook(holder), () -> {
// 动态地注册或修改这些组件和配置
application.addBootstrapRegistryInitializer(registry -> {
//
System.out.println("registry = " + registry);
});
application.addListeners(event -> {
logger.debug("SpringApp.launch ApplicationListener, event:{}", event);
});
application.addInitializers(app -> {
logger.debug("SpringApp.launch ApplicationContextInitializer");
ConfigurableEnvironment environment = app.getEnvironment();
logger.debug("environment = {}", environment);
PropertySource<?> dynamicProperties = environment.getPropertySources().get("dynamicProperties");
if (dynamicProperties != null) {
logger.debug("dynamicProperties = {}", dynamicProperties);
}
environment.getPropertySources().addLast(new PropertiesPropertySource("dynamicProperties", properties));
// app.getBeanFactory().registerSingleton("dataSource", dataSource());
logger.debug("app = {}", app);
});
startup.start("");
context = application.run();
logger.debug("SpringApp.launch application.run().");
Duration between = Duration.between(startup.getBufferedTimeline().getStartTime(), Instant.now());
holder.info("应用程序环境加载完成... " + between);
});
CompletableFuture.runAsync(() -> {
// 在这里调用 startup 性能分析
analyzeStartupPerformance(startup);
}, Desktop.instance.getExecutorService());
}
/**
* 分析启动性能数据并输出到日志
*/
private static void analyzeStartupPerformance(BufferingApplicationStartup startup) {
// 获取所有记录的事件
StartupTimeline timeline = startup.getBufferedTimeline();
if (timeline == null || timeline.getEvents().isEmpty()) {
logger.warn("StartupTimeline 为空或没有事件!");
return;
}
logger.info("总共有 {} 个事件", timeline.getEvents().size());
// 找出与 Bean 初始化相关的步骤
timeline.getEvents().stream()
.filter(event -> event.getStartupStep().getName().startsWith("spring.beans."))
.sorted((a, b) -> Long.compare(b.getDuration().toMillis(), a.getDuration().toMillis()))
.limit(30)
.forEach(event -> {
String name = event.getStartupStep().getName();
long duration = event.getDuration().toMillis();
logger.info("Bean 初始化阶段: {} - 耗时: {} ms", name, duration);
for (StartupStep.Tag tag : event.getStartupStep().getTags()) {
if ("beanName".equals(tag.getKey())) {
logger.info(" └── Bean 名称: {}", tag.getValue());
}
}
});
}
public static String getMessage(String code, Object[] args, Locale locale) {
return context.getMessage(code, args, locale);
}
public static void shutdown() {
System.out.println("SpringApp.shutdown");
if (logger.isDebugEnabled()) {
logger.debug("shutdown");
}
if (context != null) {
if (context.isRunning()) {
context.close();
}
}
}
public static boolean isRunning() {
return context != null && context.isRunning();
}
static class Hook implements SpringApplicationHook, SpringApplicationRunListener {
MessageHolder holder;
Hook(MessageHolder holder) {
this.holder = holder;
}
public void debug(String msg) {
holder.debug(msg);
}
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
logger.debug("Desktop.starting");
debug("Spring Application 启动中...");
}
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment) {
logger.debug("Desktop.environmentPrepared");
debug("初始化 Environment 中,请稍后...");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
logger.debug("Desktop.contextPrepared");
debug("Spring Application Context 预处理中,请稍后...");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
logger.debug("Desktop.contextLoaded");
debug("Spring Application Context 初始化完毕,请稍后...");
}
@Override
public void started(ConfigurableApplicationContext context, Duration timeTaken) {
logger.debug("Desktop.started");
debug("Spring Application 启动完毕.");
}
@Override
public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
logger.debug("Desktop.ready");
debug("Spring Application ready.");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
logger.error("Desktop.failed", exception);
holder.error("Spring Application 启动失败(" + exception.getMessage() + ").");
UITools.showExceptionAndWait("启动失败", exception);
}
@Override
public SpringApplicationRunListener getRunListener(SpringApplication springApplication) {
return this;
}
}
@EventListener
public void handleClosedEvent(ContextClosedEvent event) {
if (logger.isDebugEnabled()) {
logger.debug("handleClosedEvent={}", event);
}
Desktop.shutdown();
}
@Bean
public CacheManager cacheManager() {
// return new ConcurrentMapCacheManager("myCache");
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setAsyncCacheMode(true);
return cacheManager;
}
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE));
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(
DateTimeFormatter.ofPattern(MyDateTimeUtils.DEFAULT_DATETIME_FORMAT_PATTERN)));
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(
DateTimeFormatter.ofPattern(MyDateTimeUtils.DEFAULT_DATETIME_FORMAT_PATTERN)));
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ISO_LOCAL_TIME));
objectMapper.registerModule(javaTimeModule);
return objectMapper;
}
@Bean
public ScheduledExecutorService scheduledExecutorService() {
return Desktop.instance.getExecutorService();
}
}

View File

@@ -0,0 +1,41 @@
package com.ecep.contract.controller;
import org.apache.poi.ss.formula.functions.T;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.service.ViewModelService;
import com.ecep.contract.vm.IdentityViewModel;
import javafx.scene.control.TableView;
import javafx.stage.WindowEvent;
import lombok.Getter;
public abstract class AbstEntityBasedController<T extends IdentityEntity, TV extends IdentityViewModel<T>, Skin extends ManagerSkin>
extends BaseController {
public TableView<TV> table;
@Getter
private Skin skin;
protected abstract Skin createDefaultSkin();
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
if (skin == null) {
skin = createDefaultSkin();
}
skin.install();
}
@Override
public void onHiding(WindowEvent windowEvent) {
if (skin != null) {
skin.dispose();
}
super.onHiding(windowEvent);
}
public abstract ViewModelService<T, TV> getViewModelService();
}

View File

@@ -0,0 +1,228 @@
package com.ecep.contract.controller;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.RefreshableSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.service.ViewModelService;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.BaseViewModel;
import com.ecep.contract.vm.IdentityViewModel;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.stage.WindowEvent;
import lombok.Getter;
import lombok.Setter;
public abstract class AbstEntityController<T extends IdentityEntity, TV extends IdentityViewModel<T>>
extends BaseController {
/**
* 保存按钮
*/
public Button saveBtn;
@Getter
@Setter
protected TV viewModel;
@Getter
@Setter
protected CompletableFuture<T> loadedFuture;
private final ObservableList<TabSkin> tabSkins = FXCollections.observableArrayList();
@Override
public void onShown(WindowEvent windowEvent) {
Class<?> aClass = getClass();
super.onShown(windowEvent);
loadedFuture = CompletableFuture.supplyAsync(() -> {
T entity = loadEntity();
if (entity == null) {
// fixed, bind change if new view model create
if (viewModel != null) {
viewModel.bindListener();
}
return null;
}
Platform.runLater(() -> {
setStatus();
viewModel.update(entity);
});
viewModel.bindListener();
return entity;
});
registerTabSkins();
if (saveBtn != null) {
saveBtn.disableProperty().bind(createTabSkinChangedBindings().not());
saveBtn.setOnAction(event -> saveTabSkins());
}
installTabSkins();
}
protected T loadEntity() {
return getViewModelService().findById(viewModel.getId().get());
}
public T getEntity() {
return getLoadedFuture().join();
}
protected void setEntity(T entity) {
loadedFuture = CompletableFuture.completedFuture(entity);
BaseViewModel.updateInFxApplicationThread(entity, viewModel);
}
protected T saveEntity(T entity) {
return getViewModelService().save(entity);
}
public T save(T entity) {
T saved = saveEntity(entity);
setEntity(saved);
return saved;
}
protected void registerTabSkins() {
}
protected <K extends AbstEntityBasedTabSkin<?, ?, ?>> K registerTabSkin(Tab tab, Function<Tab, K> func) {
K f = func.apply(tab);
if (f != null) {
tabSkins.add(f);
}
return f;
}
protected void installTabSkins() {
for (TabSkin tabSkin : tabSkins) {
try {
tabSkin.install();
} catch (Exception e) {
throw new RuntimeException(tabSkin + ".install", e);
}
}
}
public void saveTabSkins() {
for (TabSkin tabSkin : tabSkins) {
try {
tabSkin.save();
} catch (Exception e) {
UITools.showExceptionAndWait("Tab" + tabSkin.getTab().getText() + " 保存失败,请检查", e);
}
}
}
public TabSkin getTabSkin(Tab tab) {
for (TabSkin tabSkin : tabSkins) {
if (tabSkin.getTab() == tab) {
return tabSkin;
}
}
return null;
}
public BooleanBinding createTabSkinChangedBindings() {
return Bindings.createBooleanBinding(() -> {
for (TabSkin tabSkin : tabSkins) {
if (tabSkin.changeProperty().get()) {
return true;
}
}
return false;
}, tabSkins.stream().map(TabSkin::changeProperty).toArray(BooleanProperty[]::new));
}
@SuppressWarnings("unchecked")
public <C> C getTabSkin(Class<C> skinClass) {
for (TabSkin tabSkin : tabSkins) {
if (tabSkin.getClass().isAssignableFrom(skinClass)) {
return (C) tabSkin;
}
}
return null;
}
public void onHidden(WindowEvent windowEvent) {
if (viewModel != null) {
try {
viewModel.unBindListener();
} catch (InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
uninstallTabSkins();
super.onHidden(windowEvent);
}
protected void uninstallTabSkins() {
tabSkins.forEach(TabSkin::dispose);
}
/**
* 刷新
*
* @param button 按钮
*/
public void refreshByButton(Button button) {
CompletableFuture.runAsync(() -> {
button.setDisable(true);
refresh().whenComplete((v, ex) -> {
button.setDisable(false);
if (ex != null) {
setStatus(ex.getMessage());
}
});
});
}
public CompletableFuture<Void> refresh() {
CompletableFuture<Void> future = new CompletableFuture<>();
T entity = loadEntity();
Platform.runLater(() -> {
setEntity(entity);
List<RefreshableSkin> list = tabSkins.stream()
.filter(v -> v instanceof RefreshableSkin)
.map(v -> ((RefreshableSkin) v)).toList();
if (list.isEmpty()) {
future.complete(null);
return;
}
CompletableFuture.allOf(list
.stream()
.map(RefreshableSkin::refresh)
.filter(Objects::nonNull)
.toArray(CompletableFuture<?>[]::new))
.whenComplete((v, ex) -> {
if (ex != null) {
future.completeExceptionally(ex);
} else {
future.complete(null);
}
});
});
return future;
}
public abstract ViewModelService<T, TV> getViewModelService();
}

View File

@@ -0,0 +1,587 @@
package com.ecep.contract.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
import com.ecep.contract.controller.table.TableTabSkin;
import com.ecep.contract.util.UITools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.service.ViewModelService;
import com.ecep.contract.util.TableViewUtils;
import com.ecep.contract.vm.IdentityViewModel;
import javafx.application.Platform;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent;
import javafx.util.converter.NumberStringConverter;
/**
* 实体管理器皮肤
* 提供了实体管理器的基本功能,如查询、新增、删除、修改、分页等
*
* @param <T> Entity 的类型
* @param <TV> Entity 对应的ViewModel
* @param <SKIN> Skin 的类型
* @param <C>
*/
public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>, SKIN extends ManagerSkin, C extends AbstManagerWindowController<T, TV, SKIN>>
implements ManagerSkin, TableTabSkin<T, TV>, EditableEntityTableTabSkin<T, TV> {
private static final Logger logger = LoggerFactory.getLogger(AbstEntityManagerSkin.class);
/**
*
*/
protected C controller;
protected CompletableFuture<Void> loadedFuture;
protected ObservableList<TV> dataSet = FXCollections.observableArrayList();
protected PageRequest currentPageable = PageRequest.ofSize(25);
protected final SimpleIntegerProperty currentPageNumber = new SimpleIntegerProperty();
// 是否允许调整表格高度
private boolean allowResize = true;
// 记录延时任务信息
private ScheduledFuture<?> loadTableDataSetFuture;
public AbstEntityManagerSkin(C controller) {
this.controller = controller;
}
@Override
public TableView<TV> getTableView() {
return controller.table;
}
@Override
public void handleException(String message, Throwable ex) {
if (controller != null) {
controller.handleException(message, ex);
return;
}
if (logger.isErrorEnabled()) {
logger.error(message, ex);
}
UITools.showExceptionAndWait(message, ex);
}
public Locale getLocale() {
return controller.getLocale();
}
public void install() {
onShown();
// 注册 F5 和 Ctrl+R 刷新快捷键
KeyCodeCombination ctrlRCombination = new KeyCodeCombination(KeyCode.R, KeyCombination.SHORTCUT_DOWN);
KeyCodeCombination f5Combination = new KeyCodeCombination(KeyCode.F5);
getTableView().addEventHandler(KeyEvent.KEY_PRESSED, event -> {
if (ctrlRCombination.match(event) || f5Combination.match(event)) {
System.out.println("loadTableDataSetFuture = " + loadTableDataSetFuture);
if (loadTableDataSetFuture == null) {
loadTableDataSet(false);
}
event.consume();
}
});
}
private void onShown() {
if (loadedFuture == null) {
loadedFuture = runAsync(() -> {
Platform.runLater(() -> {
getTableView().setItems(dataSet);
});
initializeSearchBar();
initializeTable();
initializeFooter();
TableView<TV> table = getTableView();
// 视图更新时
table.layoutBoundsProperty().addListener(this::resizeTable);
// 启用行编辑功能
if (!table.isEditable()) {
TableViewUtils.bindDoubleClicked(table, this::onTableRowDoubleClickedAction);
}
if (table.contextMenuProperty().get() == null) {
ContextMenu contextMenu = new ContextMenu();
createContextMenu(contextMenu);
table.setContextMenu(contextMenu);
}
table.setSortPolicy(v -> {
if (loadTableDataSetFuture == null) {
loadTableDataSet(false);
}
return true;
});
loadTableDataSet(true);
});
}
}
private void initializeSearchBar() {
if (controller.searchKeyField != null) {
controller.searchKeyField.setOnKeyReleased(event -> {
if (event.getCode() == KeyCode.ENTER) {
controller.searchBtn.fire();
}
});
}
if (controller.searchBtn != null) {
controller.searchBtn.setOnAction(this::onSearchAction);
}
}
private void initializeFooter() {
currentPageNumber.addListener(this::currentPageNumberListener);
//
controller.currentPageNumberField.textProperty().bindBidirectional(currentPageNumber,
new NumberStringConverter());
controller.previousPageBtn.setOnAction(event -> {
try {
currentPageable = currentPageable.previous();
loadTableDataSet(true);
} catch (Exception e) {
logger.warn("previous page error", e);
}
});
controller.nextPageBtn.setOnAction(event -> {
try {
currentPageable = currentPageable.next();
loadTableDataSet(true);
} catch (Exception e) {
logger.warn("next page error", e);
}
});
}
private void currentPageNumberListener(Object obj, Number old, Number newValue) {
int page = newValue.intValue();
if (page < 0) {
page = 0;
}
if (currentPageable.getPageNumber() == page) {
return;
}
currentPageable = currentPageable.withPage(page);
loadTableDataSet(false);
}
public void onSearchAction(ActionEvent event) {
currentPageable = currentPageable.withPage(0);
loadTableDataSet(true);
}
/**
* 根据表格高度重新计算分页的页大小
*/
private void resizeTable(Object observable, Bounds old, Bounds newBounds) {
if (!allowResize) {
return;
}
double tableHeight = newBounds.getHeight();
if (tableHeight <= 0) {
return;
}
TableView<TV> table = getTableView();
Node lookup = table.lookup("TableRow");
if (lookup != null) {
double rowHeight = lookup.prefHeight(-1);
int rows = (int) Math.round(table.getHeight() / rowHeight) - 1;
// 只有当行数变化超过一定阈值时才重新加载数据
int currentRows = currentPageable.getPageSize();
if (Math.abs(rows - currentRows) <= 2) {
return; // 避免微小变化导致频繁刷新
}
int pageNumber = (int) Math.abs(currentPageable.getOffset() / rows);
if (currentPageable.getPageNumber() == pageNumber && currentPageable.getPageSize() == rows) {
return;
}
currentPageable = PageRequest.of(pageNumber, rows);
loadTableDataSet(false);
}
}
protected void createContextMenu(ContextMenu contextMenu) {
MenuItem item2 = new MenuItem("刷新");
item2.setOnAction(this::onTableRefreshAction);
contextMenu.getItems().add(item2);
if (this instanceof EditableEntityTableTabSkin) {
MenuItem item1 = new MenuItem("新建");
item1.setOnAction(this::onTableCreateNewAction);
MenuItem item3 = new MenuItem("删除");
item3.setOnAction(this::onTableDeleteAction);
contextMenu.getItems().addAll(item1, item3);
}
}
protected void onTableCreateNewAction(ActionEvent event) {
TV viewModel = createNewViewModel();
dataSet.add(viewModel);
}
protected void onTableRefreshAction(ActionEvent event) {
loadTableDataSet(false);
}
protected void onTableDeleteAction(ActionEvent event) {
ObservableList<TV> selectedItems = getTableView().getSelectionModel().getSelectedItems();
if (selectedItems.isEmpty()) {
return;
}
if (!UITools.showConfirmDialog("删除行", "确认删除选中的" + selectedItems.size() + "条数据?")) {
return;
}
for (TV selectedItem : new ArrayList<>(selectedItems)) {
if (deleteRow(selectedItem)) {
dataSet.remove(selectedItem);
}
}
}
/**
* 处理单元格编辑事件
*
* @param <K>
* @param event
* @param propGetter
*/
protected <K> void acceptCellEditEvent(TableColumn.CellEditEvent<TV, K> event,
Function<TV, Property<K>> propGetter) {
TV row = event.getRowValue();
Property<K> property = propGetter.apply(row);
property.setValue(event.getNewValue());
try {
saveRowData(row);
} catch (Exception e) {
handleException("保存出错", e);
}
}
protected boolean deleteRow(TV row) {
ViewModelService<T, TV> service = getViewModelService();
T entity = service.findById(row.getId().get());
if (entity != null) {
try {
service.delete(entity);
return true;
} catch (UnsupportedOperationException e) {
handleException("删除出错,此操作不支持", e);
} catch (Exception e) {
handleException("删除出错", e);
}
}
return false;
}
protected T createNewEntity(TV row) {
ViewModelService<T, TV> service = getViewModelService();
if (service != null) {
T entity = service.createNewEntity();
if (entity != null) {
return entity;
}
}
return null;
}
protected TV createNewViewModel() {
ViewModelService<T, TV> service = getViewModelService();
if (service != null) {
TV model = service.createNewViewModel();
if (model != null) {
return model;
}
}
return null;
}
protected void saveRowData(TV row) {
ViewModelService<T, TV> service = getViewModelService();
if (service == null) {
handleException("ViewModelService is null", new RuntimeException());
return;
}
row.saveInFxApplicationThread(service);
}
/**
* 加载行数据
*
* @param row
* @return
*/
public T loadRowData(TV row) {
if (row.getId() == null) {
return null;
}
T entity = getViewModelService().findById(row.getId().get());
return entity;
}
/**
* 删除行数据
*
* @param entity
*/
public void deleteRowData(T entity) {
if (entity == null) {
return;
}
ViewModelService<T, TV> service = getViewModelService();
getViewModelService().delete(entity);
}
/**
* 保存行数据
*
* @param entity
* @return
*/
public T saveRowData(T entity) {
if (entity == null) {
return null;
}
ViewModelService<T, TV> service = getViewModelService();
return getViewModelService().save(entity);
}
@Override
public void loadTableDataSet() {
loadTableDataSet(false);
}
/**
* 加载表格数据
* 延时任务未执行前,再次调用此函数时,重新延时
*
* @param reloadNow 是否立即刷新立即刷新将直接submit一个任务到 Executor否则 schedule 一个618毫秒的延时任务
*/
public void loadTableDataSet(boolean reloadNow) {
if (loadTableDataSetFuture != null) {
loadTableDataSetFuture.cancel(true);
}
loadTableDataSetFuture = getExecutorService().schedule(() -> {
try {
_reloadTableData().thenRun(() -> loadTableDataSetFuture = null).exceptionally(this::handleException);
} catch (Exception ex) {
handleException("加载表格数据出错", ex);
}
}, reloadNow ? 0 : 618, TimeUnit.MILLISECONDS);
}
private CompletableFuture<Void> _reloadTableData() {
CompletableFuture<Void> future = new CompletableFuture<>();
Platform.runLater(() -> {
allowResize = false; // 禁用调整
dataSet.clear();
runAsync(() -> {
controller.setStatus("载入中...");
List<TV> models = loadTableData();
Platform.runLater(() -> {
try {
updateTableDataSet(models);
allowResize = true; // 恢复调整
future.complete(null);
} catch (Exception e) {
allowResize = true; // 恢复调整
future.completeExceptionally(e);
}
});
}).exceptionally(ex -> {
future.completeExceptionally(ex);
return null;
});
});
return future;
}
/**
* 更新表格数据
*
* @param models
*/
protected void updateTableDataSet(List<TV> models) {
long timeMillis = System.currentTimeMillis();
// 清除所有选择状态,避免选择状态混乱
if (getTableView() != null && getTableView().getSelectionModel() != null) {
getTableView().getSelectionModel().clearSelection();
}
// 先清空再设置新数据,避免数据叠加
dataSet.clear();
if (models != null) {
dataSet.addAll(models);
}
// 强制刷新表格布局
if (getTableView() != null) {
getTableView().requestLayout();
getTableView().refresh();
}
long timeCost = System.currentTimeMillis() - timeMillis;
if (logger.isDebugEnabled()) {
logger.debug("update table dataSet cost: {} ms", timeCost);
}
}
/**
* 加载表格数据
*
* @return
*/
protected List<TV> loadTableData() {
Map<String, Object> params = getSpecification();
ViewModelService<T, TV> service = getViewModelService();
long timeMillis = System.currentTimeMillis();
Page<T> page = service.findAll(params, getPageable());
long timeCost = System.currentTimeMillis() - timeMillis;
if (logger.isDebugEnabled()) {
logger.debug("load table data cost: {} ms", timeCost);
}
if (timeCost > 1000) {
controller.setStatus("used " + timeCost + " ms");
}
updateFooter(page);
return page.map(service::from).toList();
}
/**
* 获取ViewModelService
*
* @return
*/
protected ViewModelService<T, TV> getViewModelService() {
ViewModelService<T, TV> service = controller.getViewModelService();
if (service == null) {
throw new IllegalArgumentException("ViewModelService is null");
}
return service;
}
/**
* 获取查询条件
*
* @return
*/
public Map<String, Object> getSpecification() {
TextField field = controller.searchKeyField;
if (field != null) {
return getViewModelService().getSpecification(field.getText());
}
return null;
}
/**
* 获取查询条件
*
* @param searchText
* @return
*/
protected Map<String, Object> getSpecification(String searchText) {
return getViewModelService().getSpecification(searchText);
}
/**
* 当表格行被双击时触发
*
* @param item 被双击的行数据
*/
protected void onTableRowDoubleClickedAction(TV item) {
}
/**
* 更新页脚
*
* @param page
*/
protected void updateFooter(Page<T> page) {
Platform.runLater(() -> {
controller.previousPageBtn.setDisable(!page.hasPrevious());
controller.nextPageBtn.setDisable(!page.hasNext());
currentPageNumber.set(page.getNumber());
controller.setStatus(
(page.getNumber() + 1) + "/" + page.getTotalPages() + " 页, 总 " + page.getTotalElements() + "");
});
}
/**
* 获取表格排序
*
* @return
*/
public List<Sort.Order> getTableOrders() {
return TableViewUtils.getOrders(getTableView());
}
/**
* 获取表格排序
*
* @return
*/
public Sort getSortByTable() {
if (getTableView() == null) {
return Sort.unsorted();
}
return Sort.by(getTableOrders());
}
/**
* 获取分页参数
*
* @return
*/
public Pageable getPageable() {
Sort sort = getSortByTable();
return currentPageable.withSort(sort);
}
/**
* 显示在当前窗口为父窗口的新窗口
*
* @param <Controller> 控制器类型
* @param clz 控制器类
* @param model 数据
*/
protected <Controller extends AbstEntityController<T, TV>> void showInOwner(Class<Controller> clz, TV model) {
BaseController.show(clz, model, getTableView().getScene().getWindow());
}
}

View File

@@ -0,0 +1,44 @@
package com.ecep.contract.controller;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.vm.IdentityViewModel;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.stage.WindowEvent;
public abstract class AbstManagerWindowController<T extends IdentityEntity, TV extends IdentityViewModel<T>, Skin extends ManagerSkin>
extends AbstEntityBasedController<T, TV, Skin> {
// searcher
@FXML
public TextField searchKeyField;
@FXML
public Button searchBtn;
@FXML
public HBox footer;
// paging
@FXML
public Button previousPageBtn;
@FXML
public Button nextPageBtn;
@FXML
public TextField currentPageNumberField;
@Override
public void onShown(WindowEvent windowEvent) {
// fixed fx:include uninitialized issue
previousPageBtn = (Button) footer.lookup("#previousPageBtn");
nextPageBtn = (Button) footer.lookup("#nextPageBtn");
currentPageNumberField = (TextField) footer.lookup("#currentPageNumberField");
leftStatusLabel = (Label) footer.lookup("#leftStatusLabel");
//
super.onShown(windowEvent);
}
}

View File

@@ -0,0 +1,337 @@
package com.ecep.contract.controller;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.FxmlUtils;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import com.ecep.contract.Desktop;
import com.ecep.contract.SpringApp;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.service.EmployeeService;
import com.ecep.contract.service.SysConfService;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CurrentEmployee;
import com.ecep.contract.vm.IdentityViewModel;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import lombok.Getter;
public class BaseController {
private static final Logger logger = LoggerFactory.getLogger(BaseController.class);
public static HashMap<String, Stage> stages = new HashMap<>();
public static <T extends BaseController> CompletableFuture<Void> show(Class<T> clz, Window owner) {
return show(clz, owner, null);
}
public static <T extends BaseController> CompletableFuture<Void> show(Class<T> clz, Window owner,
Consumer<T> consumer) {
String key = clz.getName();
if (toFront(key)) {
return null;
}
FxmlPath annotation = clz.getAnnotation(FxmlPath.class);
if (annotation == null) {
throw new RuntimeException("@FxmlPath is required");
}
return FxmlUtils.newLoaderAsyncWithRunLater(annotation.value(), null, loader -> {
T controller = loader.getController();
if (consumer != null) {
consumer.accept(controller);
}
controller.show(loader, owner, Modality.NONE, key);
});
}
@SuppressWarnings("unchecked")
public static <K extends IdentityEntity, M extends IdentityViewModel<K>, T extends BaseController> void show(
Class<T> clz, M viewModel, Window owner) {
String key = getKey(clz, viewModel);
if (toFront(key)) {
return;
}
FxmlPath annotation = clz.getAnnotation(FxmlPath.class);
if (annotation == null) {
throw new RuntimeException("@FxmlPath is required");
}
FxmlUtils.newLoaderAsyncWithRunLater(annotation.value(), null, loader -> {
T controller = loader.getController();
if (controller instanceof AbstEntityController<?, ?>) {
((AbstEntityController<?, M>) controller).setViewModel(viewModel);
}
controller.show(loader, owner, Modality.NONE, key);
});
}
private static <V> String getKey(Class<?> clz, V viewModel) {
PropertyDescriptor idProperty = BeanUtils.getPropertyDescriptor(viewModel.getClass(), "id");
if (idProperty != null) {
Method readMethod = idProperty.getReadMethod();
if (readMethod != null) {
try {
Object object = readMethod.invoke(viewModel);
if (object instanceof ObservableValue<?> value) {
return clz.getName() + "#" + value.getValue();
}
} catch (IllegalAccessException | InvocationTargetException ignored) {
}
}
}
return clz.getName() + "#" + viewModel.hashCode();
}
public static void show(Class<?> clz, String resource, Window owner) {
String key = clz.getName();
if (toFront(key)) {
return;
}
FxmlUtils.newLoaderAsyncWithRunLater(resource, null, loader -> {
BaseController controller = loader.getController();
controller.show(loader, owner, Modality.NONE, key);
});
}
public static boolean toFront(String key) {
Stage stage = stages.get(key);
if (stage != null) {
stage.toFront();
if (!stage.isShowing()) {
stage.show();
}
return true;
}
return false;
}
public static CompletableFuture<Void> shutdown() {
if (logger.isDebugEnabled()) {
logger.debug("shutdown");
}
// 关闭所有窗口
CompletableFuture<Void> future = new CompletableFuture<>();
ArrayList<Stage> list = new ArrayList<>(stages.values());
if (list.isEmpty()) {
if (logger.isDebugEnabled()) {
logger.debug("no stage should to close.");
}
future.complete(null);
} else {
if (logger.isDebugEnabled()) {
logger.debug("try to close stages {}.", list);
}
Platform.runLater(() -> {
try {
for (Stage stage : list) {
System.out.println("stage = " + stage);
stage.close();
if (logger.isDebugEnabled()) {
logger.debug("stage close {}", stage);
}
}
} catch (Exception e) {
logger.error("stage close error", e);
}
future.complete(null);
});
}
return future;
}
public CompletableFuture<Void> runAsync(Runnable runnable) {
return CompletableFuture.runAsync(runnable, Desktop.instance.getExecutorService())
.exceptionally(this::handleException);
}
/**
* 窗口标题
*/
@Getter
private final StringProperty title = new SimpleStringProperty("窗口");
/**
* 窗口是否已经请求关闭
*/
@Getter
private boolean closeRequested = false;
/**
* 用于管理窗口的Key
*/
private String stageKey;
public Label leftStatusLabel;
public Label rightStatusLabel;
private Employee currentUser;
private HashMap<Class<?>, Object> cachedBeans = new HashMap<>();
public <T> T getBean(Class<T> requiredType) throws BeansException {
return SpringApp.getBean(requiredType);
}
@SuppressWarnings("unchecked")
public <T> T getCachedBean(Class<T> requiredType) throws BeansException {
Object object = cachedBeans.get(requiredType);
if (object == null) {
object = getBean(requiredType);
cachedBeans.put(requiredType, object);
}
return (T) object;
}
public SysConfService getConfService() {
return getCachedBean(SysConfService.class);
}
public EmployeeService getEmployeeService() {
return getCachedBean(EmployeeService.class);
}
public Employee getCurrentUser() {
if (currentUser == null) {
currentUser = getEmployeeService().findById(Desktop.instance.getActiveEmployeeId());
}
return currentUser;
}
public CurrentEmployee getCurrentEmployee() {
return Desktop.instance.getActiveEmployee();
}
public Locale getLocale() {
CurrentEmployee currentEmployee = getCurrentEmployee();
if (currentEmployee == null) {
return Locale.getDefault();
}
return currentEmployee.localeProperty().get();
}
public String getMessage(String code, Object... args) {
return SpringApp.getMessage(code, args, getLocale());
}
protected void setStatus() {
setStatus(Strings.EMPTY);
}
public void setStatus(String status) {
if (leftStatusLabel == null) {
return;
}
Platform.runLater(() -> leftStatusLabel.textProperty().set(status));
}
public void setRightStatus(String status) {
if (rightStatusLabel == null) {
return;
}
Platform.runLater(() -> rightStatusLabel.textProperty().set(status));
}
/**
* FXML loader 载入时会自动运行此函数
*/
public void initialize() {
}
public void show(Stage stage) {
stage.titleProperty().bind(title);
stage.setOnShown(this::onShown);
stage.setOnShowing(this::onShowing);
stage.setOnCloseRequest(this::onCloseRequest);
stage.setOnHiding(this::onHiding);
stage.setOnHidden(this::onHidden);
stage.show();
}
public Stage show(FXMLLoader loader, Window owner, Modality modality) {
Scene scene = new Scene(loader.getRoot());
Stage stage = new Stage();
if (owner != null) {
stage.initOwner(owner);
stage.initModality(modality);
}
stage.setScene(scene);
show(stage);
return stage;
}
protected Stage show(FXMLLoader loader, Window window, Modality modality, String stageKey) {
Stage stage = show(loader, window, modality);
stages.put(stageKey, stage);
this.stageKey = stageKey;
return stage;
}
public void onShowing(WindowEvent windowEvent) {
}
public void onShown(WindowEvent windowEvent) {
setStatus("");
setRightStatus("");
}
/**
* 当窗口要关闭前
*
* @param windowEvent event
*/
public void onCloseRequest(WindowEvent windowEvent) {
closeRequested = true;
}
public void onHiding(WindowEvent windowEvent) {
}
public void onHidden(WindowEvent windowEvent) {
if (stageKey != null) {
stages.remove(stageKey);
}
}
/**
* 处理错误
*
* @param ex 错误信息
* @return Void
*/
public Void handleException(Throwable ex) {
handleException(ex.getMessage(), ex);
return null;
}
public void handleException(String message, Throwable ex) {
if (logger.isErrorEnabled()) {
logger.error(message, ex);
}
UITools.showExceptionAndWait(message, ex);
}
}

View File

@@ -0,0 +1,113 @@
package com.ecep.contract.controller;
import org.hibernate.Hibernate;
import com.ecep.contract.SpringApp;
import com.ecep.contract.controller.company.CompanyWindowController;
import com.ecep.contract.controller.table.cell.CompanyTableCell;
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
import com.ecep.contract.model.CloudRk;
import com.ecep.contract.model.Company;
import com.ecep.contract.service.CloudRkService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.vm.CloudRkViewModel;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.CheckBoxTableCell;
import lombok.Setter;
public class CloudRkManagerSkin
extends AbstEntityManagerSkin<CloudRk, CloudRkViewModel, CloudRkManagerSkin, CloudRkManagerWindowController> {
@Setter
private CompanyService companyService;
public CloudRkManagerSkin(CloudRkManagerWindowController controller) {
super(controller);
}
CloudRkService getCloudRkService() {
return controller.getViewModelService();
}
CompanyService getCompanyService() {
if (companyService == null) {
companyService = SpringApp.getBean(CompanyService.class);
}
return companyService;
}
@Override
public void initializeTable() {
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
controller.companyColumn.setCellValueFactory(param -> param.getValue().getCompany());
controller.companyColumn.setCellFactory(param -> new CompanyTableCell<>(getCompanyService()));
controller.cloudIdColumn.setCellValueFactory(param -> param.getValue().getCloudId());
controller.latestUpdateColumn.setCellValueFactory(param -> param.getValue().getLatestUpdate());
controller.latestUpdateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
controller.cloudLatestColumn.setCellValueFactory(param -> param.getValue().getCloudLatest());
controller.cloudLatestColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
controller.cloudBlackListUpdatedColumn
.setCellValueFactory(param -> param.getValue().getCloudBlackListUpdated());
controller.cloudBlackListUpdatedColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
controller.cloudEntUpdateColumn.setCellValueFactory(param -> param.getValue().getCloudEntUpdate());
controller.cloudEntUpdateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
controller.autoUpdateColumn.setCellValueFactory(param -> param.getValue().getAutoUpdate());
controller.autoUpdateColumn.setCellFactory(CheckBoxTableCell.forTableColumn(controller.autoUpdateColumn));
controller.descriptionColumn.setCellValueFactory(param -> param.getValue().getDescription());
}
private void onAutoUpdateColumnEditCommit(TableColumn.CellEditEvent<CloudRkViewModel, Boolean> event) {
CloudRkViewModel row = event.getRowValue();
row.getAutoUpdate().set(event.getNewValue());
saveRowData(row);
}
@Override
protected void createContextMenu(ContextMenu contextMenu) {
MenuItem item2 = new MenuItem("刷新");
item2.setOnAction(this::onTableRefreshAction);
MenuItem item3 = new MenuItem("清空备注");
item3.setOnAction(this::onTableClearDescriptionAction);
contextMenu.getItems().addAll(item2, item3);
}
/**
* 请空选择行的注释
*
* @param event event
*/
public void onTableClearDescriptionAction(ActionEvent event) {
ObservableList<CloudRkViewModel> selectedItems = getTableView().getSelectionModel().getSelectedItems();
if (selectedItems.isEmpty()) {
return;
}
CloudRkService service = getCloudRkService();
for (CloudRkViewModel selectedItem : selectedItems) {
selectedItem.getDescription().set("");
selectedItem.saveInFxApplicationThread(service);
}
}
@Override
protected void onTableRowDoubleClickedAction(CloudRkViewModel item) {
Company company = item.getCompany().get();
if (!Hibernate.isInitialized(item)) {
company = getCompanyService().findById(company.getId());
}
CompanyWindowController.show(company, getTableView().getScene().getWindow());
}
}

View File

@@ -0,0 +1,98 @@
package com.ecep.contract.controller;
import java.time.LocalDateTime;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.Message;
import com.ecep.contract.SpringApp;
import com.ecep.contract.model.CloudRk;
import com.ecep.contract.model.Company;
import com.ecep.contract.service.CloudRkService;
import com.ecep.contract.task.CloudRkSyncTask;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CloudRkViewModel;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
import javafx.stage.WindowEvent;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/cloud/rk_manager.fxml")
public class CloudRkManagerWindowController
extends AbstManagerWindowController<CloudRk, CloudRkViewModel, CloudRkManagerSkin> {
private static final Logger logger = LoggerFactory.getLogger(CloudRkManagerWindowController.class);
public static void show() {
show(CloudRkManagerWindowController.class, null);
}
@Autowired
private CloudRkService cloudRkService;
public TableColumn<CloudRkViewModel, Number> idColumn;
public TableColumn<CloudRkViewModel, LocalDateTime> latestUpdateColumn;
public TableColumn<CloudRkViewModel, Company> companyColumn;
public TableColumn<CloudRkViewModel, String> cloudIdColumn;
public TableColumn<CloudRkViewModel, LocalDateTime> cloudLatestColumn;
public TableColumn<CloudRkViewModel, LocalDateTime> cloudBlackListUpdatedColumn;
public TableColumn<CloudRkViewModel, LocalDateTime> cloudEntUpdateColumn;
public TableColumn<CloudRkViewModel, Boolean> autoUpdateColumn;
public TableColumn<CloudRkViewModel, String> descriptionColumn;
@Override
protected CloudRkManagerSkin createDefaultSkin() {
return new CloudRkManagerSkin(this);
}
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
getTitle().set("数据源:集团相关方");
}
private void initializeTask(Task<Object> task, String prefix, Consumer<String> consumer) {
task.setOnScheduled(e -> {
consumer.accept("正在从相关方平台同步" + prefix + ",请稍后...");
});
task.setOnRunning(e -> {
consumer.accept("开始" + prefix + "...");
});
task.setOnSucceeded(e -> {
consumer.accept(prefix + "完成...");
});
task.exceptionProperty().addListener((observable, oldValue, newValue) -> {
consumer.accept(newValue.getMessage());
logger.error("{} 发生异常", prefix, newValue);
});
SpringApp.getBean(ScheduledExecutorService.class).submit(task);
consumer.accept("任务已创建...");
}
public void onDataRepairAction(ActionEvent event) {
}
public void onSyncAction(ActionEvent event) {
CloudRkSyncTask task = new CloudRkSyncTask();
UITools.showTaskDialogAndWait("同步数据", task, consumer -> {
initializeTask(task, "同步数据", msg -> consumer.accept(Message.info(msg)));
});
}
@Override
public CloudRkService getViewModelService() {
return cloudRkService;
}
}

View File

@@ -0,0 +1,136 @@
package com.ecep.contract.controller;
import java.util.List;
import org.hibernate.Hibernate;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.StringUtils;
import com.ecep.contract.SpringApp;
import com.ecep.contract.cloud.tyc.CloudTycService;
import com.ecep.contract.controller.company.CompanyWindowController;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.model.CloudTyc;
import com.ecep.contract.model.Company;
import com.ecep.contract.ui.table.cell.CompanyTableCell;
import com.ecep.contract.ui.table.cell.LocalDateTimeTableCell;
import com.ecep.contract.vm.CloudTycInfoViewModel;
import jakarta.persistence.criteria.Path;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import lombok.Setter;
public class CloudTycManagerSkin
extends
AbstEntityManagerSkin<CloudTyc, CloudTycInfoViewModel, CloudTycManagerSkin, CloudTycManagerWindowController> {
@Setter
private CloudTycService cloudTycService;
@Setter
private CompanyService companyService;
public CloudTycManagerSkin(CloudTycManagerWindowController controller) {
super(controller);
}
CloudTycService getCloudTycService() {
if (cloudTycService == null) {
cloudTycService = getBean(CloudTycService.class);
}
return cloudTycService;
}
CompanyService getCompanyService() {
if (companyService == null) {
companyService = SpringApp.getBean(CompanyService.class);
}
return companyService;
}
@Override
protected List<CloudTycInfoViewModel> loadTableData() {
String searchText = controller.searchKeyField.getText();
Specification<CloudTyc> spec = null;
if (StringUtils.hasText(searchText)) {
Specification<CloudTyc> companySpec = (root, query, builder) -> {
Path<Object> company = root.get("company");
return builder.or(
builder.like(company.get("name"), "%" + searchText + "%"),
builder.like(company.get("shortName"), "%" + searchText + "%"));
};
Specification<CloudTyc> cloudIdSpec = (root, query, builder) -> {
return builder.like(root.get("cloudId"), "%" + searchText + "%");
};
spec = Specification.anyOf(companySpec, cloudIdSpec);
}
Page<CloudTyc> page = getCloudTycService().findAll(spec, getPageable());
updateFooter(page);
return page.map(CloudTycInfoViewModel::from).toList();
}
@Override
public void initializeTable() {
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
controller.companyColumn.setCellValueFactory(param -> param.getValue().getCompany());
controller.companyColumn.setCellFactory(param -> new CompanyTableCell<>(getCompanyService()));
controller.cloudIdColumn.setCellValueFactory(param -> param.getValue().getCloudId());
controller.latestUpdateColumn.setCellValueFactory(param -> param.getValue().getLatest());
controller.latestUpdateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
controller.cloudLatestColumn.setCellValueFactory(param -> param.getValue().getCloudLatest());
controller.cloudLatestColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
controller.scoreColumn.setCellValueFactory(param -> param.getValue().getScore());
}
@Override
protected void createContextMenu(ContextMenu contextMenu) {
MenuItem item2 = new MenuItem("刷新");
item2.setOnAction(this::onTableRefreshAction);
MenuItem item3 = new MenuItem("清空备注");
item3.setOnAction(this::onTableClearDescriptionAction);
contextMenu.getItems().addAll(item2, item3);
}
/**
* 请空选择行的注释
*
* @param event event
*/
public void onTableClearDescriptionAction(ActionEvent event) {
ObservableList<CloudTycInfoViewModel> selectedItems = getTableView().getSelectionModel().getSelectedItems();
if (selectedItems.isEmpty()) {
return;
}
for (CloudTycInfoViewModel selectedItem : selectedItems) {
CloudTyc cloudTyc = getCloudTycService().findById(selectedItem.getId().get());
// selectedItem.getDescription().set("");
if (selectedItem.copyTo(cloudTyc)) {
CloudTyc saved = getCloudTycService().save(cloudTyc);
selectedItem.update(saved);
}
}
}
@Override
protected void onTableRowDoubleClickedAction(CloudTycInfoViewModel item) {
Company company = item.getCompany().get();
if (!Hibernate.isInitialized(item)) {
company = getCompanyService().findById(company.getId());
}
CompanyWindowController.show(company, getTableView().getScene().getWindow());
}
}

View File

@@ -0,0 +1,96 @@
package com.ecep.contract.controller;
import java.time.LocalDateTime;
import java.util.Objects;
import com.ecep.contract.util.FxmlPath;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.cloud.CloudBaseInfo;
import com.ecep.contract.cloud.CloudInfo;
import com.ecep.contract.cloud.tyc.CloudTycService;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.model.CloudTyc;
import com.ecep.contract.model.Company;
import com.ecep.contract.vm.CloudTycInfoViewModel;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
/**
* 天眼查信息管理窗口控制器
*/
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/cloud/tyc_manager.fxml")
public class CloudTycManagerWindowController
extends AbstManagerWindowController<CloudTyc, CloudTycInfoViewModel, CloudTycManagerSkin> {
public static void show() {
show(CloudTycManagerWindowController.class, null);
}
@Autowired
private CloudTycService cloudTycService;
@Autowired
private CompanyService companyService;
@FXML
public TableColumn<CloudTycInfoViewModel, Number> idColumn;
@FXML
public TableColumn<CloudTycInfoViewModel, LocalDateTime> latestUpdateColumn;
@FXML
public TableColumn<CloudTycInfoViewModel, Company> companyColumn;
@FXML
public TableColumn<CloudTycInfoViewModel, String> cloudIdColumn;
@FXML
public TableColumn<CloudTycInfoViewModel, LocalDateTime> cloudLatestColumn;
@FXML
public TableColumn<CloudTycInfoViewModel, Number> scoreColumn;
@FXML
public TableColumn<CloudTycInfoViewModel, String> descriptionColumn;
@Override
public CloudTycService getViewModelService() {
return cloudTycService;
}
@Override
protected CloudTycManagerSkin createDefaultSkin() {
return new CloudTycManagerSkin(this);
}
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().set("数据源:天眼查");
}
boolean copyTo(CloudInfo v, CloudBaseInfo cloudRk) {
boolean modified = false;
if (!Objects.equals(cloudRk.getLatestUpdate(), v.getLatestUpdate())) {
cloudRk.setLatestUpdate(v.getLatestUpdate());
modified = true;
}
if (!Objects.equals(cloudRk.getCloudId(), v.getCloudId())) {
cloudRk.setCloudId(v.getCloudId());
modified = true;
}
if (!Objects.equals(cloudRk.getCompany(), v.getCompany())) {
cloudRk.setCompany(v.getCompany());
modified = true;
}
return modified;
}
}

View File

@@ -0,0 +1,191 @@
package com.ecep.contract.controller;
import java.util.List;
import org.springframework.util.StringUtils;
import com.ecep.contract.model.BaseEnumEntity;
import com.ecep.contract.model.BasedEntity;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.model.NamedEntity;
import javafx.beans.property.Property;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
public class ComboBoxUtils {
static String formatEntity(Object t) {
if (t == null) {
return "全部";
}
if (t instanceof BasedEntity e) {
return e.toPrettyString();
}
if (t instanceof BaseEnumEntity<?> e) {
return e.getValue();
}
if (t instanceof NamedEntity named) {
return named.getName();
}
return t.toString();
}
static <T> T fromString(List<T> dataset, String string) {
if (dataset == null) {
// 没办法识别
return null;
}
if (!StringUtils.hasText(string)) {
// 没办法识别
return null;
}
for (T t : dataset) {
if (t == null) {
continue;
}
if (t instanceof BasedEntity e) {
if (e.toPrettyString().equals(string)) {
return t;
}
}
if (t instanceof BaseEnumEntity<?> e) {
if (e.getValue().equals(string)) {
return t;
}
}
if (t instanceof NamedEntity named) {
if (named.getName().equals(string)) {
return t;
}
}
if (t.toString().equals(string)) {
return t;
}
}
return null;
}
private static
class ComboBoxStringConverter<T> extends javafx.util.StringConverter<T> {
private final List<T> dataset;
public ComboBoxStringConverter(ObservableList<T> list) {
dataset = list;
}
@Override
public String toString(T t) {
return formatEntity(t);
}
@Override
public T fromString(String string) {
return ComboBoxUtils.fromString(dataset, string);
}
}
public static <T> void initialComboBox(ComboBox<T> comboBox, List<T> items, boolean hasNull) {
ObservableList<T> list = FXCollections.observableArrayList();
if (hasNull) {
list.add(null);
}
list.addAll(items);
comboBox.setItems(list);
comboBox.setConverter(new ComboBoxStringConverter<>(list));
}
static class EntityStringConverter<T> extends javafx.util.StringConverter<T> {
private final List<T> dataset;
EntityStringConverter(List<T> dataset) {
this.dataset = dataset;
}
@Override
public String toString(T t) {
return formatEntity(t);
}
@Override
public T fromString(String string) {
return ComboBoxUtils.fromString(dataset, string);
}
}
public static <T extends IdentityEntity & NamedEntity> void initialComboBox(
ComboBox<T> comboBox, Property<T> property, List<T> dataSet, boolean hasNull
) {
ObservableList<T> list = FXCollections.observableArrayList();
if (hasNull) {
list.add(null);
}
list.addAll(dataSet);
comboBox.setItems(list);
comboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
property.setValue(newValue);
});
EntityStringConverter<T> converter = new EntityStringConverter<>(dataSet);
comboBox.setConverter(converter);
comboBox.valueProperty().bindBidirectional(property);
}
public static <K> void bindComboBox(ComboBox<K> comboBox, Property<K> property) {
property.addListener((observable, oldValue, newValue) -> {
comboBox.setValue(newValue);
// comboBox.getItems().stream().filter(k->k.equals(newValue)).findFirst().ifPresent(comboBox::setValue);
});
comboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == null) {
property.setValue(null);
return;
}
property.setValue(newValue);
});
}
public static <K extends Enum<?>, T extends BaseEnumEntity<K>> void bindComboBox(ComboBox<T> comboBox, Property<K> property, List<T> dataSet) {
property.addListener((observable, oldValue, newValue) -> {
dataSet.stream().filter(l -> l.getType() == newValue).findFirst().ifPresent(comboBox::setValue);
});
comboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == null) {
property.setValue(null);
return;
}
property.setValue(newValue.getType());
});
// comboBox.valueProperty().addListener((observable, oldValue, newValue) -> {
// if (newValue == null) {
// property.setValue(null);
// return;
// }
// property.setValue(newValue.getType());
// });
comboBox.setCellFactory(param -> new ListCell<>() {
@Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
} else {
setText(item.getValue());
}
}
});
K value = property.getValue();
if (value != null) {
dataSet.stream().filter(l -> l.getType() == value).findFirst().ifPresent(comboBox::setValue);
}
}
}

View File

@@ -0,0 +1,22 @@
package com.ecep.contract.controller;
import org.springframework.context.ApplicationEvent;
import com.ecep.contract.vm.CurrentEmployee;
import lombok.Getter;
@Getter
public class CurrentEmployeeInitialedEvent extends ApplicationEvent {
private final CurrentEmployee employee;
public CurrentEmployeeInitialedEvent(CurrentEmployee employee) {
super(employee);
this.employee = employee;
}
@Override
public CurrentEmployee getSource() {
return employee;
}
}

View File

@@ -0,0 +1,221 @@
package com.ecep.contract.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.FxmlUtils;
import org.controlsfx.control.TaskProgressView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import com.ecep.contract.CurrentEmployeeInitialedEvent;
import com.ecep.contract.Desktop;
import com.ecep.contract.DesktopUtils;
import com.ecep.contract.cloud.old.OldVersionService;
import com.ecep.contract.cloud.rk.CloudRkManagerWindowController;
import com.ecep.contract.cloud.rk.CloudRkService;
import com.ecep.contract.cloud.tyc.CloudTycManagerWindowController;
import com.ecep.contract.cloud.u8.ContractSyncTask;
import com.ecep.contract.cloud.u8.YongYouU8ManagerWindowController;
import com.ecep.contract.cloud.u8.YongYouU8Service;
import com.ecep.contract.ds.company.controller.CompanyManagerWindowController;
import com.ecep.contract.ds.contract.controller.ContractManagerWindowController;
import com.ecep.contract.ds.customer.controller.CompanyCustomerManagerWindowController;
import com.ecep.contract.ds.other.controller.bank.BankManagerWindowController;
import com.ecep.contract.ds.other.controller.department.DepartmentManagerWindowController;
import com.ecep.contract.ds.other.controller.employee.EmployeeManagerWindowController;
import com.ecep.contract.ds.other.controller.inventory.InventoryManagerWindowController;
import com.ecep.contract.ds.other.controller.permission.EmployeeFunctionsManagerWindowController;
import com.ecep.contract.ds.other.controller.permission.EmployeeRoleManagerWindowController;
import com.ecep.contract.ds.project.controller.ProjectManagerWindowController;
import com.ecep.contract.ds.vendor.controller.CompanyVendorManagerWindowController;
import com.ecep.contract.vm.CurrentEmployee;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/home.fxml")
public class HomeWindowController extends BaseController {
private static final Logger logger = LoggerFactory.getLogger(HomeWindowController.class);
public static CompletableFuture<Void> show() {
return show(HomeWindowController.class, null);
}
// searcher
public VBox root;
public Label statusLabel;
public Button openCompanyManagerWindow;
public Button openProjectManagerWindow;
public Button openContractManagerWindow;
public Button openVendorManagerWindow;
public Button openCustomManagerWindow;
public TaskProgressView<Task<?>> taskProgressView;
public Label taskMonitorLabel;
public Label employeeStatusLabel;
public void initialize() {
openCompanyManagerWindow.setOnAction(event -> showInOwner(CompanyManagerWindowController.class));
openProjectManagerWindow.setOnAction(event -> showInOwner(ProjectManagerWindowController.class));
openContractManagerWindow.setOnAction(event -> showInOwner(ContractManagerWindowController.class));
openVendorManagerWindow.setOnAction(event -> showInOwner(CompanyVendorManagerWindowController.class));
openCustomManagerWindow.setOnAction(event -> showInOwner(CompanyCustomerManagerWindowController.class));
}
private <T extends BaseController> void showInOwner(Class<T> clz) {
show(clz, root.getScene().getWindow());
}
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
getTitle().set("销售与采购流程管理");
// ((TaskProgressViewSkin<Task<?>>)taskProgressView.getSkin()).
taskProgressView.setRetainTasks(false);
Node listView = taskProgressView.getSkin().getNode().lookup("ListView");
Node placeholder = ((ListView<?>) listView).getPlaceholder();
((Label) placeholder).setText("没有运行的任务");
employeeStatusLabel.textProperty().bind(Desktop.instance.getActiveEmployee().getName());
Desktop.instance.getTaskMonitorCenter().bindStatusLabel(taskMonitorLabel);
Desktop.instance.getActiveEmployee().initialize();
}
@EventListener
public void onCurrentEmployeeInitialed(CurrentEmployeeInitialedEvent event) {
CurrentEmployee currentEmployee = event.getEmployee();
if (currentEmployee.isSystemAdministrator()) {
if (logger.isInfoEnabled()) {
logger.info("You are administrator, try schedule sync tasks.");
}
Desktop.instance.getExecutorService().schedule(() -> {
try {
getBean(OldVersionService.class).scheduledTasks(taskProgressView);
} catch (BeansException ignored) {
}
try {
getBean(YongYouU8Service.class).scheduledTasks(taskProgressView);
} catch (BeansException ignored) {
}
try {
getBean(CloudRkService.class).scheduledTasks(taskProgressView);
} catch (BeansException ignored) {
}
}, 15, TimeUnit.SECONDS);
}
}
@Override
public void onHiding(WindowEvent windowEvent) {
super.onHiding(windowEvent);
List<Task<?>> tasks = new ArrayList<>(taskProgressView.getTasks());
for (Task<?> task : tasks) {
task.cancel();
}
// scheduledExecutorService.shutdown();
// scheduledExecutorService.shutdownNow();
}
@Override
public void onHidden(WindowEvent windowEvent) {
System.out.println("windowEvent = " + windowEvent);
super.onHidden(windowEvent);
// Platform.exit();
}
/**
* 打开 配置 窗口
*/
public void openConfigWindow(ActionEvent actionEvent) {
FxmlUtils.newLoaderAsyncWithRunLater("/ui/configs.fxml", null, loader -> {
Scene scene = new Scene(loader.getRoot());
Stage stage = new Stage();
stage.initOwner(root.getScene().getWindow());
stage.initModality(Modality.NONE);
stage.setTitle("选项");
stage.setScene(scene);
stage.show();
});
}
public void createNewU8ContractSyncTaskAction(ActionEvent event) {
try {
ContractSyncTask task = new ContractSyncTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
} catch (Exception e) {
handleException("创建U8合同同步任务失败", e);
}
}
public void openInBrowse(ActionEvent event) {
MenuItem source = (MenuItem) event.getSource();
String url = (String) source.getUserData();
DesktopUtils.showInBrowse(url);
}
public void openGroupRKResourceWindow(ActionEvent event) {
CloudRkManagerWindowController.show();
}
public void openTycResourceWindow(ActionEvent event) {
CloudTycManagerWindowController.show();
}
public void openYongYouResourceWindow(ActionEvent event) {
YongYouU8ManagerWindowController.show();
}
public void onShowEmployeeManagerWindowAction(ActionEvent event) {
showInOwner(EmployeeManagerWindowController.class);
}
public void onShowDepartmentManagerWindowAction(ActionEvent event) {
showInOwner(DepartmentManagerWindowController.class);
}
public void onShowRolesManagerWindowAction(ActionEvent event) {
showInOwner(EmployeeRoleManagerWindowController.class);
}
public void onShowFunctionManagerWindowAction(ActionEvent event) {
showInOwner(EmployeeFunctionsManagerWindowController.class);
}
public void onShowBankManagerWindowAction(ActionEvent event) {
showInOwner(BankManagerWindowController.class);
}
public void onShowInventoryManagerWindowAction(ActionEvent event) {
showInOwner(InventoryManagerWindowController.class);
}
/**
* 打开任务监控中心窗口
*/
public void onShowTaskMonitorWindowAction(ActionEvent event) {
showInOwner(TaskMonitorViewController.class);
}
}

View File

@@ -0,0 +1,584 @@
package com.ecep.contract.controller;
import static com.ecep.contract.AppV2.DEFAULT_DB_DATABASE;
import static com.ecep.contract.AppV2.DEFAULT_DB_HOST;
import static com.ecep.contract.AppV2.DEFAULT_DB_PASSWORD;
import static com.ecep.contract.AppV2.DEFAULT_DB_PORT;
import static com.ecep.contract.AppV2.DEFAULT_DB_USERNAME;
import java.io.FileOutputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.util.StringUtils;
import com.ecep.contract.Desktop;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.SpringApp;
import com.ecep.contract.ds.other.controller.HomeWindowController;
import com.zaxxer.hikari.HikariDataSource;
import javafx.application.Platform;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import lombok.Setter;
public class LoginWidowController implements MessageHolder {
private static final Logger logger = LoggerFactory.getLogger(LoginWidowController.class);
@Setter
MessageHolder holder;
@Setter
Stage primaryStage;
@Setter
Properties properties;
@Override
public void addMessage(Level level, String message) {
holder.addMessage(level, message);
}
private void storeProperties() {
try (FileOutputStream fos = new FileOutputStream("config.properties")) {
// 保存到文件
properties.store(fos, "Updated config.properties");
info("配置文件已更新!");
} catch (java.io.IOException e) {
error("保存配置文件失败:" + e.getMessage());
}
}
String getHost() {
return properties.getProperty("db.server.host", DEFAULT_DB_HOST);
}
String getPort() {
return properties.getProperty("db.server.port", DEFAULT_DB_PORT);
}
String getDatabase() {
return properties.getProperty("db.server.database", DEFAULT_DB_DATABASE);
}
public String getUserName() {
return properties.getProperty("db.server.username", DEFAULT_DB_USERNAME);
}
public String getPassword() {
return properties.getProperty("db.server.password", DEFAULT_DB_PASSWORD);
}
public void tryLogin() {
// CompletableFuture<ButtonType> future = new CompletableFuture<>();
// 检查配置文件中是否保存用户名和密码
String userName = getUserName();
if (StringUtils.hasText(userName)) {
try {
EmployeeInfo employeeInfo = tryToConnect(userName, getPassword());
if (employeeInfo.errorCode < 0) {
error("登录失败:错误代码=" + employeeInfo.errorCode);
} else {
logined(employeeInfo);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
} else {
showUserNameLoginDialog(null);
}
}
CompletableFuture<List<LoginWidowController.MacIP>> getMacAndIP() {
return CompletableFuture.supplyAsync(() -> {
// mac ip
List<LoginWidowController.MacIP> list = new ArrayList<>();
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface anInterface = interfaces.nextElement();
if (anInterface.isLoopback()) {
continue;
}
byte[] hardwareAddress = anInterface.getHardwareAddress();
if (hardwareAddress == null) {
continue;
}
Enumeration<InetAddress> inetAddresses = anInterface.getInetAddresses();
if (!inetAddresses.hasMoreElements()) {
continue;
}
// -分割16进制表示法
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hardwareAddress.length; i++) {
sb.append(String.format("%02X%s", hardwareAddress[i], i < hardwareAddress.length - 1 ? "-" : ""));
}
while (inetAddresses.hasMoreElements()) {
InetAddress inetAddress = inetAddresses.nextElement();
if (inetAddress instanceof Inet4Address) {
list.add(new LoginWidowController.MacIP(sb.toString(), inetAddress.getHostAddress()));
}
}
}
} catch (SocketException e) {
throw new RuntimeException(e);
}
return list;
});
}
private EmployeeInfo tryToConnect(String userName, String password) throws SQLException {
String host = getHost();
String port = getPort();
String database = getDatabase();
if (logger.isDebugEnabled()) {
logger.debug("try to connect db server host:{},port:{},database:{},user:{},pwd:{}", host, port, database, userName, "*");
}
try (HikariDataSource source = DataSourceBuilder.create()
.type(HikariDataSource.class)
.url("jdbc:mysql://" + host + ":" + port + "/" + database)
.username(userName)
.password(password)
.driverClassName("com.mysql.cj.jdbc.Driver")
.build()) {
CompletableFuture<List<MacIP>> macAndIP = getMacAndIP();
macAndIP.thenAccept(map -> {
if (map.isEmpty()) {
error("查询本地地址信息失败,请联系管理员或重启应用!");
logger.error("查询本地地址信息失败");
}
});
try (Connection connection = source.getConnection()) {
info("连接到数据库 " + host + ", 请稍后,正在查询账户与本绑定的登录信息...");
if (logger.isDebugEnabled()) {
logger.debug("connection.getClientInfo() = {}", connection.getClientInfo());
}
// 必须要阻塞,否则 connection 会断开
EmployeeInfo employeeInfo = tryLoginWithEmployeeBind(connection, macAndIP).get();
createSession(connection, employeeInfo);
return employeeInfo;
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
}
}
private void createSession(Connection connection, EmployeeInfo employeeInfo) {
employeeInfo.sessionId = addHistory(connection, employeeInfo.employeeId, employeeInfo.binds.getFirst());
}
private int addHistory(Connection connection, int employeeId, MacIP macIP) {
try {
String sql = "INSERT INTO EMPLOYEE_LOGIN_HISTORY (IP, MAC, DT, EMPLOYEE_ID) VALUES (?, ?, ?, ?)";
try (PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, macIP.ip);
ps.setString(2, macIP.mac);
ps.setObject(3, LocalDateTime.now()); // 根据数据库字段类型调整
ps.setInt(4, employeeId);
ps.executeUpdate();
// 返回 新的主键值
ResultSet generatedKeys = ps.getGeneratedKeys();
if (generatedKeys.next()) {
return generatedKeys.getInt(1);
}
}
} catch (SQLException e) {
holder.error("申请新会话编号失败");
logger.error("unable insert EMPLOYEE_LOGIN_HISTORY, ", e);
}
return -1;
}
static class MacIP {
String ip;
String mac;
public MacIP(String mac, String ip) {
this.mac = mac;
this.ip = ip;
}
}
static class EmployeeInfo {
Integer employeeId;
List<MacIP> binds = new ArrayList<>();
SimpleStringProperty name = new SimpleStringProperty();
SimpleBooleanProperty active = new SimpleBooleanProperty();
int sessionId;
int errorCode = 0;
public EmployeeInfo(Integer employeeId) {
this.employeeId = employeeId;
}
public static EmployeeInfo error(int code) {
EmployeeInfo employeeInfo = new EmployeeInfo(null);
employeeInfo.errorCode = code;
return employeeInfo;
}
public void addBind(MacIP macIP) {
binds.add(macIP);
}
}
private CompletableFuture<EmployeeInfo> tryLoginWithEmployeeBind(Connection connection, CompletableFuture<List<MacIP>> macAndIP) {
CompletableFuture<EmployeeInfo> future = new CompletableFuture<>();
macAndIP.thenAccept(macIPS -> {
if (macIPS.isEmpty()) {
future.complete(EmployeeInfo.error(-1));
return;
}
HashMap<Integer, EmployeeInfo> employeeMap = new HashMap<>();
for (MacIP macIP : macIPS) {
for (Integer employeeId : findAllBindEmployee(connection, macIP)) {
employeeMap.computeIfAbsent(employeeId, k -> new EmployeeInfo(employeeId)).addBind(macIP);
}
}
if (employeeMap.isEmpty()) {
error("本机未绑定登录信息,请联系管理员更新.");
// 当前计算机的信息,如用户名,计算机名等
String username = System.getProperty("user.name");
String computerName = System.getenv("COMPUTERNAME");
for (MacIP macIP : macIPS) {
if (macIP.ip.equals("127.0.0.1")) {
continue;
}
registerComputer(username, computerName, connection, macIP);
}
future.complete(EmployeeInfo.error(-2));
return;
}
if (employeeMap.size() == 1) {
// 直接登录
EmployeeInfo employeeInfo = employeeMap.values().stream().findFirst().get();
// issue #1 登录成功后没有更新员工信息
fill(connection, employeeInfo);
future.complete(employeeInfo);
} else {
List<EmployeeInfo> list = employeeMap.values().stream().toList();
// 选择登录
Platform.runLater(() -> {
EmployeeInfo info = showEmployeeSelectDialog(list);
future.complete(Objects.requireNonNullElseGet(info, () -> EmployeeInfo.error(-3)));
});
for (EmployeeInfo info : list) {
fill(connection, info);
}
}
});
return future;
}
private EmployeeInfo showEmployeeSelectDialog(List<EmployeeInfo> list) {
Stage stage = new Stage();
stage.initOwner(primaryStage);
stage.setTitle("请选择账户登录系统");
stage.setWidth(360);
stage.setHeight(280);
Label label = new Label("您的主机关联了以下账户,请选择一个登录");
label.setPadding(new Insets(10, 0, 10, 10));
ListView<Label> listView = new ListView<>();
EventHandler<MouseEvent> eventHandler = event -> {
if (event.getClickCount() == 2) {
// listView.getSelectionModel().select(cb);
stage.close();
}
};
for (EmployeeInfo employeeInfo : list) {
Label cb = new Label();
cb.setUserData(employeeInfo);
cb.textProperty().bind(employeeInfo.name);
cb.setPadding(new Insets(5));
cb.setOnMouseClicked(eventHandler);
listView.getItems().add(cb);
}
// 创建 BorderPane 并设置边距
BorderPane borderPane = new BorderPane();
borderPane.setPadding(new Insets(10));
borderPane.setTop(label);
borderPane.setCenter(listView);
Button bottom = new Button("确定");
bottom.setDefaultButton(true);
bottom.setOnAction(event -> {
Label selectedItem = listView.getSelectionModel().getSelectedItem();
if (selectedItem == null) {
// 没选中,退出继续选择
return;
}
stage.close();
});
BorderPane.setAlignment(bottom, javafx.geometry.Pos.CENTER);
borderPane.setBottom(bottom);
stage.setScene(new Scene(borderPane));
stage.setOnCloseRequest(event -> {
Label selectedItem = listView.getSelectionModel().getSelectedItem();
if (selectedItem == null) {
// 关闭时,如何没有做选择,不关闭窗口
event.consume();
}
});
stage.showAndWait();
Label selectedItem = listView.getSelectionModel().getSelectedItem();
if (selectedItem == null) {
throw new NoSuchElementException("请选择工号登录系统");
}
return (EmployeeInfo) selectedItem.getUserData();
}
private void fill(Connection connection, EmployeeInfo info) {
try {
ResultSet rs = connection.createStatement()
.executeQuery("SELECT * FROM EMPLOYEE where ID = " + info.employeeId);
if (rs.next()) {
String name = rs.getString("NAME");
boolean isActive = rs.getBoolean("IS_ACTIVE");
Platform.runLater(() -> {
info.name.set(name);
info.active.set(isActive);
});
}
} catch (SQLException e) {
logger.error("查询{}失败", info.employeeId, e);
}
}
private void registerComputer(String username, String computerName, Connection connection, MacIP macIP) {
info("正在注册本机信息(MAC:" + macIP.mac + ", IP:" + macIP.ip + ")...");
String sql = "INSERT INTO EMPLOYEE_AUTH_BIND (IP,MAC,DESCRIPTION)VALUES(?,?,?)";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setString(1, macIP.ip);
stmt.setString(2, macIP.mac);
// 当前计算机的信息,如用户名,计算机名等
stmt.setString(3, username + "," + computerName);
if (stmt.execute()) {
info("注册成功");
}
} catch (SQLException e) {
error(String.format("注册失败请联系管理员或重启应用MAC: %s, IP: %s", macIP.mac, macIP.ip));
logger.error("注册失败 mac:{}, ip:{}", macIP.mac, macIP.ip, e);
}
}
List<Integer> findAllBindEmployee(Connection connection, MacIP macIP) {
List<Integer> list = new ArrayList<>();
// 优化后代码
String sql = "SELECT * FROM EMPLOYEE_AUTH_BIND WHERE IP = ? AND MAC = ?";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setString(1, macIP.ip);
stmt.setString(2, macIP.mac);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
int id = rs.getInt("EMPLOYEE_ID");
if (id > 0) {
list.add(id);
}
}
}
} catch (SQLException e) {
throw new RuntimeException(
String.format("查询本机绑定信息异常请联系管理员或重启应用MAC: %s, IP: %s", macIP.mac, macIP.ip),
e
);
}
return list;
}
private void showUserNameLoginDialog(Exception exception) {
Stage stage = new Stage();
stage.initOwner(primaryStage);
stage.setTitle("登录");
// 创建 BorderPane 并设置边距
BorderPane borderPane = new BorderPane();
borderPane.setPadding(new Insets(10));
// 创建布局
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
// 为整个 GridPane 设置外边距
//GridPane.setMargin(grid, new Insets(10));
// 账户输入框
Label userLabel = new Label("账户:");
TextField userField = new TextField();
{
String username = getUserName();
if (StringUtils.hasText(username)) {
userField.setText(username);
}
grid.add(userLabel, 0, 0);
grid.add(userField, 1, 0);
}
// 密码输入框
Label passwordLabel = new Label("密码:");
PasswordField passwordField = new PasswordField();
{
String password = getPassword();
if (StringUtils.hasText(password)) {
passwordField.setText(password);
}
grid.add(passwordLabel, 0, 1);
grid.add(passwordField, 1, 1);
}
// 记住密码复选框
CheckBox rememberCheckBox = new CheckBox("记住账户密码");
{
String property = properties.getProperty("username_password.remember", "false");
if (Boolean.parseBoolean(property)) {
rememberCheckBox.setSelected(true);
}
}
grid.add(rememberCheckBox, 1, 2);
// 错误消息提示
Label exceptionLabel = new Label();
grid.add(exceptionLabel, 0, 3);
exceptionLabel.setWrapText(true);
GridPane.setColumnSpan(exceptionLabel, 2);
if (exception == null) {
exceptionLabel.setVisible(false);
} else {
exceptionLabel.setText(exception.getMessage());
exceptionLabel.setVisible(true);
}
borderPane.setCenter(grid);
// 登录按钮
Button loginButton = new Button("登录");
loginButton.setDefaultButton(true);
borderPane.setBottom(loginButton);
BorderPane.setAlignment(loginButton, javafx.geometry.Pos.CENTER);
// 登录按钮点击事件
loginButton.setOnAction(event -> {
String username = userField.getText();
String password = passwordField.getText();
boolean remember = rememberCheckBox.isSelected();
// 尝试连接数据库
exceptionLabel.setText("");
exceptionLabel.setVisible(false);
try {
EmployeeInfo employeeInfo = tryToConnect(username, password);
if (employeeInfo.errorCode < 0) {
exceptionLabel.setText("登录失败:错误代码=" + employeeInfo.errorCode);
exceptionLabel.setVisible(true);
} else {
logined(employeeInfo);
}
} catch (SQLException ex) {
//
exceptionLabel.setText("数据库错误:" + ex.getMessage());
exceptionLabel.setVisible(true);
return;
}
properties.setProperty("db.server.username", username);
properties.setProperty("db.server.password", password);
properties.setProperty("username_password.remember", Boolean.toString(remember));
// 如果勾选了“记住密码”,则更新配置文件
if (remember) {
CompletableFuture.runAsync(() -> {
storeProperties();
});
}
// 关闭登录窗口
stage.close();
});
// 创建场景并设置到窗口
Scene scene = new Scene(borderPane, 400, 260);
stage.setScene(scene);
// stage.setAlwaysOnTop(true);
stage.setResizable(false);
stage.showAndWait();
}
private void logined(EmployeeInfo employeeInfo) {
info("欢迎 " + employeeInfo.name.get());
if (!SpringApp.isRunning()) {
info("请稍后...");
}
Desktop.instance.setActiveEmployeeId(employeeInfo.employeeId);
Desktop.instance.setSessionId(employeeInfo.sessionId);
tryShowHomeWindow();
}
void tryShowHomeWindow() {
try {
while (!SpringApp.isRunning()) {
System.out.println("等待启动");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 必须要等待启动成功后才能关闭主场景,否则进程结束程序退出
HomeWindowController.show().thenRun(() -> Platform.runLater(primaryStage::close));
}
}

View File

@@ -0,0 +1,13 @@
package com.ecep.contract.controller;
import com.ecep.contract.controller.tab.Skin;
public interface ManagerSkin extends Skin {
void initializeTable();
default void dispose() {
}
}

View File

@@ -0,0 +1,150 @@
package com.ecep.contract.controller;
import java.io.File;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.cloud.u8.YongYouU8Service;
import com.ecep.contract.constant.CompanyVendorConstant;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.ds.customer.service.CompanyCustomerFileService;
import com.ecep.contract.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.util.StringConfig;
import jakarta.annotation.PreDestroy;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
@Lazy
@Scope("prototype")
@Component
public class SysConfWindowController extends BaseController {
private static final Logger logger = LoggerFactory.getLogger(SysConfWindowController.class);
public Label companyContractPathLabel;
public TextField u8DataBaseServerHostField;
public TextField u8DataBaseCatalogField;
public TextField u8DataBaseUserNameField;
public TextField u8DataBasePasswordField;
public Label vendorPathLabel;
public Label vendorEvaluationFormTemplateLabel;
public Label customerPathLabel;
public Label customerEvaluationFormTemplateLabel;
public Label customerSaleBookPathLabel;
StringConfig contractPathConfig = new StringConfig(ContractService.CONTRACT_BASE_PATH);
StringConfig vendorPathConfig = new StringConfig(CompanyVendorConstant.KEY_BASE_PATH);
StringConfig vendorEvaluationFormTemplateConfig = new StringConfig(
CompanyVendorConstant.KEY_EVALUATION_FORM_TEMPLATE);
StringConfig customerPathConfig = new StringConfig(CompanyCustomerService.KEY_BASE_PATH);
StringConfig customerEvaluationFormTemplateConfig = new StringConfig(
CompanyCustomerFileService.KEY_EVALUATION_FORM_TEMPLATE);
StringConfig customerSaleBookPathConfig = new StringConfig(CompanyCustomerService.KEY_SALEBOOK_PATH);
StringConfig u8DataBaseServerHostConfig = new StringConfig(YongYouU8Service.KEY_HOST_IP);
StringConfig u8DataBaseCatalogConfig = new StringConfig(YongYouU8Service.KEY_DATABASE);
StringConfig u8DataBaseUserNameConfig = new StringConfig(YongYouU8Service.KEY_USER_NAME);
StringConfig u8DataBasePasswordConfig = new StringConfig(YongYouU8Service.KEY_PASSWORD);
public void initialize() {
contractPathConfig.setControl(companyContractPathLabel);
contractPathConfig.initialize();
vendorPathConfig.setControl(vendorPathLabel);
vendorPathConfig.initialize();
vendorEvaluationFormTemplateConfig.setControl(vendorEvaluationFormTemplateLabel);
vendorEvaluationFormTemplateConfig.initialize();
customerPathConfig.setControl(customerPathLabel);
customerPathConfig.initialize();
customerEvaluationFormTemplateConfig.setControl(customerEvaluationFormTemplateLabel);
customerEvaluationFormTemplateConfig.initialize();
customerSaleBookPathConfig.setControl(customerSaleBookPathLabel);
customerSaleBookPathConfig.initialize();
u8DataBaseServerHostConfig.setControl(u8DataBaseServerHostField);
u8DataBaseServerHostConfig.initialize();
u8DataBaseCatalogConfig.setControl(u8DataBaseCatalogField);
u8DataBaseCatalogConfig.initialize();
u8DataBaseUserNameConfig.setControl(u8DataBaseUserNameField);
u8DataBaseUserNameConfig.initialize();
u8DataBasePasswordConfig.setControl(u8DataBasePasswordField);
u8DataBasePasswordConfig.initialize();
logger.debug("#initialize()");
}
private void directoryChoose(StringConfig config, String title, String key, ActionEvent event) {
DirectoryChooser chooser = new DirectoryChooser();
chooser.setTitle(title);
File value = new File(config.getProperty().getValue());
chooser.setInitialDirectory(value);
Node node = (Node) event.getSource();
File selected = chooser.showDialog(node.getScene().getWindow());
if (selected != null) {
config.getProperty().setValue(selected.getAbsolutePath());
config.save();
}
}
public void changeCompanyContractPath(ActionEvent actionEvent) {
directoryChoose(contractPathConfig, "请选择合同目录", ContractService.CONTRACT_BASE_PATH, actionEvent);
}
public void changeVendorPath(ActionEvent actionEvent) {
directoryChoose(vendorPathConfig, "请选择供应商目录", CompanyVendorConstant.KEY_BASE_PATH, actionEvent);
}
public void changeCustomerPath(ActionEvent actionEvent) {
directoryChoose(customerPathConfig, "请选择客户目录", CompanyCustomerService.KEY_BASE_PATH, actionEvent);
}
public void changeCustomerSaleBookPath(ActionEvent actionEvent) {
directoryChoose(customerSaleBookPathConfig, "请选择销售台账目录", "customer.salebook.path", actionEvent);
}
// 模拟销毁方法
@PreDestroy
public void destroy() {
}
private void fileChoose(StringConfig config, String title, String key, ActionEvent event) {
FileChooser chooser = new FileChooser();
chooser.setTitle(title);
File value = new File(config.getProperty().getValue());
chooser.setInitialDirectory(value.getParentFile());
chooser.setInitialFileName(value.getName());
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(" 模板文件(*.xlsx)", "*.xlsx"));
Node node = (Node) event.getSource();
File selected = chooser.showOpenDialog(node.getScene().getWindow());
if (selected != null) {
config.getProperty().setValue(selected.getAbsolutePath());
config.save();
}
}
public void changeCustomerEvaluationFormTemplate(ActionEvent actionEvent) {
fileChoose(customerEvaluationFormTemplateConfig, "请选择客户资信评估表模板",
CompanyCustomerFileService.KEY_EVALUATION_FORM_TEMPLATE, actionEvent);
}
public void changeVendorEvaluationFormTemplate(ActionEvent actionEvent) {
fileChoose(vendorEvaluationFormTemplateConfig, "请选择供方调查评价表模板",
CompanyVendorConstant.KEY_EVALUATION_FORM_TEMPLATE,
actionEvent);
}
}

View File

@@ -0,0 +1,360 @@
package com.ecep.contract.controller;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import com.ecep.contract.util.FxmlPath;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.Desktop;
import com.ecep.contract.Message;
import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.task.MonitoredTask;
import com.ecep.contract.task.TaskHistory;
import com.ecep.contract.task.TaskMonitorCenter;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.WindowEvent;
/**
* 任务监控界面控制器
*/
@Lazy
@Scope("prototype")
@Component
@FxmlPath(value = "/ui/task/TaskMonitorView.fxml")
public class TaskMonitorViewController extends BaseController {
@FXML
private TableView<MonitoredTask<?>> activeTasksTable;
@FXML
private TableColumn<MonitoredTask<?>, String> activeTaskIdColumn;
@FXML
private TableColumn<MonitoredTask<?>, String> activeTaskTitleColumn;
@FXML
private TableColumn<MonitoredTask<?>, Number> activeTaskProgressColumn;
@FXML
private TableColumn<MonitoredTask<?>, String> activeTaskStatusColumn;
@FXML
private TableView<TaskHistory> historyTasksTable;
@FXML
private TableColumn<TaskHistory, String> historyTaskIdColumn;
@FXML
private TableColumn<TaskHistory, String> historyTaskTitleColumn;
@FXML
private TableColumn<TaskHistory, String> historyTaskStatusColumn;
@FXML
private TableColumn<TaskHistory, String> historyTaskStartTimeColumn;
@FXML
private TableColumn<TaskHistory, String> historyTaskExecutionTimeColumn;
@FXML
private Button cancelTaskButton;
@FXML
private Button clearHistoryButton;
@FXML
private Button refreshExecutorInfoButton;
@FXML
private TableView<ExecutorInfo> executorInfoTable;
@FXML
private TableColumn<ExecutorInfo, String> executorFieldColumn;
@FXML
private TableColumn<ExecutorInfo, String> executorValueColumn;
@FXML
private TableColumn<ExecutorInfo, String> executorDescriptionColumn;
public TaskMonitorViewController() {
}
/**
* 用于存储ExecutorService信息的内部类
*/
public static class ExecutorInfo {
private final String field;
private final String value;
private final String description;
public ExecutorInfo(String field, String value, String description) {
this.field = field;
this.value = value;
this.description = description;
}
public String getField() {
return field;
}
public String getValue() {
return value;
}
public String getDescription() {
return description;
}
}
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
getTitle().set("任务监控中心");
// 初始化活动任务表格
activeTaskIdColumn.setCellValueFactory(new PropertyValueFactory<>("taskId"));
activeTaskTitleColumn.setCellValueFactory(new PropertyValueFactory<>("title"));
// 进度条列
activeTaskProgressColumn.setCellValueFactory(
param -> param.getValue().progressProperty().divide(param.getValue().totalWorkProperty()));
activeTaskProgressColumn.setCellFactory(column -> new TableCell<>() {
private final ProgressBar progressBar = new ProgressBar();
{
// 设置进度条最大宽度和水平扩展属性
progressBar.setMaxWidth(Double.MAX_VALUE);
HBox.setHgrow(progressBar, Priority.ALWAYS);
}
@Override
protected void updateItem(Number item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
return;
}
setGraphic(progressBar);
progressBar.setProgress(item.doubleValue());
}
});
// 状态列
activeTaskStatusColumn.setCellValueFactory(cellData -> {
MonitoredTask<?> task = cellData.getValue();
String description = "";
if (task.stateProperty().get() != null) {
description = task.stateProperty().get().getDescription();
}
return new SimpleStringProperty(description);
});
// 初始化历史任务表格
historyTaskIdColumn.setCellValueFactory(new PropertyValueFactory<>("taskId"));
historyTaskTitleColumn.setCellValueFactory(new PropertyValueFactory<>("title"));
historyTaskStatusColumn.setCellValueFactory(
cellData -> new SimpleStringProperty(cellData.getValue().getStatus().getDescription()));
historyTaskStartTimeColumn.setCellValueFactory(
cellData -> new SimpleStringProperty(MyDateTimeUtils.format(cellData.getValue().getStartTime())));
historyTaskExecutionTimeColumn.setCellValueFactory(
cellData -> new SimpleStringProperty(cellData.getValue().getFormattedExecutionTime()));
// 绑定数据
TaskMonitorCenter taskMonitorCenter = Desktop.instance.getTaskMonitorCenter();
activeTasksTable.setItems(taskMonitorCenter.getActiveTasks());
historyTasksTable.setItems(taskMonitorCenter.getTaskHistory());
// 添加任务双击事件(显示详情)
historyTasksTable.setRowFactory(tv -> {
TableRow<TaskHistory> row = new TableRow<>();
row.setOnMouseClicked(event -> {
if (event.getClickCount() == 2 && !row.isEmpty()) {
TaskHistory history = row.getItem();
showTaskDetails(history);
}
});
return row;
});
cancelTaskButton.disableProperty().bind(activeTasksTable.getSelectionModel().selectedItemProperty().isNull());
// 初始化Executor信息表格
initializeExecutorInfoTable();
}
/**
* 初始化Executor信息表格
*/
private void initializeExecutorInfoTable() {
executorFieldColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getField()));
executorValueColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getValue()));
executorDescriptionColumn
.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getDescription()));
// 初始加载Executor信息
refreshExecutorInfo();
// 绑定刷新按钮事件
refreshExecutorInfoButton.setOnAction(event -> refreshExecutorInfo());
}
/**
* 刷新Executor信息
*/
@FXML
private void refreshExecutorInfo() {
executorInfoTable.getItems().clear();
try {
ExecutorService executorService = Desktop.instance.getExecutorService();
if (executorService instanceof ThreadPoolExecutor threadPoolExecutor) {
executorInfoTable.getItems().add(new ExecutorInfo(
"PoolSize",
String.valueOf(threadPoolExecutor.getPoolSize()),
"PoolSize"));
executorInfoTable.getItems().add(new ExecutorInfo(
"活动线程数",
String.valueOf(threadPoolExecutor.getActiveCount()),
"当前正在执行任务的线程数"));
// 添加线程池信息
executorInfoTable.getItems().add(new ExecutorInfo(
"核心线程数",
String.valueOf(threadPoolExecutor.getCorePoolSize()),
"线程池维护的最小线程数"));
executorInfoTable.getItems().add(new ExecutorInfo(
"最大线程数",
String.valueOf(threadPoolExecutor.getMaximumPoolSize()),
"线程池允许的最大线程数"));
executorInfoTable.getItems().add(new ExecutorInfo(
"KeepAliveTime",
String.valueOf(threadPoolExecutor.getKeepAliveTime(TimeUnit.SECONDS)),
"the thread keep-alive time, which is the amount of time that threads may remain idle before being terminated. Threads that wait this amount of time without processing a task will be terminated if there are more than the core number of threads currently in the pool, or if this pool allows core thread timeout."));
executorInfoTable.getItems().add(new ExecutorInfo(
"任务队列大小",
String.valueOf(threadPoolExecutor.getQueue().size()),
"等待执行的任务数量"));
executorInfoTable.getItems().add(new ExecutorInfo(
"已完成任务数",
String.valueOf(threadPoolExecutor.getCompletedTaskCount()),
"已完成执行的任务总数"));
executorInfoTable.getItems().add(new ExecutorInfo(
"LargestPoolSize",
String.valueOf(threadPoolExecutor.getLargestPoolSize()),
"线程池当前状态"));
// 如果是ScheduledExecutorService添加额外信息
if (executorService instanceof ScheduledExecutorService) {
executorInfoTable.getItems().add(new ExecutorInfo(
"线程池类型",
"ScheduledExecutorService",
"可调度的线程池"));
} else {
executorInfoTable.getItems().add(new ExecutorInfo(
"线程池类型",
"ThreadPoolExecutor",
"普通线程池"));
}
} else {
executorInfoTable.getItems().add(new ExecutorInfo(
"错误",
"未知的ExecutorService类型",
"无法获取线程池详细信息"));
}
} catch (Exception e) {
executorInfoTable.getItems().add(new ExecutorInfo(
"异常",
e.getMessage(),
"获取ExecutorService信息时发生错误"));
}
}
/**
* 取消选中的任务
*/
@FXML
private void onCancelTask() {
MonitoredTask<?> selectedTask = activeTasksTable.getSelectionModel().getSelectedItem();
if (selectedTask != null) {
Desktop.instance.getTaskMonitorCenter().cancelTask(selectedTask.getTaskId());
}
}
/**
* 清空历史记录
*/
@FXML
private void onClearHistory() {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("确认操作");
alert.setHeaderText("清空任务历史");
alert.setContentText("确定要清空所有任务历史记录吗?此操作不可恢复。");
alert.showAndWait().ifPresent(response -> {
if (response == ButtonType.OK) {
Desktop.instance.getTaskMonitorCenter().getTaskHistory().clear();
}
});
}
/**
* 显示任务详情
*/
private void showTaskDetails(TaskHistory history) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("任务详情");
alert.setHeaderText(history.getTitle());
ScrollPane scrollPane = new ScrollPane();
scrollPane.setPrefHeight(300);
VBox vBox = new VBox();
Label taskIdLabel = new Label("任务ID: " + history.getTaskId());
Label statusLabel = new Label("状态: " + history.getStatus().getDescription());
Label startTimeLabel = new Label("开始时间: " + MyDateTimeUtils.format(history.getStartTime()));
Label endTimeLabel = new Label("结束时间: " + MyDateTimeUtils.format(history.getEndTime()));
Label executionTimeLabel = new Label("执行耗时: " + history.getFormattedExecutionTime());
vBox.getChildren().addAll(taskIdLabel, statusLabel, startTimeLabel, endTimeLabel, executionTimeLabel);
Label messageLabel = new Label("消息:");
vBox.getChildren().add(messageLabel);
for (Message msg : history.getMessages()) {
Text text = new Text(msg.getMessage());
if (msg.getLevel() == Level.INFO) {
text.setFill(Color.BLUE);
} else if (msg.getLevel() == Level.WARNING) {
text.setFill(Color.ORANGE);
} else if (msg.getLevel() == Level.SEVERE) {
text.setFill(Color.RED);
} else if (msg.getLevel() == Level.CONFIG) {
text.setFill(Color.GRAY);
}
vBox.getChildren().add(text);
}
if (history.getErrorMessage() != null) {
alert.getDialogPane().setExpandableContent(new TextArea(history.getErrorMessage()));
}
scrollPane.setContent(vBox);
alert.getDialogPane().setContent(scrollPane);
alert.showAndWait();
}
/**
* 运行任务监控演示
*/
public void onRunTaskMonitorDemo(ActionEvent event) {
com.ecep.contract.task.DemoTask.runDemo();
}
}

View File

@@ -0,0 +1,70 @@
package com.ecep.contract.controller;
import com.ecep.contract.util.FxmlPath;
import org.controlsfx.control.ToggleSwitch;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.cloud.u8.ctx.CompanyCtx;
import com.ecep.contract.cloud.u8.ctx.ContractCtx;
import com.ecep.contract.ds.other.BooleanConfig;
import com.ecep.contract.ds.other.LocalDateConfig;
import com.ecep.contract.ds.other.LocalDateTimeConfig;
import com.ecep.contract.ds.other.StringConfig;
import javafx.fxml.FXML;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TextField;
import javafx.stage.WindowEvent;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/cloud/u8_config.fxml")
public class YongYouU8ConfigWindowController extends BaseController {
@FXML
private DatePicker auto_create_company_after;
@FXML
private TextField contract_latest_date;
@FXML
private TextField contract_latest_id;
@FXML
private TextField sync_elapse;
@FXML
private ToggleSwitch use_latest_id;
LocalDateConfig config1 = new LocalDateConfig(CompanyCtx.AUTO_CREATE_COMPANY_AFTER);
LocalDateTimeConfig config2 = new LocalDateTimeConfig(ContractCtx.KEY_SYNC_BY_LATEST_DATE);
StringConfig config3 = new StringConfig(ContractCtx.KEY_SYNC_BY_LATEST_ID);
StringConfig config4 = new StringConfig(ContractCtx.KEY_SYNC_ELAPSE);
BooleanConfig config5 = new BooleanConfig(ContractCtx.KEY_SYNC_USE_LATEST_ID);
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
getTitle().set("用友U8配置");
auto_create_company_after.setConverter(getCurrentEmployee().getLocalDateStringConverter());
config1.setControl(auto_create_company_after);
config1.initialize();
config2.setControl(contract_latest_date);
config2.setControlConverter(getCurrentEmployee().getLocalDateTimeStringConverter());
config2.initialize();
config3.setControl(contract_latest_id);
config3.initialize();
config4.setControl(sync_elapse);
config4.initialize();
config5.setControl(use_latest_id);
config5.initialize();
}
}

View File

@@ -0,0 +1,138 @@
package com.ecep.contract.controller;
import java.util.List;
import org.hibernate.Hibernate;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.StringUtils;
import com.ecep.contract.SpringApp;
import com.ecep.contract.cloud.u8.YongYouU8Service;
import com.ecep.contract.controller.company.CompanyWindowController;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.model.CloudYu;
import com.ecep.contract.model.Company;
import com.ecep.contract.ui.table.cell.CompanyTableCell;
import com.ecep.contract.ui.table.cell.LocalDateTimeTableCell;
import com.ecep.contract.vm.CloudYuInfoViewModel;
import jakarta.persistence.criteria.Path;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import lombok.Setter;
public class YongYouU8ManagerSkin
extends
AbstEntityManagerSkin<CloudYu, CloudYuInfoViewModel, YongYouU8ManagerSkin, YongYouU8ManagerWindowController>
implements ManagerSkin {
@Setter
private YongYouU8Service u8Service;
@Setter
private CompanyService companyService;
public YongYouU8ManagerSkin(YongYouU8ManagerWindowController controller) {
super(controller);
}
YongYouU8Service getU8Service() {
if (u8Service == null) {
u8Service = SpringApp.getBean(YongYouU8Service.class);
}
return u8Service;
}
CompanyService getCompanyService() {
if (companyService == null) {
companyService = SpringApp.getBean(CompanyService.class);
}
return companyService;
}
@Override
protected List<CloudYuInfoViewModel> loadTableData() {
String searchText = controller.searchKeyField.getText();
Specification<CloudYu> spec = null;
if (StringUtils.hasText(searchText)) {
Specification<CloudYu> companySpec = (root, query, builder) -> {
Path<Object> company = root.get("company");
return builder.or(
builder.like(company.get("name"), "%" + searchText + "%"),
builder.like(company.get("shortName"), "%" + searchText + "%"));
};
Specification<CloudYu> cloudIdSpec = (root, query, builder) -> {
return builder.like(root.get("cloudId"), "%" + searchText + "%");
};
Specification<CloudYu> exceptionSpec = (root, query, builder) -> {
return builder.like(root.get("exceptionMessage"), "%" + searchText + "%");
};
spec = Specification.anyOf(companySpec, cloudIdSpec, exceptionSpec);
}
Page<CloudYu> page = getU8Service().findAll(spec, getPageable());
updateFooter(page);
return page.map(CloudYuInfoViewModel::from).toList();
}
@Override
public void initializeTable() {
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
controller.companyColumn.setCellValueFactory(param -> param.getValue().getCompany());
controller.companyColumn.setCellFactory(param -> new CompanyTableCell<>(getCompanyService()));
controller.cloudIdColumn.setCellValueFactory(param -> param.getValue().getCloudId());
controller.latestUpdateColumn.setCellValueFactory(param -> param.getValue().getLatest());
controller.latestUpdateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
controller.cloudLatestColumn.setCellValueFactory(param -> param.getValue().getCloudLatest());
controller.cloudLatestColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
controller.descriptionColumn.setCellValueFactory(param -> param.getValue().getVendorCode());
}
@Override
protected void createContextMenu(ContextMenu contextMenu) {
MenuItem item2 = new MenuItem("刷新");
item2.setOnAction(this::onTableRefreshAction);
MenuItem item3 = new MenuItem("清空备注");
item3.setOnAction(this::onTableClearDescriptionAction);
contextMenu.getItems().addAll(item2, item3);
}
/**
* 请空选择行的注释
*
* @param event event
*/
public void onTableClearDescriptionAction(ActionEvent event) {
ObservableList<CloudYuInfoViewModel> selectedItems = getTableView().getSelectionModel().getSelectedItems();
if (selectedItems.isEmpty()) {
return;
}
for (CloudYuInfoViewModel selectedItem : selectedItems) {
CloudYu cloudYu = getU8Service().findById(selectedItem.getId().get());
selectedItem.getCustomerCode().set("");
if (selectedItem.copyTo(cloudYu)) {
CloudYu saved = getU8Service().save(cloudYu);
selectedItem.update(saved);
}
}
}
@Override
protected void onTableRowDoubleClickedAction(CloudYuInfoViewModel item) {
Company company = item.getCompany().get();
if (!Hibernate.isInitialized(item)) {
company = getCompanyService().findById(company.getId());
}
CompanyWindowController.show(company, getTableView().getScene().getWindow());
}
}

View File

@@ -0,0 +1,122 @@
package com.ecep.contract.controller;
import java.time.LocalDateTime;
import com.ecep.contract.util.FxmlPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.Desktop;
import com.ecep.contract.UITools;
import com.ecep.contract.model.CloudYu;
import com.ecep.contract.model.Company;
import com.ecep.contract.vm.CloudYuInfoViewModel;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/cloud/u8_manager.fxml")
public class YongYouU8ManagerWindowController
extends AbstManagerWindowController<CloudYu, CloudYuInfoViewModel, YongYouU8ManagerSkin> {
private static final Logger logger = LoggerFactory.getLogger(YongYouU8ManagerWindowController.class);
public static void show() {
show(YongYouU8ManagerWindowController.class, null);
}
public TableColumn<CloudYuInfoViewModel, Number> idColumn;
public TableColumn<CloudYuInfoViewModel, LocalDateTime> latestUpdateColumn;
public TableColumn<CloudYuInfoViewModel, Company> companyColumn;
public TableColumn<CloudYuInfoViewModel, String> cloudIdColumn;
public TableColumn<CloudYuInfoViewModel, LocalDateTime> cloudLatestColumn;
public TableColumn<CloudYuInfoViewModel, String> descriptionColumn;
@Override
public YongYouU8Service getViewModelService() {
return getSkin().getU8Service();
}
@Override
protected YongYouU8ManagerSkin createDefaultSkin() {
return new YongYouU8ManagerSkin(this);
}
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().set("用友U8");
}
/**
* 打开配置窗口
*/
public void onConfigAction(ActionEvent event) {
BaseController.show(YongYouU8ConfigWindowController.class, null);
}
/**
* 数据迁移,从 CloudInfo 迁移到 CloudRk
*/
public void onDateTransferAction(ActionEvent event) {
DateTransferTask task = new DateTransferTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
public void onPersonSyncAction(ActionEvent event) {
EmployeesSyncTask task = new EmployeesSyncTask();
UITools.showTaskDialogAndWait("员工数据同步", task, null);
}
public void onContractSyncAction(ActionEvent event) {
ContractSyncTask task = new ContractSyncTask();
UITools.showTaskDialogAndWait("合同数据增量同步", task, null);
}
public void onContractAllSyncAction(ActionEvent event) {
ContractSyncAllTask task = new ContractSyncAllTask();
UITools.showTaskDialogAndWait("合同数据全量同步", task, null);
}
public void onVendorSyncAction(ActionEvent event) {
VendorSyncTask task = new VendorSyncTask();
UITools.showTaskDialogAndWait("供应商数据同步", task, null);
}
public void onCustomerSyncAction(ActionEvent event) {
CustomerSyncTask task = new CustomerSyncTask();
UITools.showTaskDialogAndWait("客户数据同步", task, null);
}
public void onContractGroupSyncAction(ActionEvent event) {
ContractGroupSyncTask task = new ContractGroupSyncTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
public void onContractTypeSyncAction(ActionEvent event) {
ContractTypeSyncTask task = new ContractTypeSyncTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
public void onContractKindSyncAction(ActionEvent event) {
ContractKindSyncTask task = new ContractKindSyncTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
public void onVendorClassSyncAction(ActionEvent event) {
VendorClassSyncTask task = new VendorClassSyncTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
public void onCustomerClassSyncAction(ActionEvent event) {
CustomerClassSyncTask task = new CustomerClassSyncTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
}

View File

@@ -0,0 +1,43 @@
package com.ecep.contract.controller.bank;
import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.controller.ManagerSkin;
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
import com.ecep.contract.model.Bank;
import com.ecep.contract.vm.BankViewModel;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.TextFieldTableCell;
public class BankManagerSkin
extends AbstEntityManagerSkin<Bank, BankViewModel, BankManagerSkin, BankManagerWindowController>
implements ManagerSkin, EditableEntityTableTabSkin<Bank, BankViewModel> {
public BankManagerSkin(BankManagerWindowController controller) {
super(controller);
}
@Override
public void initializeTable() {
getTableView().setEditable(true);
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
controller.codeColumn.setCellValueFactory(param -> param.getValue().getCode());
controller.codeColumn.setCellFactory(TextFieldTableCell.forTableColumn());
controller.codeColumn.setOnEditCommit(this::onCodeColumnEditCommit);
controller.nameColumn.setCellValueFactory(param -> param.getValue().getName());
controller.nameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
controller.nameColumn.setOnEditCommit(this::onNameColumnEditCommit);
}
private void onCodeColumnEditCommit(TableColumn.CellEditEvent<BankViewModel, String> event) {
acceptCellEditEvent(event, BankViewModel::getCode);
}
private void onNameColumnEditCommit(TableColumn.CellEditEvent<BankViewModel, String> event) {
acceptCellEditEvent(event, BankViewModel::getName);
}
}

View File

@@ -0,0 +1,47 @@
package com.ecep.contract.controller.bank;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.controller.AbstManagerWindowController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.ds.other.service.BankService;
import com.ecep.contract.model.Bank;
import com.ecep.contract.vm.BankViewModel;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/bank-manager.fxml")
public class BankManagerWindowController
extends AbstManagerWindowController<Bank, BankViewModel, BankManagerSkin> {
@Autowired
private BankService bankService;
public TableColumn<BankViewModel, Number> idColumn;
public TableColumn<BankViewModel, String> nameColumn;
public TableColumn<BankViewModel, String> codeColumn;
@Override
protected BankManagerSkin createDefaultSkin() {
return new BankManagerSkin(this);
}
public void onCreateNewAction(ActionEvent event) {
}
public void onReBuildFilesAction(ActionEvent event) {
}
@Override
public BankService getViewModelService() {
return bankService;
}
}

View File

@@ -0,0 +1,82 @@
package com.ecep.contract.controller.bank.account;
import com.ecep.contract.SpringApp;
import com.ecep.contract.controller.company.CompanyWindowController;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.converter.BankStringConverter;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyBankAccount;
import com.ecep.contract.service.BankService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.service.CompanyStringConverter;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CompanyBankAccountViewModel;
import javafx.scene.control.Tab;
import javafx.scene.control.TextField;
import lombok.Setter;
public class BankAccountBaseTabSkin
extends AbstEntityBasedTabSkin<BankAccountWindowController, CompanyBankAccount, CompanyBankAccountViewModel>
implements TabSkin {
@Setter
private CompanyService companyService;
@Setter
private BankService bankService;
public BankAccountBaseTabSkin(BankAccountWindowController controller) {
super(controller);
viewModel = controller.getViewModel();
}
@Override
public Tab getTab() {
return controller.baseInfoTab;
}
@Override
public void initializeTab() {
initializeBaseTabCompanyFieldAutoCompletion(controller.companyField);
initializeBaseTabBankFieldAutoCompletion(controller.bankField);
controller.openingBankField.textProperty().bindBidirectional(viewModel.getOpeningBank());
controller.bankAccountField.textProperty().bindBidirectional(viewModel.getAccount());
// controller.descriptionField.textProperty().bindBidirectional(viewModel.getDescription());
controller.createdField.textProperty().bind(viewModel.getCreated().asString());
controller.versionLabel.textProperty().bind(viewModel.getVersion().asString());
controller.relativeCompanyBtn.disableProperty().bind(viewModel.getCompany().isNull());
controller.relativeCompanyBtn.setOnAction(event -> {
Company company = viewModel.getCompany().get();
if (company != null) {
CompanyWindowController.show(company, controller.root.getScene().getWindow());
}
});
}
private void initializeBaseTabCompanyFieldAutoCompletion(TextField textField) {
CompanyStringConverter converter = SpringApp.getBean(CompanyStringConverter.class);
UITools.autoCompletion(textField, viewModel.getCompany(), converter::suggest, converter);
}
private void initializeBaseTabBankFieldAutoCompletion(TextField textField) {
BankStringConverter converter = SpringApp.getBean(BankStringConverter.class);
UITools.autoCompletion(textField, viewModel.getBank(), converter::suggest, converter);
}
public BankService getBankService() {
if (bankService == null) {
bankService = getBean(BankService.class);
}
return bankService;
}
public CompanyService getCompanyService() {
if (companyService == null) {
companyService = getBean(CompanyService.class);
}
return companyService;
}
}

View File

@@ -0,0 +1,68 @@
package com.ecep.contract.controller.bank.account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.ds.company.service.CompanyBankAccountService;
import com.ecep.contract.model.CompanyBankAccount;
import com.ecep.contract.vm.CompanyBankAccountViewModel;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/company/bank-account.fxml")
public class BankAccountWindowController extends AbstEntityController<CompanyBankAccount, CompanyBankAccountViewModel> {
public BorderPane root;
public TabPane tabPane;
public Tab baseInfoTab;
public TextField companyField;
public Button relativeCompanyBtn;
public TextField bankField;
public TextField openingBankField;
public TextField bankAccountField;
public TextField createdField;
public TextArea descriptionField;
public Label versionLabel;
public Tab entityTab;
public static void show(CompanyBankAccountViewModel viewModel, Window window) {
show(BankAccountWindowController.class, viewModel, window);
}
@Autowired
private CompanyBankAccountService bankAccountService;
@Override
public void onShown(WindowEvent event) {
super.onShown(event);
getTitle().set("[" + viewModel.getId().get() + "] 银行账户详情");
}
@Override
protected void registerTabSkins() {
registerTabSkin(baseInfoTab, tab -> new BankAccountBaseTabSkin(this));
}
@Override
public CompanyBankAccountService getViewModelService() {
return bankAccountService;
}
}

View File

@@ -0,0 +1,28 @@
package com.ecep.contract.controller.company;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.model.Company;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.vm.CompanyViewModel;
import javafx.concurrent.Task;
public abstract class AbstCompanyBasedTabSkin
extends AbstEntityBasedTabSkin<CompanyWindowController, Company, CompanyViewModel>
implements TabSkin {
public AbstCompanyBasedTabSkin(CompanyWindowController controller) {
super(controller);
}
protected CompanyService getCompanyService() {
Task<CompanyService> task = new Task<>() {
@Override
protected CompanyService call() throws Exception {
return controller.getViewModelService();
}
};
return controller.getViewModelService();
}
}

View File

@@ -0,0 +1,56 @@
package com.ecep.contract.controller.company;
import java.util.Map;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.controller.table.TableOfTabSkin;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.vm.CompanyBasedViewModel;
import com.ecep.contract.vm.CompanyViewModel;
import com.ecep.contract.vm.IdentityViewModel;
import lombok.Setter;
public abstract class AbstCompanyTableTabSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>>
extends AbstEntityTableTabSkin<CompanyWindowController, Company, CompanyViewModel, T, TV>
implements TabSkin, TableOfTabSkin<Company, T, TV> {
@Setter
private CompanyService companyService;
public AbstCompanyTableTabSkin(CompanyWindowController controller) {
super(controller);
viewModel = controller.getViewModel();
}
@Override
protected TV createNewViewModel() {
TV model = super.createNewViewModel();
if (model instanceof CompanyBasedViewModel m) {
m.getCompany().set(getEntity());
}
return model;
}
protected CompanyService getCompanyService() {
return getParentService();
}
protected CompanyService getParentService() {
if (companyService == null) {
companyService = getBean(CompanyService.class);
}
return companyService;
}
@Override
public Map<String, Object> getSpecification(Company parent) {
Map<String, Object> params = getSpecification();
params.put("company", parent.getId());
return params;
}
}

View File

@@ -0,0 +1,187 @@
package com.ecep.contract.controller.company;
import java.lang.reflect.InvocationTargetException;
import java.time.format.DateTimeFormatter;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.UITools;
import com.ecep.contract.controller.BaseController;
import com.ecep.contract.util.FxmlUtils;
import com.ecep.contract.ds.company.repository.CompanyContactRepository;
import com.ecep.contract.model.CompanyContact;
import com.ecep.contract.vm.CompanyContactViewModel;
import javafx.application.Platform;
import javafx.scene.control.Button;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import javafx.util.converter.LocalDateStringConverter;
import lombok.Getter;
import lombok.Setter;
@Lazy
@Scope("prototype")
@Component
public class CompanyContactWindowController extends BaseController {
private static final Logger logger = LoggerFactory.getLogger(CompanyContactWindowController.class);
/**
* 显示界面
*/
public static void show(CompanyContactViewModel viewModel, Window window) {
String key = toKey(viewModel);
// 一个合同一个界面
if (toFront(key)) {
return;
}
FxmlUtils.newLoaderAsyncWithRunLater("/ui/contact.fxml", null, loader -> {
CompanyContactWindowController controller = loader.getController();
controller.setViewModel(viewModel);
controller.show(loader, window, Modality.NONE, key);
});
}
private static String toKey(CompanyContactViewModel viewModel) {
return viewModel.getClass().getName() + "-" + viewModel.getId().get();
}
public Tab baseInfoTab;
public BorderPane root;
public TabPane tabPane;
@Getter
@Setter
private CompanyContactViewModel viewModel;
@Autowired
private CompanyContactRepository companyContactRepository;
public TextField nameField;
public TextField positionField;
public TextField phoneField;
public DatePicker createdField;
public TextField u8CodeField;
public TextField emailField;
public TextField addressField;
public TextArea descriptionField;
public Label versionLabel;
public Button saveBtn;
private CompletableFuture<CompanyContact> companyContactLoadedFuture;
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().bind(viewModel.getName().map(v -> "[" + viewModel.getId().get() + "] " + viewModel.getName().getValue() + " 曾用名详情"));
}
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
if (logger.isDebugEnabled()) {
logger.debug("onShown");
}
initializeBaseTab();
companyContactLoadedFuture = CompletableFuture.supplyAsync(() -> {
Optional<CompanyContact> optional = companyContactRepository.findById(viewModel.getId().get());
if (optional.isPresent()) {
CompanyContact oldName = optional.get();
Platform.runLater(() -> {
viewModel.update(oldName);
viewModel.bindListener();
if (logger.isDebugEnabled()) {
logger.debug("bind ViewModel({}) Listener", viewModel.getName().get());
}
tabPane.getSelectionModel().getSelectedItem().getOnSelectionChanged().handle(null);
});
return oldName;
}
return null;
});
}
private void initializeBaseTab() {
baseInfoTab.setOnSelectionChanged(event -> {
if (logger.isDebugEnabled()) {
logger.debug("baseInfoTab OnSelectionChanged");
}
if (baseInfoTab.isSelected()) {
onBaseTabShown();
}
});
saveBtn.disableProperty().bind(viewModel.getChanged().not());
saveBtn.setOnAction(event -> {
try {
CompanyContact contact = companyContactLoadedFuture.join();
viewModel.copyTo(contact);
CompanyContact saved = companyContactRepository.save(contact);
viewModel.update(saved);
companyContactLoadedFuture = CompletableFuture.completedFuture(saved);
} catch (Exception e) {
UITools.showExceptionAndWait("保存失败,请检查", e);
}
});
nameField.textProperty().bindBidirectional(viewModel.getName());
positionField.textProperty().bindBidirectional(viewModel.getPosition());
phoneField.textProperty().bindBidirectional(viewModel.getPhone());
emailField.textProperty().bindBidirectional(viewModel.getEmail());
addressField.textProperty().bindBidirectional(viewModel.getAddress());
LocalDateStringConverter converter = new LocalDateStringConverter(DateTimeFormatter.ISO_LOCAL_DATE, null);
createdField.setConverter(converter);
createdField.valueProperty().bindBidirectional(viewModel.getCreated());
u8CodeField.textProperty().bind(viewModel.getU8Code());
descriptionField.textProperty().bindBidirectional(viewModel.getMemo());
versionLabel.textProperty().bind(viewModel.getVersion().asString());
}
/**
* 基础页显示时回调函数
*/
private void onBaseTabShown() {
if (logger.isDebugEnabled()) {
logger.debug("onBaseTabShown");
}
companyContactLoadedFuture.thenAcceptAsync(contact -> {
if (logger.isDebugEnabled()) {
logger.debug("onBaseTabShown company contact {}", contact.getName());
}
// viewModel.update(contact);
}).exceptionally(ex -> {
UITools.showExceptionAndWait(ex.getMessage(), ex);
return null;
});
}
public void onHidden(WindowEvent windowEvent) {
if (viewModel != null) {
try {
viewModel.unBindListener();
if (logger.isDebugEnabled()) {
logger.debug("unbind ViewModel({}) Listener", viewModel.getName().get());
}
} catch (InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
super.onHidden(windowEvent);
}
}

View File

@@ -0,0 +1,83 @@
package com.ecep.contract.controller.company;
import java.util.List;
import java.util.Optional;
import com.ecep.contract.ds.company.controller.CompanyWindowController;
import com.ecep.contract.ds.company.service.CompanyOldNameService;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.model.Company;
import com.ecep.contract.ui.AbstEntityManagerSkin;
import com.ecep.contract.vm.CompanyViewModel;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.scene.control.Alert;
import javafx.scene.control.TextInputDialog;
import lombok.Setter;
public class CompanyManagerSkin
extends AbstEntityManagerSkin<Company, CompanyViewModel, CompanyManagerSkin, CompanyManagerWindowController> {
@Setter
private CompanyOldNameService companyOldNameService;
public CompanyManagerSkin(CompanyManagerWindowController controller) {
super(controller);
}
public CompanyService getCompanyService() {
return controller.getViewModelService();
}
public CompanyOldNameService getCompanyOldNameService() {
if (companyOldNameService == null) {
companyOldNameService = getBean(CompanyOldNameService.class);
}
return companyOldNameService;
}
@Override
public void initializeTable() {
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
controller.nameColumn.setCellValueFactory(param -> param.getValue().getName());
controller.uniscidColumn.setCellValueFactory(param -> param.getValue().getUid());
controller.entStatusColumn.setCellValueFactory(param -> param.getValue().getEntStatus());
controller.createdColumn.setCellValueFactory(param -> param.getValue().getCreated());
controller.memoColumn.setCellValueFactory(param -> param.getValue().getMemo());
Platform.runLater(() -> {
getTableView().getSortOrder().add(controller.createdColumn);
});
}
@Override
protected void onTableRowDoubleClickedAction(CompanyViewModel item) {
showInOwner(CompanyWindowController.class, item);
}
@Override
protected void onTableCreateNewAction(ActionEvent event) {
Platform.runLater(() -> {
TextInputDialog dialog = new TextInputDialog();
dialog.setTitle("新建公司");
dialog.setHeaderText("请输入新建的公司名称,请输入公司名字全称");
Optional<String> optional = dialog.showAndWait();
if (optional.isPresent()) {
CompanyService companyService = getCompanyService();
String newCompanyName = optional.get();
List<Company> list = companyService.findAllByName(newCompanyName);
if (list == null || list.isEmpty()) {
// 未登记过
Company company = companyService.createNewCompany(newCompanyName);
Company saved = companyService.save(company);
CompanyWindowController.show(saved, getTableView().getScene().getWindow());
} else {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("新建公司");
alert.setHeaderText("公司名称 " + newCompanyName + " 已经存在,是否打开公司页面");
}
}
});
}
}

View File

@@ -0,0 +1,84 @@
package com.ecep.contract.controller.company;
import java.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.ds.company.controller.CompanyManagerSkin;
import com.ecep.contract.ds.company.controller.CompanyVerifyWindowController;
import com.ecep.contract.ds.company.tasker.CompanyFilesRebuildTasker;
import com.ecep.contract.model.Company;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.ui.AbstManagerWindowController;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CompanyViewModel;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/company/company-manager.fxml")
public class CompanyManagerWindowController
extends AbstManagerWindowController<Company, CompanyViewModel, CompanyManagerSkin> {
// columns
@FXML
public TableColumn<CompanyViewModel, Number> idColumn;
@FXML
public TableColumn<CompanyViewModel, String> nameColumn;
@FXML
public TableColumn<CompanyViewModel, String> uniscidColumn;
@FXML
public TableColumn<CompanyViewModel, String> entStatusColumn;
@FXML
public TableColumn<CompanyViewModel, LocalDate> createdColumn;
@FXML
public TableColumn<CompanyViewModel, String> memoColumn;
@Autowired
private CompanyService companyService;
@Override
public CompanyService getViewModelService() {
return companyService;
}
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().set("公司管理");
}
@Override
protected CompanyManagerSkin createDefaultSkin() {
return new CompanyManagerSkin(this);
}
public void onCompanyCreateNewAction(ActionEvent event) {
getSkin().onTableCreateNewAction(event);
}
/**
* 对所有公司的文件进行重置
* <p>
* 弹出对话框模式,对话框中有进度条和重置的消息
*
* @param event 事件
*/
public void onReBuildFilesAction(ActionEvent event) {
CompanyFilesRebuildTasker task = new CompanyFilesRebuildTasker();
UITools.showTaskDialogAndWait("公司文件重置", task, null);
}
public void onVerifyAction(ActionEvent event) {
show(CompanyVerifyWindowController.class, null);
}
}

View File

@@ -0,0 +1,16 @@
package com.ecep.contract.controller.company;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.controller.BaseController;
import com.ecep.contract.util.FxmlPath;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/company/company-verify.fxml")
public class CompanyVerifyWindowController extends BaseController {
}

View File

@@ -0,0 +1,313 @@
package com.ecep.contract.controller.company;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import org.hibernate.Hibernate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.ecep.contract.DesktopUtils;
import com.ecep.contract.SpringApp;
import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.customer.CompanyCustomerWindowController;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.CompanyTabSkinBankAccount;
import com.ecep.contract.controller.tab.CompanyTabSkinBase;
import com.ecep.contract.controller.tab.CompanyTabSkinBlackReason;
import com.ecep.contract.controller.tab.CompanyTabSkinContact;
import com.ecep.contract.controller.tab.CompanyTabSkinContract;
import com.ecep.contract.controller.tab.CompanyTabSkinFile;
import com.ecep.contract.controller.tab.CompanyTabSkinInvoice;
import com.ecep.contract.controller.tab.CompanyTabSkinOldName;
import com.ecep.contract.controller.tab.CompanyTabSkinOther;
import com.ecep.contract.controller.tab.CompanyTabSkinPurchaseBillVoucher;
import com.ecep.contract.controller.vendor.CompanyVendorWindowController;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CompanyVendor;
import com.ecep.contract.service.CompanyCustomerService;
import com.ecep.contract.service.CompanyFileService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.service.CompanyVendorService;
import com.ecep.contract.service.ContractService;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CompanyViewModel;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.stage.Window;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/company/company.fxml")
public class CompanyWindowController
extends AbstEntityController<Company, CompanyViewModel> {
private static final Logger logger = LoggerFactory.getLogger(CompanyWindowController.class);
public static void show(Company company, Window window) {
CompanyViewModel viewModel = new CompanyViewModel();
if (!Hibernate.isInitialized(company)) {
company = SpringApp.getBean(CompanyService.class).findById(company.getId());
}
viewModel.update(company);
show(viewModel, window);
}
/**
* 显示界面
*/
public static void show(CompanyViewModel viewModel, Window window) {
show(CompanyWindowController.class, viewModel, window);
}
@Autowired
private CompanyService companyService;
@Autowired
private CompanyFileService companyFileService;
@Autowired
private ContractService contractService;
@Autowired
private CompanyCustomerService companyCustomerService;
@Autowired
private CompanyVendorService companyVendorService;
public BorderPane root;
public TabPane tabPane;
public Tab baseInfoTab;
public Tab oldNameTab;
public Tab contactTab;
public Tab blackReasonTab;
public Tab customerTab;
public Tab vendorTab;
public Tab bankAccountTab;
public Tab contractTab;
public Tab fileTab;
public Tab invoiceTab;
public Tab purchaseBillVoucherTab;
public Tab otherTab;
/*
*/
public TextField nameField;
public TextField shortNameField;
public TextField uidField;
public TextField entStatusField;
public DatePicker setupDateField;
public TextField entTypeField;
public TextField industryField;
public TextField districtField;
public CheckBox pathExistField;
public TextField pathField;
public DatePicker createdDateField;
public TextField telephoneField;
public TextField regAddressField;
public TextField addressField;
public TextField registeredCapitalField;
public TextField registeredCapitalCurrencyField;
public TextField legalRepresentativeField;
public DatePicker operationPeriodBeginField;
public DatePicker operationPeriodEndField;
public TextArea memoField;
public Label versionLabel;
public Button companyRenameBtn;
public Button companyPathCreateBtn;
public Button companyPathChangeBtn;
public Button companyPathSameAsNameBtn;
// private final CompanyCustomerViewModel companyCustomerViewModel = new
// CompanyCustomerViewModel();
// private final CompanyVendorViewModel companyVendorViewModel = new
// CompanyVendorViewModel();
private final SimpleObjectProperty<CompanyCustomer> companyCustomerProperty = new SimpleObjectProperty<>();
private final SimpleObjectProperty<CompanyVendor> companyVendorProperty = new SimpleObjectProperty<>();
public Pane customerTab_pane1;
public Button customerTab_openBtn;
public Pane customerTab_pane2;
public Button customerTab_createBtn;
public Pane vendorTab_pane1;
public Button vendorTab_openBtn;
public Pane vendorTab_pane2;
public Button vendorTab_createBtn;
@Override
public void show(Stage stage) {
super.show(stage);
stage.minWidthProperty().set(600);
stage.minHeightProperty().set(450);
getTitle().set("[" + viewModel.getId().get() + "] " + viewModel.getName().getValue() + " 公司详情");
}
@Override
protected Company loadEntity() {
return companyService.findById(viewModel.getId().get());
}
@Override
protected Company saveEntity(Company entity) {
return companyService.save(entity);
}
@Override
protected void registerTabSkins() {
registerTabSkin(baseInfoTab, tab1 -> new CompanyTabSkinBase(this));
registerTabSkin(oldNameTab, tab1 -> new CompanyTabSkinOldName(this));
registerTabSkin(contactTab, tab1 -> new CompanyTabSkinContact(this));
registerTabSkin(blackReasonTab, tab1 -> new CompanyTabSkinBlackReason(this));
registerTabSkin(bankAccountTab, tab1 -> new CompanyTabSkinBankAccount(this));
registerTabSkin(contractTab, tab -> new CompanyTabSkinContract(this));
registerTabSkin(fileTab, tab -> new CompanyTabSkinFile(this));
registerTabSkin(invoiceTab, tab -> new CompanyTabSkinInvoice(this));
registerTabSkin(purchaseBillVoucherTab, tab -> new CompanyTabSkinPurchaseBillVoucher(this));
registerTabSkin(otherTab, tab -> new CompanyTabSkinOther(this));
initializeVendorTab();
initializeCustomerTab();
}
@Override
protected <K extends AbstEntityBasedTabSkin<?, ?, ?>> K registerTabSkin(Tab tab, Function<Tab, K> func) {
K skin = super.registerTabSkin(tab, func);
if (skin instanceof AbstCompanyBasedTabSkin b) {
b.setViewModel(viewModel);
}
return skin;
}
@Override
public CompanyService getViewModelService() {
return companyService;
}
private void initializeCustomerTab() {
customerTab.setOnSelectionChanged(event -> {
if (logger.isDebugEnabled()) {
logger.debug("customerTab OnSelectionChanged");
}
if (customerTab.isSelected()) {
onCustomerTabShown();
}
});
customerTab_pane1.visibleProperty().bind(customerTab_pane2.visibleProperty().not());
customerTab_pane2.visibleProperty().bind(companyCustomerProperty.isNull());
customerTab_createBtn.setOnAction(event -> {
// TODO 创建
});
customerTab_openBtn.setOnAction(event -> {
CompanyCustomerWindowController.show(companyCustomerProperty.get(), root.getScene().getWindow());
});
}
private void onCustomerTabShown() {
if (logger.isDebugEnabled()) {
logger.debug("onCustomerTabShown");
}
getLoadedFuture().thenAcceptAsync(company -> {
companyCustomerProperty.set(companyCustomerService.findByCompany(company));
}).exceptionally(ex -> {
UITools.showExceptionAndWait(ex.getMessage(), ex);
return null;
});
}
private void initializeVendorTab() {
vendorTab.setOnSelectionChanged(event -> {
if (logger.isDebugEnabled()) {
logger.debug("vendorTab OnSelectionChanged");
}
if (vendorTab.isSelected()) {
onVendorTabShown();
}
});
vendorTab_pane1.visibleProperty().bind(vendorTab_pane2.visibleProperty().not());
vendorTab_pane2.visibleProperty().bind(companyVendorProperty.isNull());
vendorTab_createBtn.setOnAction(event -> {
// TODO 创建
});
vendorTab_openBtn.setOnAction(event -> {
CompanyVendorWindowController.show(companyVendorProperty.get(), root.getScene().getWindow());
});
}
private void onVendorTabShown() {
if (logger.isDebugEnabled()) {
logger.debug("onVendorTabShown");
}
getLoadedFuture().thenAcceptAsync(company -> {
if (logger.isDebugEnabled()) {
logger.debug("onVendorTabShown company {}", company.getName());
}
companyVendorProperty.set(companyVendorService.findByCompany(company));
}).exceptionally(ex -> {
UITools.showExceptionAndWait(ex.getMessage(), ex);
return null;
});
}
/**
* 合并更新企业信息
*/
public void onCompanyCompositeUpdateAction(ActionEvent event) {
CompanyCompositeUpdateTasker task = new CompanyCompositeUpdateTasker();
task.setCompany(getEntity());
UITools.showTaskDialogAndWait("更新企业信息", task, null);
}
/**
* 企业合规检查
*/
public void onCompanyVerifyAction(ActionEvent event) {
Company company = getEntity();
CompanyVerifyTasker task = new CompanyVerifyTasker();
task.setCompanyService(companyService);
task.setCompany(company);
UITools.showTaskDialogAndWait("企业合规性验证", task, null);
}
public void onCompanyOpenInExplorerAction(ActionEvent event) {
Company company = getEntity();
String path = company.getPath();
CompletableFuture.runAsync(() -> {
if (!StringUtils.hasText(path)) {
ButtonType buttonType = UITools.showConfirmation("目录未设置", "是否创建目录").join();
if (buttonType == ButtonType.OK) {
if (companyService.makePathAbsent(company)) {
save(company);
}
} else {
setStatus("未设置目录");
return;
}
}
DesktopUtils.checkAndShowInExplorer(company.getPath(), this::setStatus);
});
}
}

View File

@@ -0,0 +1,66 @@
package com.ecep.contract.controller.company_old_name;
import java.time.format.DateTimeFormatter;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.model.CompanyOldName;
import com.ecep.contract.service.CompanyOldNameService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.vm.CompanyOldNameViewModel;
import javafx.scene.control.Tab;
import javafx.util.converter.LocalDateStringConverter;
import lombok.Setter;
public class CompanyOldNameTabSkinBase
extends AbstEntityBasedTabSkin<CompanyOldNameWindowController, CompanyOldName, CompanyOldNameViewModel>
implements TabSkin {
@Setter
private CompanyService companyService;
@Setter
private CompanyOldNameService companyOldNameService;
public CompanyOldNameTabSkinBase(CompanyOldNameWindowController controller) {
super(controller);
}
@Override
public Tab getTab() {
return controller.baseInfoTab;
}
@Override
public void initializeTab() {
controller.nameField.textProperty().bind(viewModel.getName());
controller.ambiguityField.selectedProperty().bindBidirectional(viewModel.getAmbiguity());
LocalDateStringConverter converter = new LocalDateStringConverter(DateTimeFormatter.ISO_LOCAL_DATE, null);
controller.startDateField.setConverter(converter);
controller.startDateField.valueProperty().bindBidirectional(viewModel.getBeginDate());
controller.endDateField.setConverter(converter);
controller.endDateField.valueProperty().bindBidirectional(viewModel.getEndDate());
controller.pathField.textProperty().bind(viewModel.getPath());
controller.descriptionField.textProperty().bindBidirectional(viewModel.getMemo());
controller.versionLabel.textProperty().bind(viewModel.getVersion().asString());
controller.saveBtn.disableProperty().bind(viewModel.getChanged().not());
controller.saveBtn.setOnAction(this::onSaveAction);
}
public CompanyService getCompanyService() {
if (companyService == null) {
companyService = getBean(CompanyService.class);
}
return companyService;
}
public CompanyOldNameService getCompanyOldNameService() {
if (companyOldNameService == null) {
companyOldNameService = getBean(CompanyOldNameService.class);
}
return companyOldNameService;
}
}

View File

@@ -0,0 +1,377 @@
package com.ecep.contract.controller.company_old_name;
import java.io.File;
import java.util.List;
import java.util.function.Consumer;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.StringUtils;
import com.ecep.contract.CompanyFileType;
import com.ecep.contract.DesktopUtils;
import com.ecep.contract.MyProperties;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.ds.company.service.CompanyFileService;
import com.ecep.contract.model.CompanyFile;
import com.ecep.contract.model.CompanyOldName;
import com.ecep.contract.service.CompanyOldNameService;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CompanyFileViewModel;
import com.ecep.contract.vm.CompanyOldNameViewModel;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.scene.control.Tab;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableView;
import lombok.Setter;
/**
*
*/
public class CompanyOldNameTabSkinFile
extends
AbstEntityTableTabSkin<CompanyOldNameWindowController, CompanyOldName, CompanyOldNameViewModel, CompanyFile, CompanyFileViewModel>
implements TabSkin {
@Setter
private CompanyOldNameService companyOldNameService;
@Setter
private CompanyFileService companyFileService;
@Setter
private MyProperties myProperties;
public CompanyOldNameTabSkinFile(CompanyOldNameWindowController controller) {
super(controller);
setDragAndDrop(true);
setDragAndDropFileHandler(this::moveFileToCompany);
}
@Override
public Tab getTab() {
return controller.fileTab;
}
@Override
public TableView<CompanyFileViewModel> getTableView() {
return controller.fileTable;
}
@Override
protected CompanyFileService getViewModelService() {
return companyFileService;
}
@Override
public Specification<CompanyFile> getSpecification(CompanyOldName parent) {
return SpecificationUtils.and(getSpecification(), (root, query, builder) -> {
return builder.equal(root.get("company").get("id"), parent.getCompanyId());
});
}
@Override
public void initializeTab() {
// controller.fileTable_file_move_btn.setOnAction(this::onTableMoveFileAction);
// controller.fileTable_file_retrieve_from_download_dir_btn.setOnAction(this::onTableRetrieveFromDownloadDirAction);
// controller.fileTable_file_reset_btn.setOnAction(this::onTableResetAction);
//
//
// controller.fileTable_idColumn.setCellValueFactory(param ->
// param.getValue().getId());
// ObservableMap<CompanyFileType, CompanyFileTypeLocal> observableMapByLocal =
// getBean(CompanyFileTypeLocalRepository.class).getObservableMapByLocal();
// controller.fileTable_typeColumn.setCellValueFactory(param ->
// Bindings.valueAt(observableMapByLocal,
// param.getValue().getType()).map(CompanyFileTypeLocal::getValue));
// controller.fileTable_filePathColumn.setCellValueFactory(param ->
// param.getValue().getFilePath());
// controller.fileTable_filePathColumn.setCellFactory(param -> new
// FileTableFilePathTableCell());
// controller.fileTable_applyDateColumn.setCellValueFactory(param ->
// param.getValue().getApplyDate());
// controller.fileTable_expiringDateColumn.setCellValueFactory(param ->
// param.getValue().getExpiringDate());
//
//
// controller.fileTable_menu_refresh.setOnAction(this::onTableRefreshAction);
// controller.fileTable_menu_del.setOnAction(this::onTableDeleteAction);
// controller.fileTable_menu_copy_as_matched_by_contract.setOnAction(this::onTableCopyAsMatchedByContractAction);
// controller.fileTable_menu_copy_as_matched_by_contract.setOnMenuValidation(this::onTableCopyAsMatchedMenuValidation);
super.initializeTab();
}
private void onTableResetAction(ActionEvent event) {
CompanyOldName oldName = getParent();
// CompletableFuture.runAsync(() -> {
// if (getCompanyFileService().reBuildingFiles(oldName, this::setStatus)) {
// loadTableDataSet();
// }
// });
}
/**
* 从 下载目录 中查找相关的资质文件
*/
private void onTableRetrieveFromDownloadDirAction(ActionEvent event) {
// CompanyOldName oldName = getParent();
// MyProperties myProperties = getMyProperties();
// File dir = myProperties.getDownloadDirectory();
// if (!dir.exists()) {
// setStatus("下载目录 " + dir.getAbsolutePath() + " 不存在,请检查");
// return;
// }
//
// setStatus("开始检索 下载 文件夹:" + dir.getAbsolutePath() + "...");
// File[] files = dir.listFiles(File::isFile);
// if (files == null) {
// setStatus("检索 下载 文件夹失败");
// return;
// }
// if (files.length == 0) {
// setStatus("下载 文件夹没有文件");
// return;
// }
// setStatus("下载 文件夹中共有文件 " + files.length + " 个文件");
//
// if (getCompanyOldNameService().retrieveFromDownloadFiles(oldName, files,
// this::setStatus)) {
// // fixed if update
// viewModel.update(oldName);
// loadTableDataSet();
// }
}
/**
* 把文件从 老系统中移到 \\10.84.209.8\项目信息\相关方信息 目录中
*/
private void onTableMoveFileAction(ActionEvent event) {
// CompanyFileService companyFileService = getCompanyFileService();
// CompanyOldName oldName = getParent();
// List<CompanyFile> list = companyFileService.findByCompany(oldName);
// if (list.isEmpty()) {
// return;
// }
// if (getCompanyService().makePathAbsent(oldName)) {
// save(oldName);
// }
//
// String path = oldName.getPath();
// if (!StringUtils.hasText(path)) {
// setStatus("异常, 企业目录未设置");
// return;
// }
// File companyPath = new File(path);
// for (CompanyFile companyFile : list) {
// String filePath = companyFile.getFilePath();
// if (StringUtils.hasText(filePath)) {
// File file = new File(filePath);
// if (file.exists()) {
// if (file.getParentFile().equals(companyPath)) {
// continue;
// }
// File dest = new File(companyPath, file.getName());
// if (file.renameTo(dest)) {
// companyFile.setFilePath(dest.getAbsolutePath());
// companyFileService.save(companyFile);
// setStatus(file.getName() + " 移动到 " + companyPath.getName());
// }
// }
// }
// }
}
/**
*
*/
private void onTableCopyAsMatchedByContractAction(ActionEvent event) {
UITools.showDialogAndWait("复制资信评估报告", "按当前评估报告复制一个合同中最匹配的", list -> {
onTableCopyAsMatchedAction_(msg -> {
Platform.runLater(() -> {
list.add(msg);
});
});
});
}
private void onTableCopyAsMatchedAction_(Consumer<String> state) {
// CompanyOldName oldName = getParent();
//
// CompanyFileViewModel selectedItem =
// table.getSelectionModel().getSelectedItem();
// if (selectedItem == null) {
// state.accept("未选择行");
// return;
// }
// if (selectedItem.getApplyDate().get() == null) {
// state.accept("有效日期不能未空");
// return;
// }
//
// LocalDate nextCreditReportDate = null;
// try {
// nextCreditReportDate = companyFileService.getNextCreditReportDate(oldName,
// state);
// if (nextCreditReportDate == null) {
// state.accept("没有找到下一个咨询评估日期");
// return;
// }
// } catch (Exception e) {
// state.accept("获取下一个咨询评估日期失败:" + e.getMessage());
// return;
// }
//
// state.accept("下一个咨询评估日期:" + nextCreditReportDate);
//
// if (!nextCreditReportDate.isBefore(selectedItem.getApplyDate().get())) {
// state.accept("咨询评估日期晚于下一个咨询评估日期");
// return;
// }
//
//
// File src = new File(selectedItem.getFilePath().get());
// if (!src.exists()) {
// state.accept("当前选择行的文件不存在");
// return;
// }
//
// String srcDate = MyDateTimeUtils.format(selectedItem.getApplyDate().get());
// String destDate = MyDateTimeUtils.format(nextCreditReportDate);
// String srcFileName = src.getName();
// String destFileName;
//
// // 天眼查的报告
// if (CloudTycService.isTycReport(srcFileName)) {
// state.accept("天眼查的报告按标准格式命名");
// String name = oldName.getName() + "_" + CloudTycService.NAME;
// if (srcFileName.contains(CloudTycService.TYC_ENTERPRISE_BASIC_REPORT)) {
// name = name + "_" + CloudTycService.TYC_ENTERPRISE_BASIC_REPORT;
// } else if (srcFileName.contains(CloudTycService.TYC_ENTERPRISE_MAJOR_REPORT))
// {
// name = name + "_" + CloudTycService.TYC_ENTERPRISE_MAJOR_REPORT;
// } else if
// (srcFileName.contains(CloudTycService.TYC_ENTERPRISE_ANALYSIS_REPORT)) {
// name = name + "_" + CloudTycService.TYC_ENTERPRISE_ANALYSIS_REPORT;
// }
// destFileName = name + "_" + destDate + "_cp." +
// StringUtils.getFilenameExtension(srcFileName);
// } else {
// if (srcFileName.contains(srcDate)) {
// // 如果文件名中包含日期,则替换为新日期
// destFileName = srcFileName.replace(srcDate, destDate + "_cp");
// } else {
// // 如果文件名中不包含日期,则添加日期
// destFileName = oldName.getName() + "_" + destDate + "_cp." +
// StringUtils.getFilenameExtension(srcFileName);
// }
// }
//
// state.accept("新文件名:" + destFileName);
//
// File dest = new File(src.getParent(), destFileName);
// try {
// FileSystemUtils.copyRecursively(src, dest);
// state.accept("新文件复制成功");
// } catch (IOException e) {
// state.accept("新文件复制失败:" + e.getMessage());
// }
//
// CompanyFile companyFile = new CompanyFile();
// companyFile.setFilePath(dest.getAbsolutePath());
// companyFile.setApplyDate(nextCreditReportDate);
// companyFile.setExpiringDate(nextCreditReportDate.plusYears(1));
// companyFile.setType(CompanyFileType.CreditReport);
// companyFile.setCompany(oldName);
// companyFileService.save(companyFile);
//
// state.accept("新文件已记录");
//
// loadTableDataSet();
// state.accept("文件表已刷新");
}
/**
* 当fileTable选中的行是咨询评估时可用
*
* @param event event
*/
public void onTableCopyAsMatchedMenuValidation(Event event) {
// 当fileTable选中的行是咨询评估时可用
CompanyFileViewModel selectedItem = getSelectedItem();
if (selectedItem == null) {
event.consume();
return;
}
CompanyFileType type = selectedItem.getType().get();
if (type != CompanyFileType.CreditReport) {
event.consume();
return;
}
}
private void moveFileToCompany(List<File> files) {
String path = viewModel.getPath().get();
if (!StringUtils.hasText(path)) {
setStatus("未设置目录");
return;
}
File dir = new File(path);
if (!dir.exists()) {
setStatus("目录错误,不存在");
return;
}
}
@Override
protected void onTableRowDoubleClickedAction(CompanyFileViewModel item) {
String path = item.getFilePath().get();
if (StringUtils.hasText(path)) {
File file = new File(path);
if (!file.exists()) {
setStatus("文件不存在 " + file.getName());
return;
}
DesktopUtils.showInExplorer(file);
}
}
class FileTableFilePathTableCell extends TableCell<CompanyFileViewModel, String> {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText("");
return;
}
String path = viewModel.getPath().get();
if (StringUtils.hasText(path)) {
if (item.startsWith(path)) {
item = "~" + item.substring(path.length());
}
}
setText(item);
}
}
public MyProperties getMyProperties() {
if (myProperties == null) {
myProperties = getBean(MyProperties.class);
}
return myProperties;
}
private CompanyFileService getCompanyFileService() {
if (companyFileService == null) {
companyFileService = getBean(CompanyFileService.class);
}
return companyFileService;
}
public CompanyOldNameService getCompanyOldNameService() {
if (companyOldNameService == null) {
companyOldNameService = getBean(CompanyOldNameService.class);
}
return companyOldNameService;
}
}

View File

@@ -0,0 +1,129 @@
package com.ecep.contract.controller.company_old_name;
import java.io.File;
import java.util.concurrent.CompletableFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.ecep.contract.DesktopUtils;
import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.model.CompanyOldName;
import com.ecep.contract.service.CompanyOldNameService;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CompanyFileViewModel;
import com.ecep.contract.vm.CompanyOldNameViewModel;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/company/company_old_name.fxml")
public class CompanyOldNameWindowController extends AbstEntityController<CompanyOldName, CompanyOldNameViewModel> {
private static final Logger logger = LoggerFactory.getLogger(CompanyOldNameWindowController.class);
/**
* 显示界面
*/
public static void show(CompanyOldNameViewModel viewModel, Window window) {
show(CompanyOldNameWindowController.class, viewModel, window);
}
public Tab baseInfoTab;
public BorderPane root;
public TabPane tabPane;
public Tab fileTab;
public TextField nameField;
public CheckBox ambiguityField;
public DatePicker startDateField;
public DatePicker endDateField;
public TextField pathField;
public TextArea descriptionField;
public Label versionLabel;
public Button saveBtn;
public Button saveBtn2;
public TableView<CompanyFileViewModel> fileTable;
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
getTitle().set("[" + viewModel.getId().get() + "] " + viewModel.getName().getValue() + " 曾用名详情");
}
@Override
protected CompanyOldName loadEntity() {
return getViewModelService().findById(viewModel.getId().get());
}
@Override
protected CompanyOldName saveEntity(CompanyOldName entity) {
return getViewModelService().save(entity);
}
@Override
protected void registerTabSkins() {
registerTabSkin(baseInfoTab, this::createBaseTabSkin);
registerTabSkin(fileTab, this::createFileTabSkin);
}
@Override
public CompanyOldNameService getViewModelService() {
return getCachedBean(CompanyOldNameService.class);
}
private CompanyOldNameTabSkinBase createBaseTabSkin(Tab tab) {
CompanyOldNameTabSkinBase skin = new CompanyOldNameTabSkinBase(this);
skin.setCompanyOldNameService(companyOldNameService);
return skin;
}
private CompanyOldNameTabSkinFile createFileTabSkin(Tab tab) {
CompanyOldNameTabSkinFile skin = new CompanyOldNameTabSkinFile(this);
skin.setCompanyOldNameService(companyOldNameService);
// skin.setCompanyFileService(companyFileService);
return skin;
}
public void onOldCompanyOpenInExplorerAction(ActionEvent event) {
CompanyOldName companyOldName = getEntity();
String path = companyOldName.getPath();
CompletableFuture.runAsync(() -> {
if (!StringUtils.hasText(path)) {
ButtonType buttonType = UITools.showConfirmation("目录未设置", "是否创建目录").join();
if (buttonType == ButtonType.OK) {
if (companyOldNameService.makePathAbsent(companyOldName)) {
save(companyOldName);
}
} else {
setStatus("未设置目录");
return;
}
}
File file = new File(companyOldName.getPath());
DesktopUtils.showInExplorer(file);
});
}
}

View File

@@ -0,0 +1,21 @@
package com.ecep.contract.controller.contract;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.model.Contract;
import com.ecep.contract.service.ContractService;
import com.ecep.contract.vm.ContractViewModel;
public abstract class AbstContractBasedTabSkin
extends AbstEntityBasedTabSkin<ContractWindowController, Contract, ContractViewModel>
implements TabSkin {
public AbstContractBasedTabSkin(ContractWindowController controller) {
super(controller);
viewModel = controller.getViewModel();
}
public ContractService getContractService() {
return controller.contractService;
}
}

View File

@@ -0,0 +1,34 @@
package com.ecep.contract.controller.contract;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.controller.table.TableOfTabSkin;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.ds.contract.vo.ContractViewModel;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vm.IdentityViewModel;
public abstract class AbstContractTableTabSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>>
extends AbstEntityTableTabSkin<ContractWindowController, Contract, ContractViewModel, T, TV>
implements TabSkin, TableOfTabSkin<Contract, T, TV> {
public AbstContractTableTabSkin(ContractWindowController controller) {
super(controller);
}
public ContractService getContractService() {
return controller.contractService;
}
@Override
public Specification<T> getSpecification(Contract parent) {
return SpecificationUtils.and(getSpecification(), (root, query, builder) -> {
return builder.equal(root.get("contract"), parent);
});
}
}

View File

@@ -0,0 +1,98 @@
package com.ecep.contract.controller.contract;
import java.time.LocalDate;
import java.time.LocalDateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.UITools;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.tab.ContractManagerSkin;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.ds.contract.tasker.ContractFilesRebuildAllTasker;
import com.ecep.contract.ds.contract.tasker.ContractRepairAllTasker;
import com.ecep.contract.ds.contract.vo.ContractViewModel;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.ContractGroup;
import com.ecep.contract.model.ContractKind;
import com.ecep.contract.model.ContractType;
import com.ecep.contract.model.Employee;
import com.ecep.contract.ui.AbstManagerWindowController;
import javafx.event.ActionEvent;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/contract/contract-manager.fxml")
public class ContractManagerWindowController
extends AbstManagerWindowController<Contract, ContractViewModel, ContractManagerSkin> {
public ComboBox<ContractGroup> groupSelector;
public CheckBox composeViewBtn;
// columns
public TableColumn<ContractViewModel, Number> idColumn;
public TableColumn<ContractViewModel, String> nameColumn;
public TableColumn<ContractViewModel, String> codeColumn;
public TableColumn<ContractViewModel, ContractGroup> groupColumn;
public TableColumn<ContractViewModel, ContractType> typeColumn;
public TableColumn<ContractViewModel, ContractKind> kindColumn;
public TableColumn<ContractViewModel, String> parentCodeColumn;
public TableColumn<ContractViewModel, LocalDate> setupDateColumn;
public TableColumn<ContractViewModel, LocalDate> orderDateColumn;
public TableColumn<ContractViewModel, LocalDate> startDateColumn;
public TableColumn<ContractViewModel, Employee> employeeColumn;
public TableColumn<ContractViewModel, LocalDateTime> createdColumn;
public TableColumn<ContractViewModel, Number> amountColumn;
public TableColumn<ContractViewModel, Company> companyColumn;
@Autowired
private ContractService contractService;
@Override
public ContractService getViewModelService() {
return contractService;
}
@Override
protected ContractManagerSkin createDefaultSkin() {
ContractManagerSkin skin = new ContractManagerSkin(this);
return skin;
}
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().set("合同管理");
}
public void onVerifyAction(ActionEvent event) {
show(ContractVerifyWindowController.class, null);
}
public void onCreateNewContractAction(ActionEvent event) {
// getSkin().onTableCreateNewAction(event);
}
public void onContractFilesRebuildAction(ActionEvent event) {
ContractFilesRebuildAllTasker task = new ContractFilesRebuildAllTasker();
task.setContractService(contractService);
task.setGroup(groupSelector.getValue());
UITools.showTaskDialogAndWait("合同文件重置", task, null);
}
public void onContractRepairAllAction(ActionEvent event) {
ContractRepairAllTasker task = new ContractRepairAllTasker();
UITools.showTaskDialogAndWait("合同同步修复", task, null);
}
}

View File

@@ -0,0 +1,165 @@
package com.ecep.contract.controller.contract;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.hibernate.Hibernate;
import com.ecep.contract.SpringApp;
import com.ecep.contract.UITools;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.ds.contract.service.ExtendVendorInfoService;
import com.ecep.contract.ds.contract.vo.ExtendVendorInfoViewModel;
import com.ecep.contract.ds.vendor.service.VendorGroupService;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.ExtendVendorInfo;
import com.ecep.contract.model.VendorGroup;
import com.ecep.contract.ui.ComboBoxUtils;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TextField;
import javafx.util.converter.NumberStringConverter;
import lombok.Setter;
@FxmlPath("/ui/contract/contract-tab-ext-vendor-info.fxml")
public class ContractTabSkinExtendVendorInfo
extends AbstContractBasedTabSkin {
@Setter
private ExtendVendorInfoService extendVendorInfoService;
@Setter
private VendorGroupService vendorGroupService;
@FXML
public ComboBox<VendorGroup> vendorGroupField;
@FXML
public Label vendorGroupLabel;
@FXML
public TextField sequenceNumberField;
@FXML
public CheckBox assignedProviderField;
@FXML
public CheckBox prePurchaseField;
CompletableFuture<ExtendVendorInfo> loadedFuture;
private ExtendVendorInfoViewModel viewModel = new ExtendVendorInfoViewModel();
public ContractTabSkinExtendVendorInfo(ContractWindowController controller) {
super(controller);
}
@Override
public Tab getTab() {
return controller.extendVendorInfo;
}
@Override
public void initializeUIComponents() {
super.initializeUIComponents();
listenTabSelectionChanged();
}
@Override
public BooleanProperty changeProperty() {
return viewModel.getChanged();
}
@Override
public void onTabShown() {
if (loadedFuture == null) {
loadedFuture = CompletableFuture.supplyAsync(() -> {
initializeTab();
Contract contract = getEntity();
return loadExtendVendorInfo(contract);
});
}
}
void updateViewModel(ExtendVendorInfo info) {
if (Platform.isFxApplicationThread()) {
viewModel.update(info);
} else {
Platform.runLater(() -> viewModel.update(info));
}
}
private ExtendVendorInfo loadExtendVendorInfo(Contract contract) {
ExtendVendorInfoService service = getExtendVendorInfoService();
try {
ExtendVendorInfo info = service.findByContract(contract);
if (info == null) {
info = service.newInstanceByContract(contract);
info = service.save(info);
}
updateViewModel(info);
viewModel.bindListener();
return info;
} catch (Exception e) {
UITools.showExceptionAndWait("加载成本时发生错误", e);
throw e;
}
}
@Override
public void initializeTab() {
List<VendorGroup> groups = getVendorGroupService().findAll();
ComboBoxUtils.initialComboBox(vendorGroupField, groups, true);
vendorGroupField.valueProperty().bindBidirectional(viewModel.getGroup());
vendorGroupLabel.textProperty().bind(vendorGroupField.valueProperty().map(v -> {
if (v == null) {
return "-";
}
if (!Hibernate.isInitialized(v)) {
v = getVendorGroupService().findById(v.getId());
viewModel.getGroup().set(v);
}
return v.getDescription();
}));
sequenceNumberField.textProperty().bindBidirectional(viewModel.getCodeSequenceNumber(),
new NumberStringConverter());
assignedProviderField.selectedProperty().bindBidirectional(viewModel.getAssignedProvider());
assignedProviderField.disableProperty().bind(Bindings.createBooleanBinding(() -> {
VendorGroup group = viewModel.getGroup().get();
if (group == null) {
return false;
}
return !group.isPriceComparison();
}, viewModel.getGroup()));
prePurchaseField.selectedProperty().bindBidirectional(viewModel.getPrePurchase());
}
@Override
public void save() {
if (loadedFuture != null) {
ExtendVendorInfo vendorInfo = loadedFuture.join();
if (viewModel.copyTo(vendorInfo)) {
ExtendVendorInfo saved = getExtendVendorInfoService().save(vendorInfo);
updateViewModel(saved);
loadedFuture = CompletableFuture.completedFuture(saved);
}
}
}
public ExtendVendorInfoService getExtendVendorInfoService() {
if (extendVendorInfoService == null) {
extendVendorInfoService = SpringApp.getBean(ExtendVendorInfoService.class);
}
return extendVendorInfoService;
}
public VendorGroupService getVendorGroupService() {
if (vendorGroupService == null) {
vendorGroupService = SpringApp.getBean(VendorGroupService.class);
}
return vendorGroupService;
}
}

View File

@@ -0,0 +1,399 @@
package com.ecep.contract.controller.contract;
import static com.ecep.contract.util.TableViewUtils.bindDoubleClicked;
import static com.ecep.contract.util.TableViewUtils.bindEnterPressed;
import java.io.File;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.ecep.contract.ContractPayWay;
import com.ecep.contract.Message;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.UITools;
import com.ecep.contract.controller.BaseController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.ds.contract.tasker.ContractVerifyComm;
import com.ecep.contract.ds.contract.tasker.ContractVerifyResultExportAsExcelFile;
import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.ds.project.service.SaleTypeService;
import com.ecep.contract.ds.vendor.service.VendorGroupService;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.Employee;
import com.ecep.contract.ui.table.cell.EmployeeTableCell;
import javafx.application.Platform;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.HBox;
import javafx.stage.FileChooser;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/contract/contract-verify.fxml")
public class ContractVerifyWindowController extends BaseController {
private static final Logger logger = LoggerFactory.getLogger(ContractVerifyWindowController.class);
@Override
public Stage show(FXMLLoader loader, Window owner, Modality modality) {
return super.show(loader, owner, modality);
}
@Setter
@Getter
public static class MessageExt extends Message {
private String prefix = null;
public MessageExt(Level level, String message) {
super(level, message);
}
}
@Data
public static class Model implements MessageHolder {
private SimpleStringProperty code = new SimpleStringProperty();
private SimpleStringProperty name = new SimpleStringProperty();
private SimpleObjectProperty<Employee> employee = new SimpleObjectProperty<>();
private SimpleObjectProperty<LocalDate> setupDate = new SimpleObjectProperty<>();
private SimpleListProperty<MessageExt> messages = new SimpleListProperty<>(FXCollections.observableArrayList());
@Override
public void addMessage(Level level, String message) {
MessageExt msg = new MessageExt(level, message);
messages.add(msg);
}
@Override
public MessageHolder sub(String prefix) {
return (level, message) -> {
MessageExt msg = new MessageExt(level, message);
msg.setPrefix(prefix);
msg.setMessage(message);
messages.add(msg);
};
}
}
static class StateTableCell extends TableCell<Model, ObservableList<MessageExt>> {
Label message2Label(MessageExt message) {
Label label = new Label(message.getMessage());
label.getStyleClass().add("msg-text");
label.getStyleClass().add(message.getLevel().getName().toLowerCase());
return label;
}
@Override
protected void updateItem(ObservableList<MessageExt> item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
return;
}
if (item.isEmpty()) {
setText("");
setGraphic(null);
return;
}
try {
HBox box = new HBox();
box.getStyleClass().add("message-column-ctx");
// 使用绑定方式监听列表变化,避免重复创建节点
List<Label> labels = item.stream().map(this::message2Label).toList();
Platform.runLater(() -> box.getChildren().setAll(labels));
// 监听消息列表变化,动态更新 UI
item.addListener((ListChangeListener<? super MessageExt>) change -> {
List<MessageExt> messages = new ArrayList<>(change.getList());
List<Label> list = messages.stream().map(this::message2Label).toList();
Platform.runLater(() -> box.getChildren().setAll(list));
});
setGraphic(box);
} catch (Exception e) {
if (logger.isWarnEnabled()) {
logger.warn("{} @ {}", e.getMessage(), getTableRow().getItem().getCode(), e);
}
}
}
}
ContractVerifyComm comm = new ContractVerifyComm();
@Autowired
private ProjectSaleTypeService saleTypeService;
@Autowired
private VendorGroupService vendorGroupService;
@Autowired
private ContractService contractService;
@Autowired
private EmployeeService employeeService;
@FXML
public DatePicker setupDateBeginSelector;
@FXML
public DatePicker setupDateEndSelector;
// 企业验证开关
@FXML
public CheckMenuItem verifyCompanyStatusChecker;
@FXML
public CheckMenuItem verifyCompanyPathChecker;
@FXML
public CheckMenuItem verifyCompanyCreditChecker;
// 供应商验证开关
@FXML
public CheckMenuItem verifyVendorChecker;
@FXML
public CheckMenuItem verifyVendorFileChecker;
// 客户验证开关
@FXML
public CheckMenuItem verifyCustomerChecker;
@FXML
public CheckMenuItem verifyCustomerFileChecker;
@FXML
public CheckMenuItem verifyCustomerSubContractDateChecker;
@FXML
public CheckMenuItem onlyShowVerifiedChecker;
@FXML
public TableView<Model> viewTable;
private final ObservableList<Model> viewTableDataSet = FXCollections.observableArrayList();
@FXML
public TableColumn<Model, String> viewTable_codeColumn;
@FXML
public TableColumn<Model, String> viewTable_nameColumn;
@FXML
public TableColumn<Model, Employee> viewTable_employeeColumn;
@FXML
public TableColumn<Model, LocalDate> viewTable_setupDateColumn;
@FXML
public TableColumn<Model, ObservableList<MessageExt>> viewTable_stateColumn;
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().set("合同合规验证");
}
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
comm.setContractService(contractService);
viewTable.getScene().getStylesheets().add("/ui/contract/contract-verify.css");
LocalDate now = LocalDate.now();
setupDateBeginSelector.setValue(LocalDate.of(2024, 1, 1));
setupDateEndSelector.setValue(now);
viewTable_codeColumn.setCellValueFactory(param -> param.getValue().getCode());
viewTable_nameColumn.setCellValueFactory(param -> param.getValue().getName());
viewTable_employeeColumn.setCellValueFactory(param -> param.getValue().getEmployee());
viewTable_employeeColumn.setCellFactory(param -> new EmployeeTableCell<>(getEmployeeService()));
viewTable_setupDateColumn.setCellValueFactory(param -> param.getValue().getSetupDate());
// viewTable_stateColumn.setCellValueFactory(param ->
// param.getValue().getMessages().map(messages -> {
// return
// messages.stream().map(Message::getMessage).collect(Collectors.joining(", "));
// }));
viewTable_stateColumn.setCellValueFactory(param -> param.getValue().getMessages());
viewTable_stateColumn.setCellFactory(param -> new StateTableCell());
viewTable.setItems(viewTableDataSet);
comm.getVerifyCompanyPath().bind(verifyCompanyPathChecker.selectedProperty());
comm.getVerifyCompanyStatus().bind(verifyCompanyStatusChecker.selectedProperty());
comm.getVerifyCompanyCredit().bind(verifyCompanyCreditChecker.selectedProperty());
comm.getVerifyVendor().bind(verifyVendorChecker.selectedProperty());
comm.getVerifyVendorFiles().bind(verifyVendorFileChecker.selectedProperty());
comm.getVerifyCustomer().bind(verifyCustomerChecker.selectedProperty());
comm.getVerifyCustomerFiles().bind(verifyCustomerFileChecker.selectedProperty());
comm.getVerifyCustomerSubContractDate().bind(verifyCustomerSubContractDateChecker.selectedProperty());
bindDoubleClicked(viewTable, this::showContract);
bindEnterPressed(viewTable, this::reVerifyContract);
}
/**
* 合同验证
*/
public void onVerifyAction(ActionEvent event) {
Button btn = (Button) event.getSource();
CompletableFuture<Void> future = runAsync(() -> {
viewTableDataSet.clear();
Pageable pageRequest = PageRequest.ofSize(200);
AtomicInteger counter = new AtomicInteger(0);
Specification<Contract> spec = (root, query, builder) -> {
return builder.and(
builder.or(
builder.equal(root.get("payWay"), ContractPayWay.RECEIVE),
builder.and(
builder.equal(root.get("payWay"), ContractPayWay.PAY),
builder.or(
builder.isNull(root.get("parentCode")),
builder.equal(root.get("parentCode"), "")))),
builder.between(root.get("setupDate"), setupDateBeginSelector.getValue(),
setupDateEndSelector.getValue()));
};
long total = contractService.count(spec);
setStatus("合同:" + total + "");
while (true) {
if (isCloseRequested()) {
break;
}
Page<Contract> page = contractService.findAll(spec, pageRequest);
for (Contract contract : page) {
if (isCloseRequested()) {
break;
}
counter.incrementAndGet();
Model model = new Model();
viewTableDataSet.add(model);
Employee handler = contract.getHandler();
if (handler == null) {
model.getEmployee().set(contract.getEmployee());
} else {
model.getEmployee().set(handler);
}
model.getCode().set(contract.getCode());
model.getName().set(contract.getName());
model.getSetupDate().set(contract.getSetupDate());
comm.verify(contract, model);
setStatus("合同验证进度:" + counter.get() + " / " + total);
// 移除中间消息
if (!model.getMessages().isEmpty()) {
model.getMessages().removeIf(msg -> msg.getLevel().intValue() <= Level.INFO.intValue());
}
if (model.getMessages().isEmpty()) {
if (onlyShowVerifiedChecker.isSelected()) {
viewTableDataSet.remove(model);
}
}
}
if (!page.hasNext()) {
break;
}
pageRequest = page.nextPageable();
}
setStatus("合同验证完成");
}).exceptionally(e -> {
setStatus(e.getMessage());
logger.error(e.getMessage(), e);
return null;
});
}
public void onContractReVerifyAction(ActionEvent event) {
Model selectedItem = viewTable.getSelectionModel().getSelectedItem();
if (selectedItem == null) {
return;
}
reVerifyContract(selectedItem);
}
private void reVerifyContract(Model model) {
String contractCode = model.getCode().get();
if (!StringUtils.hasText(contractCode)) {
return;
}
runAsync(() -> {
Contract contract = contractService.findByCode(contractCode);
if (contract == null) {
return;
}
model.getMessages().clear();
try {
comm.verify(contract, model);
// 移除中间消息
if (!model.getMessages().isEmpty()) {
model.getMessages().removeIf(msg -> msg.getLevel().intValue() <= Level.INFO.intValue());
}
} catch (Exception e) {
logger.error(model.getCode().get(), e);
model.error(e.getMessage());
}
if (model.getMessages().isEmpty()) {
Platform.runLater(() -> {
viewTableDataSet.remove(model);
});
}
});
}
public void onShowContractDetailAction(ActionEvent event) {
Model selectedItem = viewTable.getSelectionModel().getSelectedItem();
if (selectedItem == null) {
return;
}
showContract(selectedItem);
}
private void showContract(Model model) {
String contractCode = model.getCode().get();
if (!StringUtils.hasText(contractCode)) {
return;
}
Contract contract = contractService.findByCode(contractCode);
if (contract == null) {
return;
}
ContractWindowController.show(contract, viewTable.getScene().getWindow());
}
public void onExportVerifyResultAsFileAction(ActionEvent e) {
FileChooser chooser = new FileChooser();
chooser.setTitle("导出核验结果");
chooser.setInitialFileName("核验结果.xlsx");
File selected = chooser.showSaveDialog(viewTable.getScene().getWindow());
if (selected != null) {
ContractVerifyResultExportAsExcelFileTasker task = new ContractVerifyResultExportAsExcelFileTasker();
task.setDestFile(selected);
task.setModels(new ArrayList<>(viewTableDataSet));
UITools.showTaskDialogAndWait("导出中...", task, null);
}
}
}

View File

@@ -0,0 +1,258 @@
package com.ecep.contract.controller.contract;
import java.io.File;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.ecep.contract.DesktopUtils;
import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.company.CompanyWindowController;
import com.ecep.contract.controller.tab.ContractTabSkinBase;
import com.ecep.contract.controller.tab.ContractTabSkinFiles;
import com.ecep.contract.controller.tab.ContractTabSkinItems;
import com.ecep.contract.controller.tab.ContractTabSkinItemsV2;
import com.ecep.contract.controller.tab.ContractTabSkinPayPlan;
import com.ecep.contract.controller.tab.ContractTabSkinPurchaseOrders;
import com.ecep.contract.controller.tab.ContractTabSkinSaleOrders;
import com.ecep.contract.controller.tab.ContractTabSkinSubContract;
import com.ecep.contract.controller.tab.ContractTabSkinVendorBid;
import com.ecep.contract.ds.contract.tasker.ContractVerifyTasker;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.Contract;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.service.ContractService;
import com.ecep.contract.service.ProjectService;
import com.ecep.contract.task.ContractRepairTask;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.ContractViewModel;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/contract/contract.fxml")
public class ContractWindowController
extends AbstEntityController<Contract, ContractViewModel> {
public static void show(Contract contract, Window owner) {
ContractViewModel model = new ContractViewModel();
model.update(contract);
show(model, owner);
}
/**
* 显示界面
*/
public static void show(ContractViewModel viewModel, Window window) {
show(ContractWindowController.class, viewModel, window);
}
public BorderPane root;
public Tab baseInfoTab;
public Tab extendVendorInfo;
public Tab contractTab;
public Tab bidVendorTab;
public Tab payPlanTab;
public Tab itemTab;
public Tab fileTab;
public Button verifyBtn;
public Button openRelativeCompanyCustomerBtn;
public Button openRelativeCompanyVendorBtn;
@Autowired
CompanyService companyService;
@Autowired
ContractService contractService;
@Autowired
ProjectService projectService;
public TextField nameField;
public TextField guidField;
public TextField codeField;
public TextField parentCodeField;
public TextField companyField;
public TextField stateField;
public TextField groupField;
public TextField typeField;
public TextField kindField;
public TextField pathField;
public DatePicker orderDateField;
public DatePicker startDateField;
public DatePicker endDateField;
public TextField setupPersonField;
public DatePicker setupDateField;
public TextField inurePersonField;
public DatePicker inureDateField;
public TextField varyPersonField;
public DatePicker varyDateField;
public TextField projectField;
public TextField employeeField;
public TextField handlerField;
public TextField createdField;
public TextArea descriptionField;
public TextField amountField;
public TextField totalQuantityField;
public TextField totalAmountField;
public TextField totalUnTaxAmountField;
public TextField execQuantityField;
public TextField execAmountField;
public TextField execUnTaxAmountField;
public TextField payWayField;
public Button linkContractProjectBtn;
public Button contractRenameBtn;
public Button contractPathCreateBtn;
public Button contractPathChangeBtn;
public Button contractPathAsNameBtn;
public Button contractPathAsCodeBtn;
/**
* 打开主合同
*/
public Button openMainContractBtn;
/**
* 计算主合同编号按钮
*/
public Button calcMainContractNoBtn;
public Label versionLabel;
@Override
public ContractService getViewModelService() {
return contractService;
}
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
getTitle().bind(viewModel.getName()
.map(name -> "[" + viewModel.getId().get() + "] " + viewModel.getCode().get() + " " + name + " 合同详情"));
root.getScene().getStylesheets().add("/ui/contract/contract.css");
}
@Override
protected void registerTabSkins() {
TabPane tabPane = baseInfoTab.getTabPane();
ObservableList<Tab> tabs = tabPane.getTabs();
registerTabSkin(baseInfoTab, tab -> new ContractTabSkinBase(this));
switch (viewModel.getPayWay().get()) {
case RECEIVE -> {
tabs.remove(extendVendorInfo);
registerTabSkin(contractTab, t -> new ContractTabSkinSubContract(this));
tabs.remove(bidVendorTab);
Tab saleOrderTab = new Tab("销售订单");
tabs.add(saleOrderTab);
registerTabSkin(saleOrderTab, tab -> new ContractTabSkinSaleOrders(this, tab));
tabs.add(new Tab("票据"));
break;
}
case PAY -> {
registerTabSkin(extendVendorInfo, t -> new ContractTabSkinExtendVendorInfo(this));
tabs.remove(contractTab);
registerTabSkin(bidVendorTab, t -> new ContractTabSkinVendorBid(this));
Tab purchaseOrderTab = new Tab("采购订单");
tabs.add(purchaseOrderTab);
registerTabSkin(purchaseOrderTab, tab -> new ContractTabSkinPurchaseOrders(this, tab));
tabs.add(new Tab("发货单"));
tabs.add(new Tab("签收单"));
tabs.add(new Tab("付款单"));
break;
}
}
registerTabSkin(itemTab, tab -> new ContractTabSkinItemsV2(this));
registerTabSkin(payPlanTab, tab -> new ContractTabSkinPayPlan(this));
registerTabSkin(fileTab, tab -> new ContractTabSkinFiles(this));
}
public void onContractOpenInExplorerAction(ActionEvent event) {
Contract contract = getEntity();
String path = contract.getPath();
if (!StringUtils.hasText(path)) {
setStatus("未设置目录");
return;
}
File file = new File(path);
if (!file.exists()) {
setStatus("目录错误,不存在");
return;
}
DesktopUtils.showInExplorer(file);
}
public void onContractOpenRelativeCompanyAction(ActionEvent event) {
Contract contract = getEntity();
if (contract.getCompany() == null) {
UITools.showAlertAndWait("没有关联的公司,你可以尝试同步修复异常。");
return;
}
Integer companyId = contract.getCompany().getId();
Company company = companyService.findById(companyId);
if (company != null) {
CompanyWindowController.show(company, root.getScene().getWindow());
}
}
public void onSyncContractAction(ActionEvent event) {
ContractRepairTask task = new ContractRepairTask();
task.setContractService(contractService);
task.setContract(getEntity());
UITools.showTaskDialogAndWait("同步合同", task, null);
if (task.isRepaired()) {
setEntity(task.getContract());
}
if (task.isFilesUpdated()) {
ContractTabSkinFiles tabSkin = getTabSkin(ContractTabSkinFiles.class);
if (tabSkin != null) {
tabSkin.loadTableDataSet();
}
}
if (task.isPlayPlanUpdated()) {
ContractTabSkinPayPlan tabSkin = getTabSkin(ContractTabSkinPayPlan.class);
if (tabSkin != null) {
tabSkin.loadTableDataSet();
}
}
if (task.isItemsUpdated()) {
ContractTabSkinItems tabSkin = getTabSkin(ContractTabSkinItems.class);
if (tabSkin != null) {
tabSkin.loadTableDataSet();
}
}
if (task.isSaleOrderUpdated()) {
ContractTabSkinSaleOrders tabSkin = getTabSkin(ContractTabSkinSaleOrders.class);
if (tabSkin != null) {
tabSkin.loadTableDataSet();
}
}
}
/**
* 验证合同合规性
*/
public void onContractVerifyAction(ActionEvent event) {
Contract contract = getEntity();
ContractVerifyTasker task = new ContractVerifyTasker();
task.setContractService(contractService);
task.setContract(contract);
UITools.showTaskDialogAndWait("同步合规性验证", task, null);
}
}

View File

@@ -0,0 +1,29 @@
package com.ecep.contract.controller.customer;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.service.CompanyCustomerService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.vm.CompanyCustomerViewModel;
import com.ecep.contract.vm.IdentityViewModel;
public abstract class AbstCompanyCustomerTableTabSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>>
extends
AbstEntityTableTabSkin<CompanyCustomerWindowController, CompanyCustomer, CompanyCustomerViewModel, T, TV>
implements TabSkin {
public AbstCompanyCustomerTableTabSkin(CompanyCustomerWindowController controller) {
super(controller);
}
public CompanyService getCompanyService() {
return controller.getCompanyService();
}
protected CompanyCustomerService getCompanyCustomerService() {
return getCachedBean(CompanyCustomerService.class);
}
}

View File

@@ -0,0 +1,449 @@
package com.ecep.contract.controller.customer;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.ecep.contract.controller.BaseController;
import com.ecep.contract.util.FxmlUtils;
import com.ecep.contract.ds.company.CompanyFileUtils;
import com.ecep.contract.ds.customer.repository.CompanyCustomerEvaluationFormFileRepository;
import com.ecep.contract.ds.customer.repository.CompanyCustomerFileRepository;
import com.ecep.contract.ds.customer.service.CompanyCustomerFileService;
import com.ecep.contract.model.CompanyCustomerEvaluationFormFile;
import com.ecep.contract.model.CompanyCustomerFile;
import com.ecep.contract.vm.CompanyCustomerFileViewModel;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.ActionEvent;
import javafx.geometry.Bounds;
import javafx.scene.control.CheckBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TextField;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
@Lazy
@Scope("prototype")
@Component
public class CompanyCustomerEvaluationFormFileWindowController extends BaseController {
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerEvaluationFormFileWindowController.class);
public static void show(CompanyCustomerFile saved, Window window) {
CompanyCustomerFileViewModel model = new CompanyCustomerFileViewModel();
model.update(saved);
show(model, window);
}
public static void show(CompanyCustomerFileViewModel viewModel, Window window) {
String key = viewModel.getClass().getName() + "-" + viewModel.getId().get();
if (toFront(key)) {
return;
}
FxmlUtils.newLoaderAsyncWithRunLater("/ui/company/customer/customer_evaluation_form.fxml", null, loader -> {
CompanyCustomerEvaluationFormFileWindowController controller = loader.getController();
controller.viewModel = viewModel;
controller.show(loader, window, Modality.NONE, key);
});
}
public Label idField;
public TextField filePathField;
public CheckBox validField;
public TextField editFilePathField;
public DatePicker signDateField;
public ToggleGroup catalog;
public ToggleGroup level;
public ToggleGroup score1;
public ToggleGroup score2;
public ToggleGroup score3;
public ToggleGroup score4;
public ToggleGroup score5;
public ToggleGroup creditLevel;
public ImageView imageView;
public SplitPane splitPane;
public ScrollPane leftPane;
public Label totalCreditScoreLabel;
private CompanyCustomerFileViewModel viewModel;
private final SimpleStringProperty catalogProperty = new SimpleStringProperty("");
private final SimpleStringProperty levelProperty = new SimpleStringProperty("");
private final SimpleIntegerProperty score1Property = new SimpleIntegerProperty(-1);
private final SimpleIntegerProperty score2Property = new SimpleIntegerProperty(-1);
private final SimpleIntegerProperty score3Property = new SimpleIntegerProperty(-1);
private final SimpleIntegerProperty score4Property = new SimpleIntegerProperty(-1);
private final SimpleIntegerProperty score5Property = new SimpleIntegerProperty(-1);
private final SimpleIntegerProperty creditLevelProperty = new SimpleIntegerProperty(-1);
private final SimpleIntegerProperty totalCreditScoreProperty = new SimpleIntegerProperty(-1);
private SimpleObjectProperty<Image> imageProperty = new SimpleObjectProperty<>();
private CompletableFuture<CompanyCustomerEvaluationFormFile> loadedFuture;
@Autowired
private CompanyCustomerFileRepository companyCustomerFileRepository;
@Autowired
private CompanyCustomerEvaluationFormFileRepository companyCustomerEvaluationFormFileRepository;
@Lazy
@Autowired
private CompanyCustomerFileService companyCustomerFileService;
@Override
public void show(Stage stage) {
super.show(stage);
stage.setFullScreen(false);
// Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
//
// stage.setX(screenBounds.getMinX());
// stage.setY(screenBounds.getMinY());
// stage.setWidth(screenBounds.getWidth());
// stage.setHeight(screenBounds.getHeight());
//
// stage.isMaximized();
stage.setMaximized(true);
getTitle().set("客户评估表单");
}
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
if (logger.isDebugEnabled()) {
logger.debug("onShown");
}
initializePane();
loadedFuture = CompletableFuture.supplyAsync(() -> {
int id = viewModel.getId().get();
CompanyCustomerFile customerFile = companyCustomerFileService.findById(id);
CompanyCustomerEvaluationFormFile formFile = companyCustomerFileService.findCustomerEvaluationFormFileByCustomerFile(customerFile);
Platform.runLater(() -> update(formFile));
return formFile;
});
}
BiConsumer<ToggleGroup, String> stringRadioGroupUpdater = (group, newValue) -> {
if (newValue != null) {
for (Toggle toggle : group.getToggles()) {
String data = (String) toggle.getUserData();
if (data == null) {
if (logger.isWarnEnabled()) {
logger.warn("toggle unset userData: {}", toggle);
}
continue;
}
if (newValue.equals(data)) {
toggle.setSelected(true);
return;
}
}
}
Platform.runLater(() -> {
group.selectToggle(null);
// Toggle first = group.getToggles().getFirst();
// first.setSelected(true);
// first.setSelected(false);
// RadioButton btn = (RadioButton) first;
// btn.setText(newValue);
});
};
BiConsumer<SimpleStringProperty, Toggle> stringPropertyUpdater = (property, toggle) -> {
if (toggle == null) {
property.set("");
return;
}
String data = (String) toggle.getUserData();
property.set(data);
};
BiConsumer<ToggleGroup, Number> numberRadioGroupUpdater = (group, newValue) -> {
String value = String.valueOf(newValue);
for (Toggle toggle : group.getToggles()) {
String data = (String) toggle.getUserData();
if (value.equals(data)) {
toggle.setSelected(true);
return;
}
}
Platform.runLater(() -> {
group.selectToggle(null);
// Toggle first = group.getToggles().getFirst();
// first.setSelected(true);
// first.setSelected(false);
// RadioButton btn = (RadioButton) first;
// btn.setText(String.valueOf(newValue));
});
};
BiConsumer<SimpleIntegerProperty, Toggle> numberPropertyUpdater = (property, toggle) -> {
if (toggle == null) {
property.set(0);
return;
}
String data = (String) toggle.getUserData();
property.set(Integer.parseInt(data));
};
int totalScoreToLevel(int score) {
if (score >= 200) {
return 4;
} else if (score >= 150) {
return 3;
} else if (score >= 100) {
return 2;
} else if (score >= 60) {
return 1;
}
return 0;
}
boolean calcValid() {
if (creditLevelProperty.get() <= 0) {
return false;
}
if (!StringUtils.hasText(catalogProperty.get())) {
return false;
}
if (!StringUtils.hasText(levelProperty.get())) {
return false;
}
if (score1Property.get() <= 0) {
return false;
}
if (score2Property.get() <= 0) {
return false;
}
if (score3Property.get() <= 0) {
return false;
}
if (score4Property.get() <= 0) {
return false;
}
if (score5Property.get() <= 0) {
return false;
}
if (creditLevelProperty.get() <= 0) {
return false;
}
return true;
}
private void initializePane() {
idField.textProperty().bind(viewModel.getId().asString());
filePathField.textProperty().bind(viewModel.getFilePath());
editFilePathField.textProperty().bind(viewModel.getEditFilePath());
signDateField.valueProperty().bindBidirectional(viewModel.getSignDate());
validField.selectedProperty().bindBidirectional(viewModel.getValid());
initializeRadioGroup(catalog, catalogProperty);
initializeRadioGroup(level, levelProperty);
initializeRadioGroup(score1, score1Property);
initializeRadioGroup(score2, score2Property);
initializeRadioGroup(score3, score3Property);
initializeRadioGroup(score4, score4Property);
initializeRadioGroup(score5, score5Property);
creditLevelProperty.addListener((observable, oldValue, newValue) -> {
numberRadioGroupUpdater.accept(creditLevel, newValue);
});
SimpleIntegerProperty[] scores = new SimpleIntegerProperty[]{score1Property, score2Property, score3Property, score4Property, score5Property};
totalCreditScoreProperty.bind(Bindings.createIntegerBinding(() -> {
int total = 0;
for (SimpleIntegerProperty score : scores) {
total += score.get();
}
creditLevelProperty.set(totalScoreToLevel(total));
return total;
}, scores));
totalCreditScoreLabel.textProperty().bind(totalCreditScoreProperty.map(score -> {
return "合计总分:" + score;
}));
Bindings.createBooleanBinding(() -> {
boolean valid = calcValid();
viewModel.getValid().set(valid);
return valid;
}, catalogProperty, levelProperty, score1Property, score2Property, score3Property, score4Property, score5Property, creditLevelProperty).addListener(((observable, oldValue, newValue) -> {
logger.info("valid:{}", newValue);
}));
imageView.imageProperty().bind(viewModel.getFilePath().map(path -> {
if (CompanyFileUtils.withExtensions(path, CompanyFileUtils.PDF)) {
File pdfFile = new File(path);
try (PDDocument pdDocument = Loader.loadPDF(pdfFile)) {
PDFRenderer pdfRenderer = new PDFRenderer(pdDocument);
BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(0, 300);
// 获取 BufferedImage 的宽度和高度
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
WritableImage writableImage = new WritableImage(width, height);
PixelWriter pixelWriter = writableImage.getPixelWriter();
// 将 BufferedImage 的像素数据复制到 WritableImage
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int argb = bufferedImage.getRGB(x, y);
pixelWriter.setArgb(x, y, argb);
}
}
return writableImage;
} catch (Exception e) {
setStatus(e.getMessage());
}
return null;
} else {
File file = new File(path);
Image image = new Image(file.toURI().toString());
return image;
}
}));
leftPane.widthProperty().addListener((observable, oldValue, newValue) -> {
Platform.runLater(() -> {
imageView.setFitWidth(leftPane.getWidth());
imageView.setFitHeight(leftPane.getHeight());
});
});
imageView.setFitWidth(leftPane.getWidth());
imageView.setFitHeight(leftPane.getHeight());
imageView.setOnScroll(event -> {
System.out.println("event = " + event);
System.out.println("event.getDeltaY() = " + event.getDeltaY());
Bounds bounds = imageView.getBoundsInLocal();
// Bounds latestBounds = (Bounds) imageView.getProperties().get("latestBounds");
// if (latestBounds != null) {
// double latestBoundsWidth = latestBounds.getWidth();
// }
// if (bounds.getWidth() < leftPane.getWidth()) {
imageView.setFitWidth(bounds.getWidth() + event.getDeltaY());
// } else {
imageView.setFitHeight(bounds.getHeight() + event.getDeltaY());
// }
});
}
private void initializeRadioGroup(ToggleGroup toggleGroup, SimpleStringProperty property) {
property.addListener((observable, oldValue, newValue) -> {
stringRadioGroupUpdater.accept(toggleGroup, newValue);
});
toggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
stringPropertyUpdater.accept(property, newValue);
});
}
private void initializeRadioGroup(ToggleGroup toggleGroup, SimpleIntegerProperty property) {
property.addListener((observable, oldValue, newValue) -> {
numberRadioGroupUpdater.accept(toggleGroup, newValue);
});
toggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
numberPropertyUpdater.accept(property, newValue);
});
}
private void update(CompanyCustomerEvaluationFormFile formFile) {
viewModel.update(formFile.getCustomerFile());
// formFile.getScoreTemplateVersion();
catalogProperty.set(formFile.getCatalog());
levelProperty.set(formFile.getLevel());
score1Property.set(formFile.getScore1());
score2Property.set(formFile.getScore2());
score3Property.set(formFile.getScore3());
score4Property.set(formFile.getScore4());
score5Property.set(formFile.getScore5());
creditLevelProperty.set(formFile.getCreditLevel());
}
public void onSaveAction(ActionEvent event) {
boolean modified = false;
int id = viewModel.getId().get();
CompanyCustomerEvaluationFormFile formFile = companyCustomerFileService.findCustomerEvaluationFormFileById(id);
CompanyCustomerFile customerFile = formFile.getCustomerFile();
if (!Objects.equals(catalogProperty.get(), formFile.getCatalog())) {
formFile.setCatalog(catalogProperty.get());
modified = true;
}
if (!Objects.equals(levelProperty.get(), formFile.getLevel())) {
formFile.setLevel(levelProperty.get());
modified = true;
}
if (!Objects.equals(score1Property.get(), formFile.getScore1())) {
formFile.setScore1(score1Property.get());
modified = true;
}
if (!Objects.equals(score2Property.get(), formFile.getScore2())) {
formFile.setScore2(score2Property.get());
modified = true;
}
if (!Objects.equals(score3Property.get(), formFile.getScore3())) {
formFile.setScore3(score3Property.get());
modified = true;
}
if (!Objects.equals(score4Property.get(), formFile.getScore4())) {
formFile.setScore4(score4Property.get());
modified = true;
}
if (!Objects.equals(score5Property.get(), formFile.getScore5())) {
formFile.setScore5(score5Property.get());
modified = true;
}
if (!Objects.equals(creditLevelProperty.get(), formFile.getCreditLevel())) {
formFile.setCreditLevel(creditLevelProperty.get());
modified = true;
}
if (viewModel.copyTo(customerFile)) {
modified = true;
}
if (modified) {
companyCustomerFileService.save(formFile);
}
}
}

View File

@@ -0,0 +1,250 @@
package com.ecep.contract.controller.customer;
import static com.ecep.contract.util.ExcelUtils.setCellValue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.LocalDate;
import java.util.Comparator;
import java.util.List;
import com.ecep.contract.service.*;
import com.ecep.contract.util.CompanyUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellRangeAddress;
import org.hibernate.Hibernate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import com.ecep.contract.CompanyCustomerFileType;
import com.ecep.contract.SpringApp;
import com.ecep.contract.model.CloudTyc;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CompanyCustomerEvaluationFormFile;
import com.ecep.contract.model.CompanyCustomerFile;
import javafx.concurrent.Task;
import lombok.Setter;
public class CompanyCustomerEvaluationFormUpdateTask extends Task<Object> {
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerEvaluationFormUpdateTask.class);
@Setter
private CompanyCustomer customer;
@Setter
private CompanyService companyService;
private CompanyContactService companyContactService;
private CompanyCustomerService companyCustomerService;
@Setter
private CompanyCustomerFileService companyCustomerFileService;
private CompanyService getCompanyService() {
if (companyService == null) {
companyService = SpringApp.getBean(CompanyService.class);
}
return companyService;
}
private CompanyCustomerService getCompanyCustomerService() {
if (companyCustomerService == null) {
companyCustomerService = SpringApp.getBean(CompanyCustomerService.class);
}
return companyCustomerService;
}
private CompanyContactService getCompanyContactService() {
if (companyContactService == null) {
companyContactService = SpringApp.getBean(CompanyContactService.class);
}
return companyContactService;
}
private CompanyCustomerFileService getCompanyCustomerFileService() {
if (companyCustomerFileService == null) {
companyCustomerFileService = SpringApp.getBean(CompanyCustomerFileService.class);
}
return companyCustomerFileService;
}
@Override
protected Object call() throws Exception {
try {
updateEvaluationForm();
} catch (Exception ex) {
updateMessage(ex.getMessage());
}
return null;
}
private File getEvaluationFormTemplate() {
return getCompanyCustomerFileService().getEvaluationFormTemplate();
}
public void updateEvaluationForm() {
if (!StringUtils.hasText(customer.getPath())) {
updateMessage("供应商目录未设置,请先设置供应商目录");
return;
}
File template = getEvaluationFormTemplate();
if (template == null) {
updateMessage("评价表模板文件未设置,请先设置评价表模板文件");
return;
}
if (!template.exists()) {
updateMessage("评价表模板文件 " + template.getAbsolutePath() + " 不存在,请检查");
return;
}
File dir = new File(customer.getPath());
String template_file_name = template.getName();
File destFile = new File(dir, template_file_name);
if (destFile.exists()) {
updateMessage("表单文件已经存在," + destFile.getName());
try (
InputStream inp = new FileInputStream(destFile);
Workbook wb = WorkbookFactory.create(inp)) {
updateEvaluationForm(wb, destFile);
updateMessage("评价表已更新");
} catch (Exception e) {
updateMessage(e.getMessage());
logger.error(e.getMessage(), e);
}
} else {
updateMessage("根据模板 " + template_file_name + " 创建表单 " + destFile.getName());
try (
InputStream inp = new FileInputStream(template);
Workbook wb = WorkbookFactory.create(inp)) {
updateEvaluationForm(wb, destFile);
updateMessage("评价表已创建");
CompanyCustomerFile customerFile = new CompanyCustomerFile();
customerFile.setCustomer(customer);
customerFile.setFilePath(destFile.getAbsolutePath());
customerFile.setType(CompanyCustomerFileType.General);
save(customerFile);
} catch (Exception e) {
updateMessage(e.getMessage());
logger.error(e.getMessage(), e);
}
}
updateProgress(1, 1);
}
private void save(CompanyCustomerFile customerFile) {
getCompanyCustomerFileService().save(customerFile);
}
/**
* 更新客户评估表,依据模板创建,如果已经存在生成的文件,则更新评估表
*
* @param wb work book
* @param destFile 目标文件
*/
public void updateEvaluationForm(
Workbook wb, File destFile) throws IOException {
Company company = customer.getCompany();
if (!Hibernate.isInitialized(company)) {
company = getCompanyService().findById(company.getId());
customer.setCompany(company);
}
Sheet sheet = wb.getSheetAt(0);
updateSheet(company, sheet);
// 输出到文件
try (OutputStream fileOut = new FileOutputStream(destFile)) {
wb.write(fileOut);
} catch (FileNotFoundException e) {
updateMessage("写评估表时发生文件错误,请检查评估表是否被打开中");
updateMessage(e.getMessage());
logger.error(e.getMessage(), e);
}
}
private void updateSheet(Company company, Sheet sheet) {
setCellValue(sheet, "B3", "客户编号:" + CompanyUtils.formatCompanyVendorId(customer.getId()));
setCellValue(sheet, "B4", "客户名称:" + company.getName());
LocalDate suggestDate = getCompanyCustomerFileService().getNextSignDate(customer, (level, msg) -> {
updateMessage(" - " + msg);
});
if (suggestDate == null) {
suggestDate = LocalDate.now();
}
setCellValue(sheet, "H3", "评定时间:" + suggestDate);
setCellValue(sheet, "H4", "统一社会信用代码:");
setCellValue(sheet, "H5", company.getUniscid());
// 注册所属地
setCellValue(sheet, "B5", "注册所属地:" + company.getDistrict());
// 经营状态
setCellValue(sheet, "D6", "经营状态:" + company.getEntStatus());
// 成立日期
setCellValue(sheet, "H6", "成立日期:" + company.getSetupDate());
// 所属行业
setCellValue(sheet, "D7", "所属行业:" + company.getIndustry());
setCellValue(sheet, "D8",
"注册资金:" + company.getRegisteredCapital() + " " + company.getRegisteredCapitalCurrency());
// 企业类型
setCellValue(sheet, "H10", "企业类型:" + company.getEntType());
// 天眼评分
CloudTycService cloudTycService = SpringApp.getBean(CloudTycService.class);
CloudTyc cloudTyc = cloudTycService.getOrCreateCloudTyc(company);
setCellValue(sheet, "D10", "天眼评分:" + (cloudTyc.getScore() > 0 ? cloudTyc.getScore() : ""));
// 检索评估表
List<CompanyCustomerEvaluationFormFile> evaluationFormFiles = getCompanyCustomerFileService()
.findAllCustomerEvaluationFormFiles(customer);
List<CompanyCustomerEvaluationFormFile> filteredList = evaluationFormFiles.stream()
.filter(v -> {
CompanyCustomerFile file = v.getCustomerFile();
return file.getSignDate() != null && file.isValid();
})
.sorted(Comparator.comparing(v -> v.getCustomerFile().getSignDate()))
.toList();
if (filteredList.isEmpty()) {
setCellValue(sheet, "C40", "首次评价");
try {
sheet.addMergedRegion(CellRangeAddress.valueOf("C40:K40"));
} catch (Exception ignored) {
}
} else {
setCellValue(sheet, "C40", "评价日期");
try {
sheet.addMergedRegion(CellRangeAddress.valueOf("C40:D40"));
} catch (Exception ignored) {
}
setCellValue(sheet, "E40", "经济指标");
setCellValue(sheet, "F40", "综合指标");
setCellValue(sheet, "G40", "资信等级");
String[] CreditLevelTitles = new String[]{"-", "差★", " 一般★★", " 较好★★★", " 好★★★★", " "};
int baseRow = 40;
for (CompanyCustomerEvaluationFormFile form : filteredList) {
CompanyCustomerFile customerFile = form.getCustomerFile();
setCellValue(sheet, baseRow, 2, String.valueOf(customerFile.getSignDate()));
setCellValue(sheet, baseRow, 4, form.getCatalog());
setCellValue(sheet, baseRow, 5, form.getLevel());
if (form.getCreditLevel() == null) {
setCellValue(sheet, baseRow, 6, "-");
} else {
setCellValue(sheet, baseRow, 6, CreditLevelTitles[form.getCreditLevel()]);
}
try {
sheet.addMergedRegion(new CellRangeAddress(baseRow, baseRow, 2, 3));
} catch (Exception ignored) {
}
baseRow++;
}
}
}
}

View File

@@ -0,0 +1,202 @@
package com.ecep.contract.controller.customer;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.model.*;
import com.ecep.contract.service.CompanyCustomerEntityService;
import com.ecep.contract.service.CompanyCustomerFileService;
import com.ecep.contract.service.CompanyCustomerService;
import com.ecep.contract.task.Tasker;
import com.ecep.contract.util.UITools;
import lombok.Setter;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFTable;
import org.hibernate.Hibernate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Pageable;
import org.springframework.util.StringUtils;
import java.io.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import static com.ecep.contract.util.ExcelUtils.*;
public class CompanyCustomerExportExcelTasker extends Tasker<Object> {
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerExportExcelTasker.class);
@Setter
File destFile;
CompanyCustomerService customerService;
CompanyCustomerEntityService customerEntityService;
CompanyCustomerFileService customerFileService;
CompanyCustomerService getCustomerService() {
if (customerService == null)
customerService = getBean(CompanyCustomerService.class);
return customerService;
}
CompanyCustomerFileService getCustomerFileService() {
if (customerFileService == null)
customerFileService = getBean(CompanyCustomerFileService.class);
return customerFileService;
}
CompanyCustomerEntityService getCustomerEntityService() {
if (customerEntityService == null)
customerEntityService = getBean(CompanyCustomerEntityService.class);
return customerEntityService;
}
@Override
protected Object execute(MessageHolder holder) throws Exception {
if (destFile.exists()) {
holder.warn("文件已存在, 输出将覆盖文件...");
}
String path = getConfService().getString("customer.evaluation.book.template");
if (!StringUtils.hasText(path)) {
holder.error("模板文件未设置,模板文件");
return null;
}
File template = new File(path);
if (!template.exists()) {
holder.warn("模板文件 " + template.getAbsolutePath() + " 不存在,请检查");
return null;
}
try (
InputStream inp = new FileInputStream(template);
Workbook wb = WorkbookFactory.create(inp);
) {
String sheetName = "客户资信台账";
Sheet sheet = null;
for (int i = 0; i < wb.getNumberOfSheets(); i++) {
Sheet s = wb.getSheetAt(i);
if (s.getSheetName().contains(sheetName)) {
sheet = s;
break;
}
}
if (sheet == null) {
holder.error("找不到" + sheetName + "Sheet");
return null;
}
setCellValue(sheet, "H2", "年度:" + LocalDate.now().getYear());
setCellValue(sheet, "A19", "Build by CMS @ " + MyDateTimeUtils.format(LocalDateTime.now()));
int rowIndex = 0;
for (CompanyCustomer customer : getCustomerService().findAll(null, Pageable.unpaged())) {
Company company = customer.getCompany();
if (company == null) {
holder.warn("客户 #" + customer.getId() + " 不存在对应公司");
continue;
}
if (!Hibernate.isInitialized(company)) {
company = getCompanyService().findById(company.getId());
}
LocalDate devDate = null;
List<CompanyCustomerEntity> entities = getCustomerEntityService() .findAllByCustomer(customer);
for (CompanyCustomerEntity entity : entities) {
if (devDate == null || devDate.isAfter(entity.getDevelopDate())) {
devDate = entity.getDevelopDate();
}
}
CompanyCustomerEvaluationFormFile evaluationFormFile = getCustomerFileService().findAllCustomerEvaluationFormFiles(customer).stream().filter(v -> {
CompanyCustomerFile customerFile = v.getCustomerFile();
if (customerFile == null) {
return false;
}
return customerFile.isValid();
}).max(Comparator.comparing(v -> v.getCustomerFile().getSignDate())).orElse(null);
if (evaluationFormFile == null) {
holder.warn(company.getName() + " 未匹配的客户评估");
continue;
}
CompanyCustomerFile customerFile = evaluationFormFile.getCustomerFile();
if (devDate != null && devDate.isAfter(customerFile.getSignDate())) {
holder.debug(company.getName() + " 最新评估日期早于客户开发日期,评估日期:" + customerFile.getSignDate() + ", 开发日期:" + devDate);
}
rowIndex++;
if (rowIndex > 11) {
// 插入行,并复制行的格式
sheet.shiftRows(rowIndex + 3, sheet.getLastRowNum(), 1);
Row templateRow = getRow(sheet, rowIndex + 2, true);
Row newRow = getRow(sheet, rowIndex + 3, true);
if (templateRow != null && newRow != null) {
for (int i = 0; i < templateRow.getLastCellNum(); i++) {
Cell templateCell = templateRow.getCell(i);
Cell newCell = getCell(newRow, i, true);
if (templateCell != null && newCell != null) {
newCell.setCellStyle(templateCell.getCellStyle());
}
}
}
}
Row row = getRow(sheet, rowIndex + 3, true);
setCellValue(row, 0, rowIndex);
setCellValue(row, 1, company.getName());
// 客户类型
setCellValue(row, 2, evaluationFormFile.getCatalog());
setCellValue(row, 3, devDate);
setCellValue(row, 4, "");
setCellValue(row, 5, "");
setCellValue(row, 6, evaluationFormFile.getCreditLevel());
setCellValue(row, 7, "更新 " + customerFile.getSignDate());
}
for (XSSFTable table : ((XSSFSheet) sheet).getTables()) {
if ("表2".equals(table.getName())) {
holder.info("找到表=" + table.getName() + ", style=" + table.getStyleName());
table.setCellReferences(new AreaReference("$A$4:$H$" + (rowIndex + 4), SpreadsheetVersion.EXCEL2007));
// table.setDataRowCount(rowIndex);
//table.getCTTable().setRef("$A$4:$H$" + (rowIndex + 4));
}
}
wb.setPrintArea(wb.getSheetIndex(sheet), "A1:H" + (rowIndex + 7));
PrintSetup printSetup = sheet.getPrintSetup();
printSetup.setPaperSize(PrintSetup.A4_PAPERSIZE); // A4 纸
printSetup.setFitHeight((short) 0);
printSetup.setFitWidth((short) 1); // 调整为 1 页宽
sheet.setFitToPage(true);
sheet.setRepeatingRows(CellRangeAddress.valueOf("$1:$4"));
sheet.getFooter().setCenter("第 &P 页, 共 &N 页");
sheet.getFooter().setRight("&D &T");
try (OutputStream fileOut = new FileOutputStream(destFile)) {
wb.write(fileOut);
} catch (FileNotFoundException e) {
UITools.showExceptionAndWait("写时发生文件错误", e);
}
} catch (Exception e) {
UITools.showExceptionAndWait("保存失败", e);
}
return null;
}
}

View File

@@ -0,0 +1,60 @@
package com.ecep.contract.controller.customer;
import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.controller.table.cell.CompanyTableCell;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.service.CompanyCustomerService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.vm.CompanyCustomerViewModel;
import javafx.application.Platform;
import lombok.Setter;
public class CompanyCustomerManagerSkin
extends
AbstEntityManagerSkin<CompanyCustomer, CompanyCustomerViewModel, CompanyCustomerManagerSkin, CompanyCustomerManagerWindowController> {
@Setter
private CompanyService companyService;
public CompanyCustomerManagerSkin(CompanyCustomerManagerWindowController controller) {
super(controller);
}
public CompanyService getCompanyService() {
if (companyService == null) {
companyService = getBean(CompanyService.class);
}
return companyService;
}
public CompanyCustomerService getCompanyCustomerService() {
return controller.getViewModelService();
}
@Override
public void initializeTable() {
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
controller.companyColumn.setCellValueFactory(param -> param.getValue().getCompany());
controller.companyColumn.setCellFactory(param-> new CompanyTableCell<>(getCompanyService()));
controller.developDateColumn.setCellValueFactory(param -> param.getValue().getDevelopDate());
controller.pathColumn.setCellValueFactory(param -> param.getValue().getPath());
controller.createdColumn
.setCellValueFactory(param -> param.getValue().getCreated().map(MyDateTimeUtils::format));
Platform.runLater(() -> {
getTableView().getSortOrder().add(controller.idColumn);
});
}
@Override
protected void onTableRowDoubleClickedAction(CompanyCustomerViewModel item) {
showInOwner(item);
}
private void showInOwner(CompanyCustomerViewModel model) {
showInOwner(CompanyCustomerWindowController.class, model);
}
}

View File

@@ -0,0 +1,166 @@
package com.ecep.contract.controller.customer;
import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.controller.AbstManagerWindowController;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.service.CompanyCustomerService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CompanyCustomerViewModel;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.control.*;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.hibernate.Hibernate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
import java.io.File;
import java.time.LocalDate;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/company/customer/customer_manager.fxml")
public class CompanyCustomerManagerWindowController
extends AbstManagerWindowController<CompanyCustomer, CompanyCustomerViewModel, CompanyCustomerManagerSkin> {
// columns
public TableColumn<CompanyCustomerViewModel, Number> idColumn;
public TableColumn<CompanyCustomerViewModel, Company> companyColumn;
public TableColumn<CompanyCustomerViewModel, String> catalogColumn;
public TableColumn<CompanyCustomerViewModel, LocalDate> developDateColumn;
public TableColumn<CompanyCustomerViewModel, String> pathColumn;
public TableColumn<CompanyCustomerViewModel, String> createdColumn;
@Autowired
private CompanyService companyService;
@Autowired
private CompanyCustomerService companyCustomerService;
@Override
public CompanyCustomerService getViewModelService() {
return companyCustomerService;
}
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().set("客户管理");
}
@Override
protected CompanyCustomerManagerSkin createDefaultSkin() {
CompanyCustomerManagerSkin skin = new CompanyCustomerManagerSkin(this);
skin.setCompanyService(companyService);
return skin;
}
/**
* 对所有客户的文件进行重置
* <p>
* 弹出对话框模式,对话框中有进度条和重置的消息
*
* @param event 事件
*/
public void onReBuildFilesAction(ActionEvent event) {
Dialog<String> dialog = new Dialog<>();
dialog.setTitle("客户文件重置");
AtomicBoolean canceled = new AtomicBoolean(false);
VBox box = new VBox();
Label header = new Label("遍历所有客户,对每个可以客户的文件进行“重置”操作");
ProgressBar progressBar = new ProgressBar();
progressBar.setPrefWidth(300);
progressBar.setMinHeight(18);
VBox.setMargin(progressBar, new Insets(8, 0, 8, 0));
ListView<String> listView = new ListView<>();
ObservableList<String> listViewDataSet = FXCollections.observableArrayList();
listView.setItems(listViewDataSet);
VBox.setVgrow(listView, Priority.ALWAYS);
box.getChildren().setAll(header, progressBar, listView);
dialog.getDialogPane().setContent(box);
dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
dialog.setResizable(true);
dialog.setWidth(600);
dialog.setHeight(500);
dialog.setOnCloseRequest(v -> canceled.set(true));
dialog.initOwner(table.getScene().getWindow());
dialog.show();
CompletableFuture.runAsync(() -> {
Pageable pageRequest = PageRequest.ofSize(50);
while (!canceled.get()) {
Page<CompanyCustomer> page = companyCustomerService.findAll(null, pageRequest);
int index = page.getNumber() * page.getSize();
int i = 1;
for (CompanyCustomer companyCustomer : page) {
if (canceled.get()) {
return;
}
Company company = companyCustomer.getCompany();
if (!Hibernate.isInitialized(company)) {
company = companyService.findById(company.getId());
}
String prefix = (index + i) + "/" + page.getTotalElements() + ", " + company.getName() + "> ";
companyCustomerService.reBuildingFiles(companyCustomer, (level, msg) -> {
Platform.runLater(() -> {
listViewDataSet.add(prefix + msg);
listView.scrollTo(listViewDataSet.size() - 1);
});
});
double progress = (double) (index + i) / page.getTotalElements();
Platform.runLater(() -> {
progressBar.setProgress(progress);
});
i++;
}
if (!page.hasNext()) {
break;
}
pageRequest = page.nextPageable();
}
});
}
public void onCreateNewCustomerAction(ActionEvent event) {
}
public void onBaseTableRefreshAction(ActionEvent event) {
getSkin().loadTableDataSet(true);
}
public void onExportAction(ActionEvent event) {
CompanyCustomerExportExcelTasker tasker = new CompanyCustomerExportExcelTasker();
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("导出");
fileChooser.setInitialFileName("客户资信台账-" + MyDateTimeUtils.format(LocalDate.now()) + ".xlsx");
fileChooser.setInitialDirectory(companyCustomerService.getBasePath());
File destFile = fileChooser.showSaveDialog(table.getScene().getWindow());
tasker.setDestFile(destFile);
UITools.showTaskDialogAndWait("导出Excel", tasker, null);
}
}

View File

@@ -0,0 +1,124 @@
package com.ecep.contract.controller.customer;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.SpringApp;
import com.ecep.contract.UITools;
import com.ecep.contract.controller.company.CompanyWindowController;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.converter.EntityStringConverter;
import com.ecep.contract.ds.company.service.CompanyContactService;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyContact;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.service.CompanyStringConverter;
import com.ecep.contract.vm.CompanyCustomerViewModel;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.ActionEvent;
import javafx.scene.control.Tab;
import javafx.scene.control.TextField;
import javafx.util.converter.LocalDateStringConverter;
public class CompanyCustomerTabSkinBase
extends AbstEntityBasedTabSkin<CompanyCustomerWindowController, CompanyCustomer, CompanyCustomerViewModel>
implements TabSkin {
public CompanyCustomerTabSkinBase(CompanyCustomerWindowController controller) {
super(controller);
}
@Override
public Tab getTab() {
return controller.baseInfoTab;
}
@Override
public void initializeTab() {
initializeCompanyFieldAutoCompletion(controller.companyField);
initializeContactFieldAutoCompletion(controller.contactField);
LocalDateStringConverter converter = new LocalDateStringConverter(DateTimeFormatter.ISO_LOCAL_DATE, null);
controller.developDateField.setConverter(converter);
controller.developDateField.valueProperty().bindBidirectional(viewModel.getDevelopDate());
controller.pathField.textProperty().bind(viewModel.getPath());
controller.descriptionField.textProperty().bindBidirectional(viewModel.getDescription());
controller.createdField.textProperty().bind(
Bindings.createStringBinding(
() -> localDateTimeFormatter(viewModel.getCreated()),
viewModel.getCreated())
);
controller.versionLabel.textProperty().bind(viewModel.getVersion().asString());
controller.relativeCompanyBtn.disableProperty().bind(viewModel.getCompany().isNull());
controller.relativeCompanyBtn.setOnAction(event -> {
Company company = viewModel.getCompany().get();
if (company != null) {
CompanyWindowController.show(company, controller.root.getScene().getWindow());
}
});
controller.createPathBtn.setOnAction(this::onCompanyCustomerCreatePathAction);
controller.changePathBtn.setOnAction(this::onCompanyCustomerChangePathAction);
controller.pathAsNameBtn.setOnAction(this::onCompanyCustomerPathSameAsNameAction);
}
protected String localDateTimeFormatter(SimpleObjectProperty<LocalDateTime> property) {
LocalDateTime dateTime = property.get();
if (dateTime == null) {
return "";
}
return MyDateTimeUtils.format(dateTime);
}
private void initializeContactFieldAutoCompletion(TextField textField) {
EntityStringConverter<CompanyContact> stringConverter = new EntityStringConverter<>();
stringConverter.setInitialized(cc -> getCompanyContactService().findById(cc.getId()));
UITools.autoCompletion(textField, viewModel.getContact(), p -> getCompanyContactService().searchByCompany(viewModel.getCompany().get(), p.getUserText()), stringConverter);
}
private void initializeCompanyFieldAutoCompletion(TextField textField) {
CompanyStringConverter converter = SpringApp.getBean(CompanyStringConverter.class);
UITools.autoCompletion(textField, viewModel.getCompany(), converter::suggest, converter);
}
public void onCompanyCustomerCreatePathAction(ActionEvent event) {
CompanyCustomer companyCustomer = getCompanyCustomerService().findById(viewModel.getId().get());
if (getCompanyCustomerService().makePathAbsent(companyCustomer)) {
companyCustomer = getCompanyCustomerService().save(companyCustomer);
viewModel.update(companyCustomer);
} else {
setStatus("目录存在或创建失败");
}
}
public void onCompanyCustomerChangePathAction(ActionEvent event) {
setStatus("未实现");
}
public void onCompanyCustomerPathSameAsNameAction(ActionEvent event) {
setStatus("未实现");
}
public CompanyCustomerService getCompanyCustomerService() {
return controller.companyCustomerService;
}
public CompanyContactService getCompanyContactService() {
return controller.companyContactService;
}
public CompanyService getCompanyService() {
return controller.companyService;
}
}

View File

@@ -0,0 +1,127 @@
package com.ecep.contract.controller.customer;
import java.io.File;
import org.hibernate.Hibernate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.ecep.contract.DesktopUtils;
import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.service.CompanyContactService;
import com.ecep.contract.service.CompanyCustomerService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.vm.CompanyCustomerViewModel;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/company/customer/customer.fxml")
public class CompanyCustomerWindowController extends AbstEntityController<CompanyCustomer, CompanyCustomerViewModel> {
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerWindowController.class);
/**
* 显示界面
*/
public static void show(CompanyCustomer customer, Window window) {
CompanyCustomerViewModel viewModel = new CompanyCustomerViewModel();
viewModel.update(customer);
show(CompanyCustomerWindowController.class, viewModel, window);
}
public Tab baseInfoTab;
public BorderPane root;
public TabPane tabPane;
public Tab fileTab;
public Tab entityTab;
public Tab satisfactionTab;
public TextField companyField;
public TextField contactField;
public TextField pathField;
public TextArea descriptionField;
public DatePicker developDateField;
public TextField createdField;
public Label versionLabel;
public Button relativeCompanyBtn;
public Button createPathBtn;
public Button changePathBtn;
public Button pathAsNameBtn;
public Button OpenCustomerPathInExplorerBtn;
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().bind(viewModel.getCompany().map(company -> {
if (company == null) {
return "-";
}
if (!Hibernate.isInitialized(company)) {
company = getCompanyService().findById(company.getId());
viewModel.getCompany().set(company);
}
return getMessage("ui.customer.title", String.valueOf(viewModel.getId().get()), company.getName());
}));
}
@Override
protected void registerTabSkins() {
registerTabSkin(baseInfoTab, tab -> new CompanyCustomerTabSkinBase(this));
registerTabSkin(fileTab, tab -> new CustomerTabSkinFile(this));
registerTabSkin(entityTab, tab -> new CustomerTabSkinEntity(this));
registerTabSkin(satisfactionTab, tab -> new CustomerTabSkinSatisfactionSurvey(this));
}
@Override
public CompanyCustomerService getViewModelService() {
return getCachedBean(CompanyCustomerService.class);
}
public CompanyService getCompanyService() {
return getCachedBean(CompanyService.class);
}
public CompanyContactService getCompanyContactService() {
return getCachedBean(CompanyContactService.class);
}
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
OpenCustomerPathInExplorerBtn.setOnAction(this::onCompanyCustomerOpenInExplorerAction);
}
public void onCompanyCustomerOpenInExplorerAction(ActionEvent event) {
String path = viewModel.getPath().get();
if (!StringUtils.hasText(path)) {
setStatus("未设置目录");
return;
}
File file = new File(path);
if (!file.exists()) {
setStatus("目录错误,不存在");
return;
}
DesktopUtils.showInExplorer(file);
}
}

View File

@@ -0,0 +1,108 @@
package com.ecep.contract.controller.customer;
import java.time.LocalDate;
import java.time.LocalDateTime;
import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.SpringApp;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.converter.EmployeeStringConverter;
import com.ecep.contract.converter.EntityStringConverter;
import com.ecep.contract.ds.customer.service.CompanyCustomerEntityService;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CompanyCustomerEntity;
import com.ecep.contract.model.CustomerCatalog;
import com.ecep.contract.ui.table.cell.LocalDateTimeTableCell;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vm.CustomerEntityViewModel;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
import lombok.Setter;
@FxmlPath("/ui/company/customer/customer-tab-entity.fxml")
public class CustomerTabSkinEntity
extends AbstCompanyCustomerTableTabSkin<CompanyCustomerEntity, CustomerEntityViewModel> {
// 关联项 tab
public TableColumn<CustomerEntityViewModel, Number> entityTable_idColumn;
public TableColumn<CustomerEntityViewModel, String> entityTable_catalogColumn;
public TableColumn<CustomerEntityViewModel, String> entityTable_nameColumn;
public TableColumn<CustomerEntityViewModel, String> entityTable_abbNameColumn;
public TableColumn<CustomerEntityViewModel, String> entityTable_codeColumn;
public TableColumn<CustomerEntityViewModel, String> entityTable_creatorColumn;
public TableColumn<CustomerEntityViewModel, LocalDate> entityTable_developDateColumn;
public TableColumn<CustomerEntityViewModel, String> entityTable_modifierColumn;
public TableColumn<CustomerEntityViewModel, LocalDate> entityTable_modifyDateColumn;
public TableColumn<CustomerEntityViewModel, LocalDate> entityTable_updatedDateColumn;
public TableColumn<CustomerEntityViewModel, LocalDateTime> fetchedTimeColumn;
public MenuItem entityTable_menu_refresh;
public MenuItem entityTable_menu_del;
@Setter
private CompanyCustomerEntityService customerEntityService;
public CustomerTabSkinEntity(CompanyCustomerWindowController controller) {
super(controller);
}
@Override
public Tab getTab() {
return controller.entityTab;
}
@Override
public void initializeTab() {
super.initializeTab();
entityTable_idColumn.setCellValueFactory(param -> param.getValue().getId());
entityTable_nameColumn.setCellValueFactory(param -> param.getValue().getName());
entityTable_abbNameColumn.setCellValueFactory(param -> param.getValue().getAbbName());
entityTable_codeColumn.setCellValueFactory(param -> param.getValue().getCode());
initializeEntityTabCatalogColumn(entityTable_catalogColumn);
EmployeeStringConverter stringConverter = SpringApp.getBean(EmployeeStringConverter.class);
entityTable_developDateColumn.setCellValueFactory(param -> param.getValue().getDevelopDate());
entityTable_modifyDateColumn.setCellValueFactory(param -> param.getValue().getModifyDate());
entityTable_creatorColumn.setCellValueFactory(param -> param.getValue().getCreator().map(stringConverter::toString));
entityTable_modifierColumn.setCellValueFactory(param -> param.getValue().getModifier().map(stringConverter::toString));
entityTable_updatedDateColumn.setCellValueFactory(param -> param.getValue().getUpdatedDate());
fetchedTimeColumn.setCellValueFactory(param -> param.getValue().getFetchedTime());
fetchedTimeColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
entityTable_menu_refresh.setOnAction(this::onTableRefreshAction);
entityTable_menu_del.setOnAction(this::onTableDeleteAction);
}
private void initializeEntityTabCatalogColumn(TableColumn<CustomerEntityViewModel, String> column) {
EntityStringConverter<CustomerCatalog> converter = new EntityStringConverter<>();
converter.setInitialized(v -> getCompanyCustomerService().findCatalogById(v.getId()));
column.setCellValueFactory(param -> param.getValue().getCatalog().map(converter::toString));
}
CompanyCustomerEntityService getCompanyCustomerEntityService() {
if (customerEntityService == null) {
customerEntityService = getBean(CompanyCustomerEntityService.class);
}
return customerEntityService;
}
@Override
protected CompanyCustomerEntityService getViewModelService() {
return getCompanyCustomerEntityService();
}
@Override
public Specification<CompanyCustomerEntity> getSpecification(CompanyCustomer parent) {
return SpecificationUtils.and(getSpecification(), (root, query, builder) -> {
return builder.equal(root.get("customer"), parent);
});
}
}

View File

@@ -0,0 +1,387 @@
package com.ecep.contract.controller.customer;
import com.ecep.contract.*;
import com.ecep.contract.constant.CompanyCustomerConstant;
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
import com.ecep.contract.model.*;
import com.ecep.contract.service.CompanyCustomerFileService;
import com.ecep.contract.service.CompanyCustomerService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CompanyCustomerFileViewModel;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.collections.ObservableMap;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.scene.control.*;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import lombok.Setter;
import org.hibernate.Hibernate;
import org.springframework.util.StringUtils;
import java.io.File;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
@FxmlPath("/ui/company/customer/customer-tab-file.fxml")
public class CustomerTabSkinFile
extends AbstCompanyCustomerTableTabSkin<CompanyCustomerFile, CompanyCustomerFileViewModel>
implements EditableEntityTableTabSkin<CompanyCustomerFile, CompanyCustomerFileViewModel> {
@Setter
private CompanyCustomerFileService companyCustomerFileService;
public TableColumn<CompanyCustomerFileViewModel, Number> fileTable_idColumn;
public TableColumn<CompanyCustomerFileViewModel, String> fileTable_typeColumn;
public TableColumn<CompanyCustomerFileViewModel, String> fileTable_filePathColumn;
public TableColumn<CompanyCustomerFileViewModel, String> fileTable_editFilePathColumn;
public TableColumn<CompanyCustomerFileViewModel, LocalDate> fileTable_signDateColumn;
public TableColumn<CompanyCustomerFileViewModel, Boolean> fileTable_validColumn;
public TableColumn<CompanyCustomerFileViewModel, String> fileTable_descriptionColumn;
public Button fileTable_reBuildBtn;
public Button fileTable_updateEvaluationFormBuildBtn;
public Button fileTable_calcNextSignDateBtn;
public MenuItem fileTable_menu_refresh;
public MenuItem fileTable_menu_add;
public MenuItem fileTable_menu_del;
public CustomerTabSkinFile(CompanyCustomerWindowController controller) {
super(controller);
setDragAndDrop(true);
setDragAndDropFileHandler(this::moveFileToCustomer);
}
// 文件 tab
@Override
public Tab getTab() {
return controller.fileTab;
}
@Override
protected CompanyCustomerFileService getViewModelService() {
return getCompanyCustomerFileService();
}
@Override
public Map<String, Object> getSpecification(CompanyCustomer parent) {
Map<String, Object> params = getSpecification();
params.put("customer", parent.getId());
return params;
}
@Override
public void initializeTable() {
super.initializeTable();
TableView<CompanyCustomerFileViewModel> table = getTableView();
table.disableProperty().bind(viewModel.getPath().isEmpty());
fileTable_idColumn.setCellValueFactory(param -> param.getValue().getId());
ObservableMap<CompanyCustomerFileType, CompanyCustomerFileTypeLocal> observableMapByLocal = SpringApp
.getBean(CompanyCustomerFileTypeLocalRepository.class).getObservableMapByLocal();
fileTable_typeColumn.setCellValueFactory(param -> Bindings.valueAt(observableMapByLocal,
param.getValue().getType()).map(BaseEnumEntity::getValue));
fileTable_filePathColumn.setCellValueFactory(param -> param.getValue().getFilePath());
fileTable_filePathColumn.setCellFactory(param -> new FileTableFilePathTableCell());
fileTable_editFilePathColumn.setCellValueFactory(param -> param.getValue().getEditFilePath());
fileTable_editFilePathColumn.setCellFactory(param -> new FileTableFilePathTableCell());
fileTable_signDateColumn.setCellValueFactory(param -> param.getValue().getSignDate());
fileTable_validColumn.setEditable(true);
fileTable_validColumn.setCellValueFactory(param -> param.getValue().getValid());
fileTable_validColumn.setCellFactory(param -> new CheckBoxTableCell<>());
table.setOnDragOver(event -> {
Dragboard dragboard = event.getDragboard();
if (dragboard.hasFiles()) {
event.acceptTransferModes(TransferMode.MOVE);
}
event.consume();
});
table.setOnDragDropped(event -> {
Dragboard dragboard = event.getDragboard();
boolean success = false;
if (dragboard.hasFiles()) {
List<File> files = dragboard.getFiles();
CompletableFuture.runAsync(() -> {
try {
moveFileToCustomer(files);
} catch (Exception e) {
UITools.showExceptionAndWait("移动文件出错", e);
}
});
}
event.setDropCompleted(success);
event.consume();
});
fileTable_reBuildBtn.setOnAction(this::onFileReBuildingAction);
fileTable_updateEvaluationFormBuildBtn.setOnAction(this::onUpdateEvaluationFormAction);
fileTable_calcNextSignDateBtn.setOnAction(this::onCalcNextSignDateAction);
fileTable_menu_refresh.setOnAction(this::onTableRefreshAction);
fileTable_menu_add.setOnAction(this::onTableDeleteAction);
fileTable_menu_del.setOnAction(this::onFileTableMoveToCompanyPathAction);
}
@Override
protected void onTableRowDoubleClickedAction(CompanyCustomerFileViewModel item) {
CompanyCustomerFileType fileType = item.getType().get();
if (fileType == CompanyCustomerFileType.EvaluationForm) {
// 文件不是 Excel 文件时打开编辑UI
if (!CompanyFileUtils.withExtensions(item.getFilePath().get(), CompanyFileUtils.XLS,
CompanyFileUtils.XLSX)) {
CompanyCustomerEvaluationFormFileWindowController.show(item, controller.root.getScene().getWindow());
return;
}
}
File file = new File(item.getFilePath().get());
if (!file.exists()) {
setStatus("文件不存在 " + file.getName());
return;
}
DesktopUtils.showInExplorer(file);
}
private void moveFileToCustomer(List<File> files) {
String path = viewModel.getPath().get();
if (!StringUtils.hasText(path)) {
setStatus("未设置目录");
return;
}
File dir = new File(path);
if (!dir.exists()) {
setStatus("目录错误,不存在");
return;
}
CompanyCustomer companyCustomer = getParent();
LocalDate nextSignDate = getCompanyCustomerFileService().getNextSignDate(companyCustomer, ((level, message) -> setStatus(message)));
if (nextSignDate != null && files.size() == 1) {
File file = files.getFirst();
String fileName = file.getName();
if (fileName.startsWith("S")) {
String destFileName = CompanyCustomerConstant.EVALUATION_FORM_NAME2 + "_"
+ MyDateTimeUtils.format(nextSignDate)
+ "." + StringUtils.getFilenameExtension(fileName);
File dest = new File(dir, destFileName);
if (file.renameTo(dest)) {
CompanyCustomerFile ccf = new CompanyCustomerFile();
ccf.setCustomer(companyCustomer);
ccf.setType(CompanyCustomerFileType.EvaluationForm);
ccf.setFilePath(dest.getAbsolutePath());
ccf.setSignDate(nextSignDate);
ccf.setValid(false);
CompanyCustomerFile saved = getCompanyCustomerFileService().save(ccf);
Platform.runLater(() -> {
CompanyCustomerFileViewModel model = new CompanyCustomerFileViewModel();
model.update(saved);
dataSet.add(model);
CompanyCustomerEvaluationFormFileWindowController.show(model,
getTableView().getScene().getWindow());
});
return;
}
}
}
List<CompanyCustomerFile> companyCustomerFiles = new ArrayList<>();
for (File file : files) {
File dest = new File(dir, file.getName());
if (file.renameTo(dest)) {
CompanyCustomerFile ccf = new CompanyCustomerFile();
ccf.setCustomer(companyCustomer);
ccf.setType(CompanyCustomerFileType.General);
ccf.setFilePath(dest.getAbsolutePath());
ccf.setValid(false);
companyCustomerFiles.add(ccf);
}
}
getCompanyCustomerFileService().saveAll(companyCustomerFiles);
loadTableDataSet();
}
public void onFileReBuildingAction(ActionEvent event) {
CompletableFuture.runAsync(() -> {
CompanyCustomerService customerService = getCompanyCustomerService();
try {
CompanyCustomer companyCustomer = customerService.findById(viewModel.getId().get());
if (customerService.reBuildingFiles(companyCustomer, (level, message) -> setStatus(message))) {
loadTableDataSet();
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
@Override
public CompanyCustomerFile loadRowData(CompanyCustomerFileViewModel row) {
return getCompanyCustomerFileService().findById(row.getId().get());
}
@Override
public CompanyCustomerFile saveRowData(CompanyCustomerFile entity) {
return getCompanyCustomerFileService().save(entity);
}
@Override
public void deleteRowData(CompanyCustomerFile entity) {
getCompanyCustomerFileService().delete(entity);
}
@Override
protected boolean deleteRow(CompanyCustomerFileViewModel row) {
String path = row.getFilePath().get();
if (super.deleteRow(row)) {
File file = new File(path);
if (file.exists()) {
UITools.showConfirmation("数据记录已经删除,请确认是否删除物理文件", path)
.thenAccept(buttonType -> {
if (buttonType == ButtonType.OK) {
if (file.delete()) {
setStatus("删除文件 " + path);
}
}
});
}
return true;
}
return false;
}
public void onFileTableMoveToCompanyPathAction(ActionEvent event) {
Company company = viewModel.getCompany().get();
if (!Hibernate.isInitialized(company)) {
company = getCompanyService().findById(company.getId());
viewModel.getCompany().set(company);
}
if (!StringUtils.hasText(company.getPath())) {
setStatus("公司目录未设置");
return;
}
File companyPath = new File(company.getPath());
if (!companyPath.exists()) {
setStatus("公司目录设置设置异常,无法访问");
return;
}
CompanyCustomerFileViewModel selectedItem = getSelectedItem();
if (selectedItem == null) {
return;
}
String filePath = selectedItem.getFilePath().get();
String editFilePath = selectedItem.getEditFilePath().get();
if (StringUtils.hasText(filePath)) {
File file = new File(filePath);
if (file.exists()) {
File dest = new File(companyPath, file.getName());
if (file.renameTo(dest)) {
setStatus(file.getAbsolutePath() + " -> " + dest.getAbsolutePath());
}
}
}
if (StringUtils.hasText(editFilePath)) {
File file = new File(editFilePath);
if (file.exists()) {
File dest = new File(companyPath, file.getName());
if (file.renameTo(dest)) {
setStatus(file.getAbsolutePath() + " -> " + dest.getAbsolutePath());
}
}
}
deleteRow(selectedItem);
// getCompanyCustomerService().deleteFileById(selectedItem.getId().get());
// dataSet.remove(selectedItem);
}
private void initializeTask(Task<Object> task, String prefix, Consumer<String> consumer) {
task.setOnScheduled(e -> {
consumer.accept("正在" + prefix + ",请稍后...");
});
task.setOnRunning(e -> {
consumer.accept("开始" + prefix + "...");
});
task.setOnSucceeded(e -> {
consumer.accept(prefix + "同步完成...");
});
task.exceptionProperty().addListener((observable, oldValue, newValue) -> {
consumer.accept(newValue.getMessage());
});
SpringApp.getBean(ScheduledExecutorService.class).submit(task);
consumer.accept("任务已创建...");
}
public void onUpdateEvaluationFormAction(ActionEvent event) {
CompanyCustomerEvaluationFormUpdateTask task = new CompanyCustomerEvaluationFormUpdateTask();
task.setCompanyService(getCompanyService());
task.setCompanyCustomerFileService(getCompanyCustomerFileService());
task.setCustomer(getCompanyCustomerService().findById(viewModel.getId().get()));
UITools.showTaskDialogAndWait("更新评价表", task, consumer -> {
initializeTask(task, "更新评价表", msg -> consumer.accept(Message.info(msg)));
});
loadTableDataSet();
}
public void onCalcNextSignDateAction(ActionEvent event) {
UITools.showDialogAndWait("计算客户下一个评价日期", "依据已有的客户评估表和登记采购的合同计算下一个评估日期", ds -> {
CompanyCustomer companyCustomer = getCompanyCustomerService().findById(viewModel.getId().get());
LocalDate nextSignDate = getCompanyCustomerFileService().getNextSignDate(companyCustomer, (level, msg) -> {
Platform.runLater(() -> {
ds.add(msg);
});
});
if (nextSignDate != null) {
Platform.runLater(() -> {
ds.add("下一个评价日期:" + nextSignDate);
});
}
});
}
class FileTableFilePathTableCell extends TableCell<CompanyCustomerFileViewModel, String> {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || !StringUtils.hasText(item)) {
setText("");
return;
}
String path = viewModel.getPath().get();
if (StringUtils.hasText(path)) {
if (item.startsWith(path)) {
item = "~" + item.substring(path.length());
}
}
setText(item);
}
}
private CompanyCustomerFileService getCompanyCustomerFileService() {
if (companyCustomerFileService == null) {
companyCustomerFileService = SpringApp.getBean(CompanyCustomerFileService.class);
}
return companyCustomerFileService;
}
}

View File

@@ -0,0 +1,112 @@
package com.ecep.contract.controller.customer;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Map;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.project.satisfaction_survey.CustomerSatisfactionSurveyWindowController;
import com.ecep.contract.controller.table.cell.EmployeeTableCell;
import com.ecep.contract.controller.table.cell.ProjectTableCell;
import com.ecep.contract.converter.EntityStringConverter;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CustomerCatalog;
import com.ecep.contract.model.CustomerSatisfactionSurvey;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.Project;
import com.ecep.contract.service.CustomerSatisfactionSurveyService;
import com.ecep.contract.service.ProjectService;
import com.ecep.contract.vm.CustomerEntityViewModel;
import com.ecep.contract.vm.CustomerSatisfactionSurveyViewModel;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
import lombok.Setter;
@FxmlPath("/ui/company/customer/customer-tab-satisfaction-survey.fxml")
public class CustomerTabSkinSatisfactionSurvey
extends AbstCompanyCustomerTableTabSkin<CustomerSatisfactionSurvey, CustomerSatisfactionSurveyViewModel> {
// 关联项 tab
public TableColumn<CustomerSatisfactionSurveyViewModel, Number> idColumn;
public TableColumn<CustomerSatisfactionSurveyViewModel, Project> projectColumn;
public TableColumn<CustomerSatisfactionSurveyViewModel, String> codeColumn;
public TableColumn<CustomerSatisfactionSurveyViewModel, Number> totalScoreColumn;
public TableColumn<CustomerSatisfactionSurveyViewModel, Employee> applicantColumn;
public TableColumn<CustomerSatisfactionSurveyViewModel, LocalDateTime> applyTimeColumn;
public TableColumn<CustomerSatisfactionSurveyViewModel, String> descriptionColumn;
public TableColumn<CustomerSatisfactionSurveyViewModel, LocalDate> dateColumn;
public MenuItem entityTable_menu_refresh;
public MenuItem entityTable_menu_del;
@Setter
private ProjectService projectService;
@Setter
private CustomerSatisfactionSurveyService satisfactionSurveyService;
public CustomerTabSkinSatisfactionSurvey(CompanyCustomerWindowController controller) {
super(controller);
}
@Override
public Tab getTab() {
return controller.satisfactionTab;
}
@Override
public void initializeTab() {
super.initializeTab();
bindNumberColumn(idColumn, CustomerSatisfactionSurveyViewModel::getId);
bindColumn(codeColumn, CustomerSatisfactionSurveyViewModel::getCode);
projectColumn.setCellValueFactory(param -> param.getValue().getProject());
projectColumn.setCellFactory(cell -> new ProjectTableCell<>(getProjectService()));
bindLocalDateColumn(dateColumn, CustomerSatisfactionSurveyViewModel::getDate);
bindNumberColumn(totalScoreColumn, CustomerSatisfactionSurveyViewModel::getTotalScore);
applicantColumn.setCellValueFactory(param -> param.getValue().getApplicant());
applicantColumn.setCellFactory(cell -> new EmployeeTableCell<>(getEmployeeService()));
bindLocalDateTimeColumn(applyTimeColumn, CustomerSatisfactionSurveyViewModel::getApplyTime);
bindColumn(descriptionColumn, CustomerSatisfactionSurveyViewModel::getDescription);
entityTable_menu_refresh.setOnAction(this::onTableRefreshAction);
entityTable_menu_del.setOnAction(this::onTableDeleteAction);
}
private void initializeEntityTabCatalogColumn(TableColumn<CustomerEntityViewModel, String> column) {
EntityStringConverter<CustomerCatalog> converter = new EntityStringConverter<>();
converter.setInitialized(v -> getCompanyCustomerService().findCatalogById(v.getId()));
column.setCellValueFactory(param -> param.getValue().getCatalog().map(converter::toString));
}
private CustomerSatisfactionSurveyService getCustomerSatisfactionSurveyService() {
if (satisfactionSurveyService == null) {
satisfactionSurveyService = getBean(CustomerSatisfactionSurveyService.class);
}
return satisfactionSurveyService;
}
ProjectService getProjectService() {
if (projectService == null) {
projectService = getBean(ProjectService.class);
}
return projectService;
}
@Override
protected CustomerSatisfactionSurveyService getViewModelService() {
return getCustomerSatisfactionSurveyService();
}
@Override
public Map<String, Object> getSpecification(CompanyCustomer parent) {
Map<String, Object> params = getSpecification();
params.put("project.customer", parent.getId());
return params;
}
@Override
protected void onTableRowDoubleClickedAction(CustomerSatisfactionSurveyViewModel item) {
showInOwner(CustomerSatisfactionSurveyWindowController.class, item);
}
}

View File

@@ -0,0 +1,69 @@
package com.ecep.contract.controller.customer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.tab.SalesOrderTabSkinBase;
import com.ecep.contract.controller.tab.SalesOrderTabSkinBillVoucher;
import com.ecep.contract.controller.tab.SalesOrderTabSkinItems;
import com.ecep.contract.model.SalesOrder;
import com.ecep.contract.service.SaleOrdersService;
import com.ecep.contract.vm.SalesOrderViewModel;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import javafx.stage.Window;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/contract/sale-orders.fxml")
public class SalesOrderWindowController extends AbstEntityController<SalesOrder, SalesOrderViewModel> {
private static final Logger logger = LoggerFactory.getLogger(SalesOrderWindowController.class);
public TabPane tabPane;
public Button saveBtn;
public Tab baseInfoTab;
public Tab itemTab;
public Tab billVoucherTab;
public TextField codeField;
public TextField employeeField;
public TextField verifierDateField;
public TextField verifierField;
public TextField makeDateField;
public TextField makerField;
public TextArea descriptionField;
public static void show(SalesOrderViewModel viewModel, Window window) {
show(SalesOrderWindowController.class, viewModel, window);
}
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().set("销售订单详情");
}
@Override
protected void registerTabSkins() {
registerTabSkin(baseInfoTab, tab -> new SalesOrderTabSkinBase(this));
registerTabSkin(itemTab, tab -> new SalesOrderTabSkinItems(this));
registerTabSkin(billVoucherTab, tab -> new SalesOrderTabSkinBillVoucher(this));
}
@Override
public SaleOrdersService getViewModelService() {
return getCachedBean(SaleOrdersService.class);
}
}

View File

@@ -0,0 +1,98 @@
package com.ecep.contract.controller.department;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.controller.ManagerSkin;
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
import com.ecep.contract.converter.EmployeeStringConverter;
import com.ecep.contract.model.Department;
import com.ecep.contract.model.Employee;
import com.ecep.contract.vm.DepartmentViewModel;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.TextFieldTableCell;
public class DepartmentManagerSkin
extends AbstEntityManagerSkin<Department, DepartmentViewModel, DepartmentManagerSkin, DepartmentManagerWindowController>
implements ManagerSkin, EditableEntityTableTabSkin<Department, DepartmentViewModel> {
public DepartmentManagerSkin(DepartmentManagerWindowController controller) {
super(controller);
}
@Override
public void initializeTable() {
getTableView().setEditable(true);
Specification<Employee> spec = (root, query, cb) -> {
return cb.equal(root.get("isActive"), true);
};
List<Employee> employees = controller.getEmployeeService().findAll(spec, Pageable.ofSize(30)).getContent();
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
controller.nameColumn.setCellValueFactory(param -> param.getValue().getName());
controller.nameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
controller.nameColumn.setOnEditCommit(this::onNameColumnEditCommit);
controller.codeColumn.setCellValueFactory(param -> param.getValue().getCode());
controller.codeColumn.setCellFactory(TextFieldTableCell.forTableColumn());
controller.codeColumn.setOnEditCommit(this::onCodeColumnEditCommit);
controller.leaderColumn.setCellValueFactory(param -> param.getValue().getLeader());
controller.leaderColumn.setCellFactory(ComboBoxTableCell.forTableColumn(getBean(EmployeeStringConverter.class), FXCollections.observableArrayList(employees)));
controller.leaderColumn.setOnEditCommit(this::onLeaderColumnEditCommit);
controller.activeColumn.setCellValueFactory(param -> param.getValue().getIsActive());
controller.activeColumn.setEditable(true);
controller.activeColumn.setCellFactory(CheckBoxTableCell.forTableColumn(controller.activeColumn));
controller.activeColumn.setOnEditCommit(this::onActiveColumnEditCommit);
}
private void onCodeColumnEditCommit(TableColumn.CellEditEvent<DepartmentViewModel, String> event) {
DepartmentViewModel row = event.getRowValue();
row.getCode().set(event.getNewValue());
saveRowData(row);
}
private void onNameColumnEditCommit(TableColumn.CellEditEvent<DepartmentViewModel, String> event) {
DepartmentViewModel row = event.getRowValue();
row.getName().set(event.getNewValue());
saveRowData(row);
}
private void onLeaderColumnEditCommit(TableColumn.CellEditEvent<DepartmentViewModel, Employee> event) {
DepartmentViewModel row = event.getRowValue();
row.getLeader().set(event.getNewValue());
saveRowData(row);
}
private void onActiveColumnEditCommit(TableColumn.CellEditEvent<DepartmentViewModel, Boolean> event) {
DepartmentViewModel row = event.getRowValue();
row.getIsActive().set(event.getNewValue());
saveRowData(row);
}
@Override
protected void onTableRowDoubleClickedAction(DepartmentViewModel item) {
//TODO 显示详情
}
@Override
protected void onTableCreateNewAction(ActionEvent event) {
Department employee = new Department();
employee = controller.getViewModelService().save(employee);
DepartmentViewModel viewModel = DepartmentViewModel.from(employee);
dataSet.add(viewModel);
}
}

View File

@@ -0,0 +1,63 @@
package com.ecep.contract.controller.department;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.controller.AbstManagerWindowController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.ds.other.service.DepartmentService;
import com.ecep.contract.model.Department;
import com.ecep.contract.model.Employee;
import com.ecep.contract.vm.DepartmentViewModel;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
@Lazy
@Scope("prototype")
@Component
@FxmlPath(value = "/ui/employee/department-manager.fxml")
public class DepartmentManagerWindowController
extends AbstManagerWindowController<Department, DepartmentViewModel, DepartmentManagerSkin> {
public TableColumn<DepartmentViewModel, Number> idColumn;
public TableColumn<DepartmentViewModel, String> nameColumn;
public TableColumn<DepartmentViewModel, String> codeColumn;
public TableColumn<DepartmentViewModel, Employee> leaderColumn;
public TableColumn<DepartmentViewModel, Boolean> activeColumn;
@Autowired
DepartmentService departmentService;
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().set("部门管理");
}
@Override
protected DepartmentManagerSkin createDefaultSkin() {
return new DepartmentManagerSkin(this);
}
public void onReBuildFilesAction(ActionEvent event) {
}
public void onBaseTableRefreshAction(ActionEvent event) {
getSkin().loadTableDataSet(true);
}
public void onCreateNewAction(ActionEvent event) {
getSkin().onTableCreateNewAction(event);
}
@Override
public DepartmentService getViewModelService() {
return departmentService;
}
}

View File

@@ -0,0 +1,42 @@
package com.ecep.contract.controller.employee;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.ds.other.service.EmployeeRoleService;
import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.ds.other.service.PermissionService;
import com.ecep.contract.model.Employee;
import com.ecep.contract.vm.EmployeeViewModel;
import lombok.Setter;
public abstract class AbstEmployeeBasedTabSkin
extends AbstEntityBasedTabSkin<EmployeeWindowController, Employee, EmployeeViewModel>
implements TabSkin {
@Setter
private PermissionService permissionService;
@Setter
private EmployeeRoleService employeeRoleService;
public AbstEmployeeBasedTabSkin(EmployeeWindowController controller) {
super(controller);
}
protected EmployeeService getEmployeeService() {
return controller.employeeService;
}
protected EmployeeRoleService getEmployeeRoleService() {
if (employeeRoleService == null) {
employeeRoleService = getBean(EmployeeRoleService.class);
}
return employeeRoleService;
}
protected PermissionService getPermissionService() {
if (permissionService == null) {
permissionService = getBean(PermissionService.class);
}
return permissionService;
}
}

View File

@@ -0,0 +1,44 @@
package com.ecep.contract.controller.employee;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vm.EmployeeBasedViewModel;
import com.ecep.contract.vm.EmployeeViewModel;
import com.ecep.contract.vm.IdentityViewModel;
public abstract class AbstEmployeeTableTabSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>>
extends AbstEntityTableTabSkin<EmployeeWindowController, Employee, EmployeeViewModel, T, TV>
implements TabSkin {
public AbstEmployeeTableTabSkin(EmployeeWindowController controller) {
super(controller);
}
public EmployeeService getEmployeeService() {
return controller.employeeService;
}
@Override
protected TV createNewViewModel() {
TV model = super.createNewViewModel();
if (model instanceof EmployeeBasedViewModel m) {
m.getEmployee().set(getEntity());
}
return model;
}
@Override
public Specification<T> getSpecification(Employee parent) {
return SpecificationUtils.and(getSpecification(), (root, query, builder) -> {
return builder.equal(root.get("employee"), parent);
});
}
}

View File

@@ -0,0 +1,82 @@
package com.ecep.contract.controller.employee;
import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.controller.ManagerSkin;
import com.ecep.contract.ds.other.service.DepartmentService;
import com.ecep.contract.model.Employee;
import com.ecep.contract.ui.table.cell.DepartmentTableCell;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vm.EmployeeViewModel;
import javafx.event.ActionEvent;
import javafx.scene.control.cell.CheckBoxTableCell;
public class EmployeeManagerSkin
extends AbstEntityManagerSkin<Employee, EmployeeViewModel, EmployeeManagerSkin, EmployeeManagerWindowController>
implements ManagerSkin {
public EmployeeManagerSkin(EmployeeManagerWindowController controller) {
super(controller);
}
DepartmentService departmentService;
DepartmentService getDepartmentService() {
if (departmentService == null) {
departmentService = getBean(DepartmentService.class);
}
return departmentService;
}
@Override
public Specification<Employee> getSpecification() {
Specification<Employee> spec = super.getSpecification();
if (controller.activeCheckBox.isSelected()) {
spec = SpecificationUtils.and(spec, (root, query, builder) -> {
return builder.isTrue(root.get("isActive"));
});
}
return spec;
}
@Override
public void initializeTable() {
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
controller.nameColumn.setCellValueFactory(param -> param.getValue().getName());
controller.aliasColumn.setCellValueFactory(param -> param.getValue().getAlias());
controller.codeColumn.setCellValueFactory(param -> param.getValue().getCode());
controller.accountColumn.setCellValueFactory(param -> param.getValue().getAccount());
controller.departmentColumn.setCellValueFactory(param -> param.getValue().getDepartment());
controller.departmentColumn.setCellFactory(param -> new DepartmentTableCell<>(getDepartmentService()));
controller.emailColumn.setCellValueFactory(param -> param.getValue().getEmail());
controller.createdColumn.setCellValueFactory(param -> param.getValue().getCreated());
controller.entryDateColumn.setCellValueFactory(param -> param.getValue().getEntryDate());
controller.leaveDateColumn.setCellValueFactory(param -> param.getValue().getLeaveDate());
controller.activeColumn.setCellValueFactory(param -> param.getValue().getIsActive());
controller.activeColumn.setCellFactory(CheckBoxTableCell.forTableColumn(controller.activeColumn));
}
@Override
protected void onTableRowDoubleClickedAction(EmployeeViewModel item) {
show(item);
}
@Override
protected void onTableCreateNewAction(ActionEvent event) {
Employee employee = new Employee();
employee = controller.getViewModelService().save(employee);
EmployeeViewModel viewModel = EmployeeViewModel.from(employee);
dataSet.add(viewModel);
show(viewModel);
}
public void show(EmployeeViewModel viewModel) {
showInOwner(EmployeeWindowController.class, viewModel);
}
}

View File

@@ -0,0 +1,90 @@
package com.ecep.contract.controller.employee;
import java.time.LocalDate;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.UITools;
import com.ecep.contract.cloud.u8.EmployeesSyncTask;
import com.ecep.contract.constant.CloudServiceConstant;
import com.ecep.contract.controller.AbstManagerWindowController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.model.Department;
import com.ecep.contract.model.Employee;
import com.ecep.contract.vm.EmployeeViewModel;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/employee/employee-manager.fxml")
public class EmployeeManagerWindowController
extends AbstManagerWindowController<Employee, EmployeeViewModel, EmployeeManagerSkin> {
@FXML
public TableColumn<EmployeeViewModel, Number> idColumn;
@FXML
public TableColumn<EmployeeViewModel, String> accountColumn;
@FXML
public TableColumn<EmployeeViewModel, Department> departmentColumn;
@FXML
public TableColumn<EmployeeViewModel, String> nameColumn;
@FXML
public TableColumn<EmployeeViewModel, String> aliasColumn;
@FXML
public TableColumn<EmployeeViewModel, String> codeColumn;
@FXML
public TableColumn<EmployeeViewModel, String> emailColumn;
@FXML
public TableColumn<EmployeeViewModel, LocalDate> createdColumn;
@FXML
public TableColumn<EmployeeViewModel, LocalDate> entryDateColumn;
@FXML
public TableColumn<EmployeeViewModel, LocalDate> leaveDateColumn;
@FXML
public TableColumn<EmployeeViewModel, Boolean> activeColumn;
@FXML
public CheckBox activeCheckBox;
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().set("员工管理");
}
@Override
protected EmployeeManagerSkin createDefaultSkin() {
return new EmployeeManagerSkin(this);
}
/**
* 从 U8系统 同步员工数据
*/
public void onSyncFromU8Action(ActionEvent event) {
Task<Object> task = new EmployeesSyncTask();
UITools.showTaskDialogAndWait("" + CloudServiceConstant.U8_NAME + " 同步员工数据", task, null);
}
public void onBaseTableRefreshAction(ActionEvent event) {
getSkin().loadTableDataSet(true);
}
public void onCreateNewAction(ActionEvent event) {
getSkin().onTableCreateNewAction(event);
}
@Override
public EmployeeService getViewModelService() {
return getEmployeeService();
}
}

View File

@@ -0,0 +1,96 @@
package com.ecep.contract.controller.employee;
import java.time.LocalDateTime;
import java.util.List;
import org.springframework.data.domain.Sort;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.ds.other.service.EmployeeAuthBindService;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.EmployeeAuthBind;
import com.ecep.contract.ui.table.cell.EmployeeTableCell;
import com.ecep.contract.vm.EmployeeAuthBindViewModel;
import javafx.application.Platform;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
import lombok.Setter;
@FxmlPath("/ui/employee/employee-auth-bind.fxml")
public class EmployeeTabSkinAuthBind
extends AbstEmployeeTableTabSkin<EmployeeAuthBind, EmployeeAuthBindViewModel> {
public TableColumn<EmployeeAuthBindViewModel, Number> idColumn;
public TableColumn<EmployeeAuthBindViewModel, String> ipColumn;
public TableColumn<EmployeeAuthBindViewModel, String> macColumn;
public TableColumn<EmployeeAuthBindViewModel, LocalDateTime> createTime;
public TableColumn<EmployeeAuthBindViewModel, LocalDateTime> updateTimeColumn;
public TableColumn<EmployeeAuthBindViewModel, Employee> updaterColumn;
public TableColumn<EmployeeAuthBindViewModel, String> descriptionColumn;
@Setter
private EmployeeAuthBindService employeeAuthBindService;
public EmployeeTabSkinAuthBind(EmployeeWindowController controller) {
super(controller);
}
EmployeeAuthBindService getEmployeeAuthBindService() {
if (employeeAuthBindService == null) {
employeeAuthBindService = getBean(EmployeeAuthBindService.class);
}
return employeeAuthBindService;
}
@Override
protected EmployeeAuthBindService getViewModelService() {
return getEmployeeAuthBindService();
}
@Override
public Tab getTab() {
return controller.authBindTab;
}
@Override
public void initializeTab() {
super.initializeTab();
getTableView().setEditable(true);
bindNumberColumn(idColumn, EmployeeAuthBindViewModel::getId);
bindColumn(ipColumn, EmployeeAuthBindViewModel::getIp);
bindColumn(macColumn, EmployeeAuthBindViewModel::getMac);
bindLocalDateTimeColumn(createTime, EmployeeAuthBindViewModel::getCreateTime);
bindLocalDateTimeColumn(updateTimeColumn, EmployeeAuthBindViewModel::getUpdateTime);
updaterColumn.setCellValueFactory(param -> param.getValue().getUpdater());
updaterColumn.setCellFactory(cell -> new EmployeeTableCell<>());
bindColumn(descriptionColumn, EmployeeAuthBindViewModel::getDescription);
Platform.runLater(() -> {
getTableView().getSortOrder().add(updateTimeColumn);
});
}
@Override
protected void createContextMenu(ContextMenu contextMenu) {
super.createContextMenu(contextMenu);
MenuItem menuItem = new MenuItem("导入未关联");
menuItem.setOnAction(event -> {
EmployeeAuthBindService service = getEmployeeAuthBindService();
List<EmployeeAuthBind> authBinds = service.findAllByEmployee(null, Sort.unsorted());
for (EmployeeAuthBind authBind : authBinds) {
authBind.setEmployee(getEntity());
authBind.setUpdateTime(LocalDateTime.now());
authBind.setUpdater(controller.getCurrentUser());
service.save(authBind);
}
});
contextMenu.getItems().add(menuItem);
}
}

View File

@@ -0,0 +1,71 @@
package com.ecep.contract.controller.employee;
import java.time.format.DateTimeFormatter;
import com.ecep.contract.UITools;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.converter.EntityStringConverter;
import com.ecep.contract.ds.other.service.DepartmentService;
import com.ecep.contract.model.Department;
import javafx.scene.control.Tab;
import javafx.util.converter.LocalDateStringConverter;
public class EmployeeTabSkinBase
extends AbstEmployeeBasedTabSkin
implements TabSkin {
public EmployeeTabSkinBase(EmployeeWindowController controller) {
super(controller);
}
@Override
public Tab getTab() {
return controller.baseInfoTab;
}
@Override
public void initializeTab() {
EntityStringConverter<Department> departmentEntityStringConverter = new EntityStringConverter<>();
DepartmentService departmentService = getBean(DepartmentService.class);
departmentEntityStringConverter.setInitialized(department -> departmentService.findById(department.getId()));
UITools.autoCompletion(controller.departmentField, viewModel.getDepartment(),
p -> departmentService.search(p.getUserText()), departmentEntityStringConverter);
controller.nameField.textProperty().bindBidirectional(viewModel.getName());
controller.aliasField.textProperty().bindBidirectional(viewModel.getAlias());
controller.codeField.textProperty().bind(viewModel.getCode());
controller.accountField.textProperty().bindBidirectional(viewModel.getAccount());
controller.emailField.textProperty().bindBidirectional(viewModel.getEmail());
controller.phoneField.textProperty().bindBidirectional(viewModel.getPhone());
LocalDateStringConverter converter = new LocalDateStringConverter(DateTimeFormatter.ISO_LOCAL_DATE, null);
controller.leaveDateField.setConverter(converter);
controller.leaveDateField.valueProperty().bindBidirectional(viewModel.getLeaveDate());
controller.entryDateField.setConverter(converter);
controller.entryDateField.valueProperty().bindBidirectional(viewModel.getEntryDate());
controller.createdField.setConverter(converter);
controller.createdField.valueProperty().bindBidirectional(viewModel.getCreated());
controller.isActiveField.selectedProperty().bindBidirectional(viewModel.getIsActive());
// Callback<ListView<EmployeeRole>, ListCell<EmployeeRole>> cellFactory = controller.rolesField.getCellFactory();
// StringConverter<EmployeeRole> employeeRoleStringConverter = new EntityStringConverter<>();
// controller.rolesField.setCellFactory(param -> {
// ListCell<EmployeeRole> cell = cellFactory.call(param);
// if (cell instanceof CheckBoxListCell<EmployeeRole> list) {
// list.setConverter(employeeRoleStringConverter);
// }
// return cell;
// });
// controller.rolesField.getCheckModel().getCheckedItems().setAll();
// Property<IndexedCheckModel<EmployeeRole>> selectedRoles = new SimpleObjectProperty<>();
// controller.rolesField.getCheckModel().getCheckedItems().addListener((ListChangeListener<? super EmployeeRole>) changed -> {
// System.out.println("newValue = " + changed);
// });
}
}

View File

@@ -0,0 +1,65 @@
package com.ecep.contract.controller.employee;
import java.time.LocalDateTime;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.ds.other.service.EmployeeLoginHistoryService;
import com.ecep.contract.model.EmployeeLoginHistory;
import com.ecep.contract.ui.table.cell.LocalDateTimeTableCell;
import com.ecep.contract.vm.EmployeeLoginHistoryViewModel;
import javafx.application.Platform;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
@FxmlPath("/ui/employee/employee-login-history.fxml")
public class EmployeeTabSkinLoginHistory
extends AbstEmployeeTableTabSkin<EmployeeLoginHistory, EmployeeLoginHistoryViewModel>
implements TabSkin {
public TableColumn<EmployeeLoginHistoryViewModel, Number> idColumn;
public TableColumn<EmployeeLoginHistoryViewModel, String> ipColumn;
public TableColumn<EmployeeLoginHistoryViewModel, String> macColumn;
public TableColumn<EmployeeLoginHistoryViewModel, LocalDateTime> loginTimeColumn;
public TableColumn<EmployeeLoginHistoryViewModel, LocalDateTime> activeTimeColumn;
private EmployeeLoginHistoryService loginHistoryService;
public EmployeeTabSkinLoginHistory(EmployeeWindowController controller) {
super(controller);
}
EmployeeLoginHistoryService getLoginHistoryService() {
if (loginHistoryService == null) {
loginHistoryService = getBean(EmployeeLoginHistoryService.class);
}
return loginHistoryService;
}
@Override
protected EmployeeLoginHistoryService getViewModelService() {
return getLoginHistoryService();
}
@Override
public Tab getTab() {
return controller.loginHistoryTab;
}
@Override
public void initializeTab() {
super.initializeTab();
idColumn.setCellValueFactory(param -> param.getValue().getId());
ipColumn.setCellValueFactory(param -> param.getValue().getIp());
macColumn.setCellValueFactory(param -> param.getValue().getMac());
loginTimeColumn.setCellValueFactory(param -> param.getValue().getLoginTime());
loginTimeColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
activeTimeColumn.setCellValueFactory(param -> param.getValue().getActiveTime());
activeTimeColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
Platform.runLater(() -> {
getTableView().getSortOrder().add(activeTimeColumn);
});
}
}

View File

@@ -0,0 +1,92 @@
package com.ecep.contract.controller.employee;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.Desktop;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.EmployeeRole;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.ListChangeListener;
import javafx.scene.control.ListCell;
import javafx.scene.control.Tab;
public class EmployeeTabSkinRole
extends AbstEmployeeBasedTabSkin
implements TabSkin {
private final SimpleBooleanProperty changed = new SimpleBooleanProperty(false);
public EmployeeTabSkinRole(EmployeeWindowController controller) {
super(controller);
}
@Override
public BooleanProperty changeProperty() {
return changed;
}
@Override
public Tab getTab() {
return controller.rolesTab;
}
@Override
public void initializeTab() {
initializeListView();
loadSelectedRoles();
}
private void loadSelectedRoles() {
List<EmployeeRole> selectedRoles = getEmployeeService().getRolesByEmployeeId(viewModel.getId().get());
controller.rolesField.getTargetItems().setAll(selectedRoles);
changed.set(false);
}
private void initializeListView() {
// 非系统内置账户
Specification<EmployeeRole> spec = null;
if (!Desktop.instance.getActiveEmployee().isSystemAdministrator()) {
spec = (root, query, cb) -> cb.equal(root.get("systemAdministrator"), false);
}
List<EmployeeRole> roles = getEmployeeRoleService().findAll(spec, Pageable.ofSize(500)).getContent();
controller.rolesField.getSourceItems().setAll(roles);
controller.rolesField.setCellFactory(param -> {
return new ListCell<>() {
@Override
protected void updateItem(EmployeeRole item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(item.getName());
}
}
};
});
controller.rolesField.getTargetItems().addListener((ListChangeListener<EmployeeRole>) change -> {
while (change.next()) {
List<? extends EmployeeRole> added = change.getAddedSubList();
List<? extends EmployeeRole> removed = change.getRemoved();
if (!added.isEmpty() || !removed.isEmpty()) {
changed.set(true);
}
}
});
}
@Override
public void save() {
Employee entity = getEntity();
entity.setRoles(controller.rolesField.getTargetItems());
save(entity);
loadSelectedRoles();
}
}

View File

@@ -0,0 +1,105 @@
package com.ecep.contract.controller.employee;
import org.controlsfx.control.ListSelectionView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.EmployeeRole;
import com.ecep.contract.vm.EmployeeViewModel;
import javafx.scene.control.CheckBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import lombok.Getter;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/employee/employee.fxml")
public class EmployeeWindowController extends AbstEntityController<Employee, EmployeeViewModel> {
private static final Logger logger = LoggerFactory.getLogger(EmployeeWindowController.class);
/**
* 显示界面
*/
public static void show(EmployeeViewModel viewModel, Window window) {
show(EmployeeWindowController.class, viewModel, window);
}
public BorderPane root;
public TabPane tabPane;
public Tab baseInfoTab;
public TextField nameField;
public TextField departmentField;
public TextField aliasField;
public TextField accountField;
public TextField emailField;
public TextField phoneField;
public DatePicker createdField;
public TextField codeField;
public DatePicker leaveDateField;
public DatePicker entryDateField;
public CheckBox isActiveField;
/*
*/
public Tab rolesTab;
public ListSelectionView<EmployeeRole> rolesField;
public Tab loginHistoryTab;
public Tab authBindTab;
/*
*/
public Tab permissionsTab;
public TableView<Tab> permissionsTable;
public static void show(Employee employee, Window owner) {
EmployeeViewModel model = EmployeeViewModel.from(employee);
show(model, owner);
}
@Autowired
@Getter
EmployeeService employeeService;
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
getTitle().bind(viewModel.getName().map(name -> "[" + viewModel.getId().get() + "] " + name + " 员工详情"));
}
@Override
protected void registerTabSkins() {
registerTabSkin(baseInfoTab, tab -> new EmployeeTabSkinBase(this));
registerTabSkin(rolesTab, tab -> new EmployeeTabSkinRole(this));
registerTabSkin(loginHistoryTab, tab -> new EmployeeTabSkinLoginHistory(this));
registerTabSkin(authBindTab, tab -> new EmployeeTabSkinAuthBind(this));
}
@Override
public EmployeeService getViewModelService() {
return employeeService;
}
}

View File

@@ -0,0 +1,132 @@
package com.ecep.contract.controller.inventory;
import java.util.function.Function;
import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.controller.table.LocalDateFieldTableCell;
import com.ecep.contract.converter.EntityStringConverter;
import com.ecep.contract.ds.other.service.InventoryCatalogService;
import com.ecep.contract.ds.other.service.InventoryService;
import com.ecep.contract.model.Inventory;
import com.ecep.contract.model.InventoryCatalog;
import com.ecep.contract.ui.table.cell.LocalDateTimeTableCell;
import com.ecep.contract.vm.InventoryViewModel;
import javafx.beans.property.Property;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.util.converter.CurrencyStringConverter;
import javafx.util.converter.NumberStringConverter;
import lombok.Setter;
public class InventoryManagerSkin extends
AbstEntityManagerSkin<Inventory, InventoryViewModel, InventoryManagerSkin, InventoryManagerWindowController> {
@Setter
private InventoryCatalogService catalogService;
public InventoryManagerSkin(InventoryManagerWindowController controller) {
super(controller);
}
InventoryService getService() {
return controller.getViewModelService();
}
InventoryCatalogService getInventoryCatalogService() {
if (catalogService == null) {
catalogService = getBean(InventoryCatalogService.class);
}
return catalogService;
}
@Override
public void initializeTable() {
getTableView().setEditable(false);
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
controller.nameColumn.setCellValueFactory(param -> param.getValue().getName());
controller.nameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
controller.nameColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getName));
controller.codeColumn.setCellValueFactory(param -> param.getValue().getCode());
controller.codeColumn.setCellFactory(TextFieldTableCell.forTableColumn());
controller.codeColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getCode));
controller.catalogColumn.setCellValueFactory(param -> param.getValue().getCatalog());
EntityStringConverter<InventoryCatalog> catalogStringConverter = new EntityStringConverter<>();
catalogStringConverter.setInitialized(v -> getInventoryCatalogService().findById(v.getId()));
catalogStringConverter.setFormater(InventoryCatalog::getName);
catalogStringConverter.setFromString(v -> getInventoryCatalogService().findByName(v));
catalogStringConverter.setSuggestion(getInventoryCatalogService()::search);
controller.catalogColumn.setCellFactory(TextFieldTableCell.forTableColumn(catalogStringConverter));
controller.catalogColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getCatalog));
controller.specificationColumn.setCellValueFactory(param -> param.getValue().getSpecification());
controller.specificationColumn.setCellFactory(TextFieldTableCell.forTableColumn());
controller.specificationColumn
.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getSpecification));
controller.unitColumn.setCellValueFactory(param -> param.getValue().getUnit());
controller.unitColumn.setCellFactory(TextFieldTableCell.forTableColumn());
controller.unitColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getUnit));
controller.purchaseTaxRateColumn.setCellValueFactory(param -> param.getValue().getPurchaseTaxRate());
controller.purchaseTaxRateColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
controller.purchaseTaxRateColumn
.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getPurchaseTaxRate));
controller.purchasePriceColumn.setCellValueFactory(param -> param.getValue().getPurchasePrice());
controller.purchasePriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(new CurrencyStringConverter()));
// controller.purchasePriceColumn.setOnEditCommit(event ->
// onColumnEditCommit(event, InventoryViewModel::getPurchasePrice));
controller.saleTaxRateColumn.setCellValueFactory(param -> param.getValue().getSaleTaxRate());
controller.saleTaxRateColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
controller.saleTaxRateColumn
.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getSaleTaxRate));
controller.salePriceColumn.setCellValueFactory(param -> param.getValue().getSalePrice());
controller.salePriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(new CurrencyStringConverter()));
// controller.salePriceColumn.setOnEditCommit(event -> onColumnEditCommit(event,
// InventoryViewModel::getSalePrice));
controller.createTimeColumn.setCellValueFactory(param -> param.getValue().getCreateTime());
controller.createTimeColumn.setCellFactory(LocalDateFieldTableCell.forTableColumn());
controller.createTimeColumn
.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getCreateTime));
controller.updateDateColumn.setCellValueFactory(param -> param.getValue().getUpdateDate());
controller.updateDateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
controller.updateDateColumn
.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getUpdateDate));
controller.descriptionColumn.setCellValueFactory(param -> param.getValue().getDescription());
controller.descriptionColumn.setCellFactory(TextFieldTableCell.forTableColumn());
controller.descriptionColumn
.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getDescription));
}
private <T> void onColumnEditCommit(
TableColumn.CellEditEvent<InventoryViewModel, T> event,
Function<InventoryViewModel, Property<T>> supplier) {
InventoryViewModel row = event.getRowValue();
supplier.apply(row).setValue(event.getNewValue());
saveRowData(row);
}
@Override
protected void onTableRowDoubleClickedAction(InventoryViewModel item) {
showInOwner(InventoryWindowController.class, item);
}
@Override
protected void onTableCreateNewAction(ActionEvent event) {
Inventory inventory = getService().save(getService().createNewInstance());
InventoryViewModel viewModel = InventoryViewModel.from(inventory);
dataSet.add(viewModel);
}
}

View File

@@ -0,0 +1,88 @@
package com.ecep.contract.controller.inventory;
import java.time.LocalDate;
import java.time.LocalDateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.UITools;
import com.ecep.contract.controller.AbstManagerWindowController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.ds.other.controller.InventorySyncTask;
import com.ecep.contract.ds.other.service.InventoryService;
import com.ecep.contract.model.Inventory;
import com.ecep.contract.model.InventoryCatalog;
import com.ecep.contract.vm.InventoryViewModel;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
@Lazy
@Scope("prototype")
@Component
@FxmlPath(value = "/ui/inventory/inventory-manager.fxml")
public class InventoryManagerWindowController
extends AbstManagerWindowController<Inventory, InventoryViewModel, InventoryManagerSkin> {
@FXML
public TableColumn<InventoryViewModel, Number> idColumn;
@FXML
public TableColumn<InventoryViewModel, String> nameColumn;
@FXML
public TableColumn<InventoryViewModel, String> codeColumn;
@FXML
public TableColumn<InventoryViewModel, InventoryCatalog> catalogColumn;
@FXML
public TableColumn<InventoryViewModel, String> specificationColumn;
@FXML
public TableColumn<InventoryViewModel, String> unitColumn;
@FXML
public TableColumn<InventoryViewModel, Number> purchaseTaxRateColumn;
@FXML
public TableColumn<InventoryViewModel, Number> purchasePriceColumn;
@FXML
public TableColumn<InventoryViewModel, Number> saleTaxRateColumn;
@FXML
public TableColumn<InventoryViewModel, Number> salePriceColumn;
@FXML
public TableColumn<InventoryViewModel, LocalDate> createTimeColumn;
@FXML
public TableColumn<InventoryViewModel, LocalDateTime> updateDateColumn;
@FXML
public TableColumn<InventoryViewModel, String> descriptionColumn;
@Autowired
private InventoryService service;
@Override
public InventoryService getViewModelService() {
return service;
}
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().set("存货管理");
}
@Override
protected InventoryManagerSkin createDefaultSkin() {
return new InventoryManagerSkin(this);
}
public void onSyncAction(ActionEvent event) {
InventorySyncTask task = new InventorySyncTask();
UITools.showTaskDialogAndWait("同步数据", task, null);
}
public void onCreateNewAction(ActionEvent event) {
getSkin().onTableCreateNewAction(event);
}
}

View File

@@ -0,0 +1,214 @@
package com.ecep.contract.controller.inventory;
import java.text.NumberFormat;
import java.time.format.DateTimeFormatter;
import java.util.function.Consumer;
import org.springframework.util.StringUtils;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.UITools;
import com.ecep.contract.cloud.u8.ctx.InventoryCtx;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
import com.ecep.contract.converter.EmployeeStringConverter;
import com.ecep.contract.converter.EntityStringConverter;
import com.ecep.contract.ds.other.service.InventoryCatalogService;
import com.ecep.contract.ds.other.service.InventoryService;
import com.ecep.contract.model.Inventory;
import com.ecep.contract.model.InventoryCatalog;
import com.ecep.contract.vm.InventoryViewModel;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.ActionEvent;
import javafx.scene.control.Tab;
import javafx.scene.control.TextField;
import javafx.util.converter.CurrencyStringConverter;
import javafx.util.converter.LocalDateStringConverter;
import javafx.util.converter.LocalDateTimeStringConverter;
import javafx.util.converter.NumberStringConverter;
import lombok.Setter;
public class InventoryTabSkinBase
extends AbstEntityBasedTabSkin<InventoryWindowController, Inventory, InventoryViewModel>
implements TabSkin, EditableEntityTableTabSkin<Inventory, InventoryViewModel> {
@Setter
private InventoryService service;
@Setter
private InventoryCatalogService catalogService;
public InventoryTabSkinBase(InventoryWindowController controller) {
super(controller);
}
InventoryService getService() {
if (service == null) {
service = getBean(InventoryService.class);
}
return service;
}
InventoryCatalogService getCatalogService() {
if (catalogService == null) {
catalogService = getBean(InventoryCatalogService.class);
}
return catalogService;
}
@Override
public Tab getTab() {
return controller.baseInfoTab;
}
@Override
public void initializeTab() {
controller.syncBtn.setOnAction(this::onSyncAction);
NumberFormat numberInstance = NumberFormat.getNumberInstance(getLocale());
numberInstance.setMaximumFractionDigits(2);
numberInstance.setMinimumFractionDigits(2);
CurrencyStringConverter currencyStringConverter = new CurrencyStringConverter(numberInstance);
controller.nameField.textProperty().bindBidirectional(viewModel.getName());
controller.nameLockField.selectedProperty().bindBidirectional(viewModel.getNameLock());
controller.codeField.textProperty().bind(viewModel.getCode());
controller.unitField.textProperty().bindBidirectional(viewModel.getUnit());
controller.specificationField.textProperty().bindBidirectional(viewModel.getSpecification());
controller.specificationLockField.selectedProperty().bindBidirectional(viewModel.getSpecificationLock());
EntityStringConverter<InventoryCatalog> catalogConverter = new EntityStringConverter<>();
catalogConverter.setInitialized(v -> getCatalogService().findById(v.getId()));
catalogConverter.setFormater(InventoryCatalog::getName);
catalogConverter.setSuggestion(getCatalogService()::search);
catalogConverter.setFromString(getCatalogService()::findByName);
UITools.autoCompletion(controller.catalogField, viewModel.getCatalog(), catalogConverter);
controller.purchaseTaxRateField.textProperty().bindBidirectional(viewModel.getPurchaseTaxRate(),
new NumberStringConverter());
// 采购价
bindPriceField(controller.purchasePriceField, viewModel.getPurchasePrice(), currencyStringConverter,
viewModel::updatePurchasePrice);
bindPriceField(controller.purchaseTaxPriceField, viewModel.getPurchaseTaxPrice(), currencyStringConverter,
viewModel::updatePurchaseTaxPrice);
controller.saleTaxRateField.textProperty().bindBidirectional(viewModel.getSaleTaxRate(),
new NumberStringConverter());
// 销售价
bindPriceField(controller.salePriceField, viewModel.getSalePrice(), currencyStringConverter,
viewModel::updateSalePrice);
bindPriceField(controller.saleTaxPriceField, viewModel.getSaleTaxPrice(), currencyStringConverter,
viewModel::updateSaleTaxPrice);
// 采购价不能大于销售价
Bindings.greaterThan(viewModel.getPurchasePrice(), viewModel.getSalePrice())
.addListener((observable, oldValue, newValue) -> {
if (newValue) {
controller.purchasePriceField.getStyleClass().add("error");
controller.salePriceField.getStyleClass().add("error");
} else {
controller.purchasePriceField.getStyleClass().remove("error");
controller.salePriceField.getStyleClass().remove("error");
}
});
Bindings.greaterThan(viewModel.getPurchaseTaxPrice(), viewModel.getSaleTaxPrice())
.addListener((observable, oldValue, newValue) -> {
if (newValue) {
controller.purchaseTaxPriceField.getStyleClass().add("error");
controller.saleTaxPriceField.getStyleClass().add("error");
} else {
controller.purchaseTaxPriceField.getStyleClass().remove("error");
controller.saleTaxPriceField.getStyleClass().remove("error");
}
});
EmployeeStringConverter employeeStringConverter = getBean(EmployeeStringConverter.class);
UITools.autoCompletion(controller.creatorField, viewModel.getCreator(), employeeStringConverter);
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(MyDateTimeUtils.DEFAULT_DATE_FORMAT_PATTERN);
controller.createTimeField.textProperty().bindBidirectional(viewModel.getCreateTime(),
new LocalDateStringConverter(dateFormatter, null));
UITools.autoCompletion(controller.updaterField, viewModel.getUpdater(), employeeStringConverter);
DateTimeFormatter dateTimeFormatter = DateTimeFormatter
.ofPattern(MyDateTimeUtils.DEFAULT_DATETIME_FORMAT_PATTERN);
controller.updateDateField.textProperty().bindBidirectional(viewModel.getUpdateDate(),
new LocalDateTimeStringConverter(dateTimeFormatter, null));
controller.weightUnitField.textProperty().bindBidirectional(viewModel.getWeightUnit());
controller.sizeUnitField.textProperty().bindBidirectional(viewModel.getSizeUnit());
controller.volumeUnitField.textProperty().bindBidirectional(viewModel.getVolumeUnit());
controller.weightField.textProperty().bindBidirectional(viewModel.getWeight(), new NumberStringConverter());
controller.packagedWeightField.textProperty().bindBidirectional(viewModel.getPackagedWeight(),
new NumberStringConverter());
controller.sizeLengthField.textProperty().bindBidirectional(viewModel.getSizeLength(),
new NumberStringConverter());
controller.sizeWidthField.textProperty().bindBidirectional(viewModel.getSizeWidth(),
new NumberStringConverter());
controller.sizeHeightField.textProperty().bindBidirectional(viewModel.getSizeHeight(),
new NumberStringConverter());
controller.volumeField.textProperty().bindBidirectional(viewModel.getVolume(), new NumberStringConverter());
controller.packagedSizeLengthField.textProperty().bindBidirectional(viewModel.getPackagedSizeLength(),
new NumberStringConverter());
controller.packagedSizeWidthField.textProperty().bindBidirectional(viewModel.getPackagedSizeWidth(),
new NumberStringConverter());
controller.packagedSizeHeightField.textProperty().bindBidirectional(viewModel.getPackagedSizeHeight(),
new NumberStringConverter());
controller.packagedVolumeField.textProperty().bindBidirectional(viewModel.getPackagedVolume(),
new NumberStringConverter());
controller.descriptionField.textProperty().bindBidirectional(viewModel.getDescription());
}
private void bindPriceField(TextField textField, SimpleDoubleProperty property,
CurrencyStringConverter stringConverter, Consumer<Double> updater) {
textField.setText(stringConverter.toString(property.get()));
property.addListener((observable, oldValue, newValue) -> {
textField.setText(stringConverter.toString(newValue));
});
textField.setOnKeyTyped(event -> {
Number number = stringConverter.fromString(textField.getText());
updater.accept(number.doubleValue());
});
}
private void onSyncAction(ActionEvent event) {
Inventory inventory = getEntity();
setStatus("开始同步数据...");
if (inventory == null) {
setStatus("请选择要同步的数据.");
return;
}
if (!StringUtils.hasText(inventory.getCode())) {
setStatus("请填写商品编码.");
return;
}
setStatus("正在同步数据...");
InventoryCtx ctx = new InventoryCtx();
MessageHolder holder = (lv, msg) -> setStatus(msg);
ctx.initializeRepository(holder);
if (ctx.syncInventoryDetailByCode(inventory, inventory.getCode(), holder)) {
save(inventory);
setStatus("同步数据完成.");
} else {
setStatus("没有数据更新.");
}
}
@Override
public void deleteRowData(Inventory entity) {
getService().delete(entity);
}
@Override
public Inventory loadRowData(InventoryViewModel row) {
return getService().findById(row.getId().get());
}
@Override
public Inventory saveRowData(Inventory entity) {
return getService().save(entity);
}
}

View File

@@ -0,0 +1,123 @@
package com.ecep.contract.controller.inventory;
import java.text.NumberFormat;
import java.time.LocalDate;
import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.controller.ContractWindowController;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.ds.contract.service.ContractItemService;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.ds.other.service.InventoryHistoryPriceService;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.ContractItem;
import com.ecep.contract.model.Inventory;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vm.ContractViewModel;
import com.ecep.contract.vm.InventoryViewModel;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Subquery;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
import javafx.util.converter.CurrencyStringConverter;
import lombok.Setter;
@FxmlPath("/ui/inventory/inventory-contract.fxml")
public class InventoryTabSkinContracts
extends
AbstEntityTableTabSkin<InventoryWindowController, Inventory, InventoryViewModel, Contract, ContractViewModel>
implements TabSkin {
public TableColumn<ContractViewModel, Number> idColumn;
public TableColumn<ContractViewModel, String> nameColumn;
public TableColumn<ContractViewModel, String> codeColumn;
public TableColumn<ContractViewModel, LocalDate> setupDateColumn;
@Setter
InventoryHistoryPriceService inventoryHistoryPriceService;
@Setter
ContractService contractService;
@Setter
ContractItemService contractItemService;
public InventoryTabSkinContracts(InventoryWindowController controller) {
super(controller);
}
@Override
protected ContractService getViewModelService() {
return getContractService();
}
InventoryHistoryPriceService getInventoryHistoryPriceService() {
if (inventoryHistoryPriceService == null) {
inventoryHistoryPriceService = getBean(InventoryHistoryPriceService.class);
}
return inventoryHistoryPriceService;
}
ContractItemService getContractItemService() {
if (contractItemService == null) {
contractItemService = getBean(ContractItemService.class);
}
return contractItemService;
}
ContractService getContractService() {
if (contractService == null) {
contractService = getBean(ContractService.class);
}
return contractService;
}
@Override
public Tab getTab() {
return controller.contractsTab;
}
@Override
public void initializeTable() {
super.initializeTable();
NumberFormat numberInstance = NumberFormat.getNumberInstance(getLocale());
numberInstance.setMaximumFractionDigits(2);
numberInstance.setMinimumFractionDigits(2);
CurrencyStringConverter currencyStringConverter = new CurrencyStringConverter(numberInstance);
idColumn.setCellValueFactory(param -> param.getValue().getId());
nameColumn.setCellValueFactory(param -> param.getValue().getName());
codeColumn.setCellValueFactory(param -> param.getValue().getCode());
setupDateColumn.setCellValueFactory(param -> param.getValue().getSetupDate());
}
@Override
public Specification<Contract> getSpecification(Inventory parent) {
return SpecificationUtils.and(getSpecification(), (root, query, builder) -> {
// 创建ContractItem的子查询
Subquery<Integer> subquery = query.subquery(Integer.class);
Root<ContractItem> from = subquery.from(ContractItem.class);
// 子查询选择与指定库存相关的合同ID
subquery.select(from.get("contract").get("id"))
.where(builder.equal(from.get("inventory"), parent));
// 主查询筛选ID在子查询结果中的合同
return builder.in(root.get("id")).value(subquery);
});
}
@Override
protected void createContextMenu(ContextMenu contextMenu) {
MenuItem item2 = new MenuItem("刷新");
item2.setOnAction(this::onTableRefreshAction);
contextMenu.getItems().add(item2);
}
@Override
protected void onTableRowDoubleClickedAction(ContractViewModel item) {
showInOwner(ContractWindowController.class, item);
}
}

View File

@@ -0,0 +1,280 @@
package com.ecep.contract.controller.inventory;
import java.text.NumberFormat;
import java.time.MonthDay;
import java.time.Year;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import org.hibernate.Hibernate;
import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.ContractPayWay;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.ds.contract.service.ContractItemService;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.ds.other.service.InventoryHistoryPriceService;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.ContractItem;
import com.ecep.contract.model.HistoryPrice;
import com.ecep.contract.model.Inventory;
import com.ecep.contract.model.InventoryHistoryPrice;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vm.InventoryHistoryPriceViewModel;
import com.ecep.contract.vm.InventoryViewModel;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.util.converter.CurrencyStringConverter;
import javafx.util.converter.NumberStringConverter;
import lombok.Setter;
@FxmlPath("/ui/inventory/inventory-history-price.fxml")
public class InventoryTabSkinHistoryPrice
extends
AbstEntityTableTabSkin<InventoryWindowController, Inventory, InventoryViewModel, InventoryHistoryPrice, InventoryHistoryPriceViewModel>
implements TabSkin {
public Button refreshBtn;
public TableColumn<InventoryHistoryPriceViewModel, Number> idColumn;
public TableColumn<InventoryHistoryPriceViewModel, Year> yearColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> latestSaleTaxColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> latestSalePriceColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> latestSaleTaxPriceColumn;
public TableColumn<InventoryHistoryPriceViewModel, MonthDay> latestSalePriceDateColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> latestPurchaseTaxColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> latestPurchasePriceColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> latestPurchaseTaxPriceColumn;
public TableColumn<InventoryHistoryPriceViewModel, MonthDay> latestPurchasePriceDateColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> maxSaleTaxColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> maxSalePriceColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> maxSaleTaxPriceColumn;
public TableColumn<InventoryHistoryPriceViewModel, MonthDay> maxSalePriceDateColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> maxPurchaseTaxColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> maxPurchasePriceColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> maxPurchaseTaxPriceColumn;
public TableColumn<InventoryHistoryPriceViewModel, MonthDay> maxPurchasePriceDateColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> miniSaleTaxColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> miniSalePriceColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> miniSaleTaxPriceColumn;
public TableColumn<InventoryHistoryPriceViewModel, MonthDay> miniSalePriceDateColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> miniPurchaseTaxColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> miniPurchasePriceColumn;
public TableColumn<InventoryHistoryPriceViewModel, Number> miniPurchaseTaxPriceColumn;
public TableColumn<InventoryHistoryPriceViewModel, MonthDay> miniPurchasePriceDateColumn;
@Setter
InventoryHistoryPriceService service;
@Setter
ContractService contractService;
@Setter
ContractItemService contractItemService;
public InventoryTabSkinHistoryPrice(InventoryWindowController controller) {
super(controller);
}
@Override
protected InventoryHistoryPriceService getViewModelService() {
return getService();
}
InventoryHistoryPriceService getService() {
if (service == null) {
service = getBean(InventoryHistoryPriceService.class);
}
return service;
}
ContractItemService getContractItemService() {
if (contractItemService == null) {
contractItemService = getBean(ContractItemService.class);
}
return contractItemService;
}
ContractService getContractService() {
if (contractService == null) {
contractService = getBean(ContractService.class);
}
return contractService;
}
@Override
public Tab getTab() {
return controller.historyPriceTab;
}
@Override
public void initializeTable() {
super.initializeTable();
refreshBtn.setOnAction(this::onRefreshAction);
NumberFormat numberInstance = NumberFormat.getNumberInstance(getLocale());
numberInstance.setMaximumFractionDigits(2);
numberInstance.setMinimumFractionDigits(2);
CurrencyStringConverter currencyStringConverter = new CurrencyStringConverter(numberInstance);
idColumn.setCellValueFactory(param -> param.getValue().getId());
yearColumn.setCellValueFactory(param -> param.getValue().getYear());
latestSaleTaxColumn.setCellValueFactory(param -> param.getValue().getLatestSaleTax());
latestSaleTaxColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
latestSalePriceColumn.setCellValueFactory(param -> param.getValue().getLatestSalePrice());
latestSalePriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
latestSaleTaxPriceColumn.setCellValueFactory(param -> param.getValue().getLatestSaleTaxPrice());
latestSaleTaxPriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
latestSalePriceDateColumn.setCellValueFactory(param -> param.getValue().getLatestSalePriceDate());
latestPurchaseTaxColumn.setCellValueFactory(param -> param.getValue().getLatestPurchaseTax());
latestPurchaseTaxColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
latestPurchaseTaxPriceColumn.setCellValueFactory(param -> param.getValue().getLatestPurchaseTaxPrice());
latestPurchaseTaxPriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
latestPurchasePriceColumn.setCellValueFactory(param -> param.getValue().getLatestPurchasePrice());
latestPurchasePriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
latestPurchasePriceDateColumn.setCellValueFactory(param -> param.getValue().getLatestPurchasePriceDate());
maxSaleTaxColumn.setCellValueFactory(param -> param.getValue().getMaxSaleTax());
maxSaleTaxColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
maxSalePriceColumn.setCellValueFactory(param -> param.getValue().getMaxSalePrice());
maxSalePriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
maxSaleTaxPriceColumn.setCellValueFactory(param -> param.getValue().getMaxSaleTaxPrice());
maxSaleTaxPriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
maxSalePriceDateColumn.setCellValueFactory(param -> param.getValue().getMaxSalePriceDate());
maxPurchaseTaxColumn.setCellValueFactory(param -> param.getValue().getMaxPurchaseTax());
maxPurchaseTaxColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
maxPurchasePriceDateColumn.setCellValueFactory(param -> param.getValue().getMaxPurchasePriceDate());
maxPurchasePriceColumn.setCellValueFactory(param -> param.getValue().getMaxPurchasePrice());
maxPurchasePriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
maxPurchaseTaxPriceColumn.setCellValueFactory(param -> param.getValue().getMaxPurchaseTaxPrice());
maxPurchaseTaxPriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
miniSaleTaxColumn.setCellValueFactory(param -> param.getValue().getMiniSaleTax());
miniSaleTaxColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
miniSalePriceColumn.setCellValueFactory(param -> param.getValue().getMiniSalePrice());
miniSalePriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
miniSaleTaxPriceColumn.setCellValueFactory(param -> param.getValue().getMiniSaleTaxPrice());
miniSaleTaxPriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
miniSalePriceDateColumn.setCellValueFactory(param -> param.getValue().getMiniSalePriceDate());
miniPurchaseTaxColumn.setCellValueFactory(param -> param.getValue().getMiniPurchaseTax());
miniPurchaseTaxColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
miniPurchasePriceColumn.setCellValueFactory(param -> param.getValue().getMiniPurchasePrice());
miniPurchasePriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
miniPurchasePriceDateColumn.setCellValueFactory(param -> param.getValue().getMiniPurchasePriceDate());
miniPurchaseTaxPriceColumn.setCellValueFactory(param -> param.getValue().getMiniPurchaseTaxPrice());
miniPurchaseTaxPriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
}
private void onRefreshAction(ActionEvent event) {
runAsync(() -> {
HashMap<Integer, InventoryHistoryPrice> historyPriceMap = new HashMap<>();
for (InventoryHistoryPrice historyPrice : getService().findAllByInventory(getParent())) {
InventoryHistoryPrice oldValue = historyPriceMap.put(historyPrice.getYear().getValue(), historyPrice);
if (oldValue != null) {
getService().delete(oldValue);
}
}
List<ContractItem> items = getContractItemService().findAllByInventory(getParent());
items.stream().collect(Collectors.groupingBy(v -> {
Contract contract = v.getContract();
if (!Hibernate.isInitialized(contract)) {
contract = getContractService().findById(contract.getId());
v.setContract(contract);
}
return contract.getSetupDate().getYear();
})).forEach((year, list) -> {
InventoryHistoryPrice historyPrice = historyPriceMap.computeIfAbsent(year, k -> {
InventoryHistoryPrice price = new InventoryHistoryPrice();
price.setInventory(getParent());
price.setYear(year);
return price;
});
list.stream().collect(Collectors.groupingBy(v -> {
Contract contract = v.getContract();
return contract.getPayWay();
})).forEach((payWay, contractItems) -> {
if (ContractPayWay.RECEIVE.equals(payWay)) {
// 销售
list.stream().max(Comparator.comparing(v -> {
Contract contract = v.getContract();
return contract.getSetupDate();
})).ifPresent(v -> update(historyPrice.getLatestSalePrice(), v));
list.stream().max(Comparator.comparing(ContractItem::getTaxPrice))
.ifPresent(v -> update(historyPrice.getMaxSalePrice(), v));
list.stream().min(Comparator.comparing(ContractItem::getTaxPrice))
.ifPresent(v -> update(historyPrice.getMiniSalePrice(), v));
} else if (ContractPayWay.PAY.equals(payWay)) {
// 采购
list.stream().max(Comparator.comparing(v -> {
Contract contract = v.getContract();
return contract.getSetupDate();
})).ifPresent(v -> update(historyPrice.getLatestPurchasePrice(), v));
list.stream().max(Comparator.comparing(ContractItem::getTaxPrice))
.ifPresent(v -> update(historyPrice.getMaxPurchasePrice(), v));
list.stream().min(Comparator.comparing(ContractItem::getTaxPrice))
.ifPresent(v -> update(historyPrice.getMiniPurchasePrice(), v));
} else {
}
});
getService().save(historyPrice);
});
loadTableDataSet();
});
}
void update(HistoryPrice historyPrice, ContractItem item) {
if (item == null) {
historyPrice.setTaxRate(0);
historyPrice.setPreTaxPrice(0);
historyPrice.setPostTaxPrice(0);
historyPrice.setMonthDay(null);
return;
}
getContractService();
Contract contract = item.getContract();
if (!Hibernate.isInitialized(contract)) {
contract = getContractService().findById(contract.getId());
item.setContract(contract);
}
historyPrice.setTaxRate((float) item.getTaxRate());
historyPrice.setPreTaxPrice(item.getExclusiveTaxPrice());
historyPrice.setPostTaxPrice(item.getTaxPrice());
historyPrice.setMonthDay(MonthDay.from(contract.getSetupDate()));
}
@Override
public Specification<InventoryHistoryPrice> getSpecification(Inventory parent) {
return SpecificationUtils.and(getSpecification(), (root, query, builder) -> {
return builder.equal(root.get("inventory"), parent);
});
}
@Override
protected void createContextMenu(ContextMenu contextMenu) {
MenuItem item2 = new MenuItem("刷新");
item2.setOnAction(this::onTableRefreshAction);
contextMenu.getItems().add(item2);
}
}

View File

@@ -0,0 +1,140 @@
package com.ecep.contract.controller.inventory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.ds.other.service.InventoryService;
import com.ecep.contract.model.Inventory;
import com.ecep.contract.vm.InventoryViewModel;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.WindowEvent;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/inventory/inventory.fxml")
public class InventoryWindowController extends AbstEntityController<Inventory, InventoryViewModel> {
@FXML
public BorderPane root;
@FXML
public TabPane tabPane;
@FXML
public Tab baseInfoTab;
@FXML
public Tab historyPriceTab;
@FXML
public Tab contractsTab;
@FXML
public TextField nameField;
@FXML
public CheckBox nameLockField;
@FXML
public TextField catalogField;
@FXML
public TextField unitField;
@FXML
public TextField codeField;
@FXML
public TextField specificationField;
@FXML
public CheckBox specificationLockField;
@FXML
public TextField purchasePriceField;
@FXML
public TextField purchaseTaxRateField;
@FXML
public TextField purchaseTaxPriceField;
@FXML
public TextField creatorField;
@FXML
public TextField createTimeField;
@FXML
public TextField updaterField;
@FXML
public TextField updateDateField;
@FXML
public TextField saleTaxRateField;
@FXML
public TextField salePriceField;
@FXML
public TextField saleTaxPriceField;
@FXML
public TextArea descriptionField;
@FXML
public Button syncBtn;
@FXML
public TextField weightUnitField;
@FXML
public TextField sizeUnitField;
@FXML
public TextField volumeUnitField;
@FXML
public TextField weightField;
@FXML
public TextField packagedWeightField;
@FXML
public TextField sizeLengthField;
@FXML
public TextField sizeWidthField;
@FXML
public TextField sizeHeightField;
@FXML
public TextField volumeField;
@FXML
public TextField packagedSizeLengthField;
@FXML
public TextField packagedSizeWidthField;
@FXML
public TextField packagedSizeHeightField;
@FXML
public TextField packagedVolumeField;
@Autowired
private InventoryService service;
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
getTitle().bind(Bindings.createStringBinding(
() -> "[" + viewModel.getId().get() + "] 存货详情 " + viewModel.getName().getValue() + " "
+ viewModel.getSpecification().get(),
viewModel.getName(), viewModel.getCode(), viewModel.getSpecification()));
root.getScene().getStylesheets().add("/ui/inventory/inventory.css");
}
@Override
protected Inventory loadEntity() {
return service.findById(viewModel.getId().get());
}
@Override
protected Inventory saveEntity(Inventory entity) {
return service.save(entity);
}
@Override
protected void registerTabSkins() {
registerTabSkin(baseInfoTab, tab -> new InventoryTabSkinBase(this));
registerTabSkin(historyPriceTab, tab -> new InventoryTabSkinHistoryPrice(this));
registerTabSkin(contractsTab, tab -> new InventoryTabSkinContracts(this));
}
@Override
public InventoryService getViewModelService() {
return service;
}
}

View File

@@ -0,0 +1,39 @@
package com.ecep.contract.controller.permission;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.ds.other.service.EmployeeRoleService;
import com.ecep.contract.ds.other.service.FunctionService;
import com.ecep.contract.ds.other.service.PermissionService;
import com.ecep.contract.model.EmployeeRole;
import com.ecep.contract.vm.EmployeeRoleViewModel;
public abstract class AbstEmployeeRoleBasedTabSkin
extends AbstEntityBasedTabSkin<EmployeeRoleWindowController, EmployeeRole, EmployeeRoleViewModel> {
private EmployeeRoleService roleService;
private FunctionService functionService;
public AbstEmployeeRoleBasedTabSkin(EmployeeRoleWindowController controller) {
super(controller);
viewModel = controller.getViewModel();
}
EmployeeRoleService getRoleService() {
if (roleService == null) {
roleService = getBean(EmployeeRoleService.class);
}
return roleService;
}
FunctionService getFunctionService() {
if (functionService == null) {
functionService = getBean(FunctionService.class);
}
return functionService;
}
public PermissionService getPermissionService() {
return controller.permissionService;
}
}

View File

@@ -0,0 +1,75 @@
package com.ecep.contract.controller.permission;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Description;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.controller.AbstManagerWindowController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.ds.other.service.FunctionService;
import com.ecep.contract.ds.other.service.PermissionService;
import com.ecep.contract.model.Function;
import com.ecep.contract.vm.FunctionViewModel;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
@Lazy
@Scope("prototype")
@Description("")
@FxmlPath("/ui/employee/functions-manager.fxml")
@Component
public class EmployeeFunctionsManagerWindowController
extends AbstManagerWindowController<Function, FunctionViewModel, FunctionManagerSkin> {
@Autowired
PermissionService permissionService;
@Autowired
FunctionService functionService;
@FXML
public TableColumn<FunctionViewModel, Number> idColumn;
@FXML
public TableColumn<FunctionViewModel, String> nameColumn;
@FXML
public TableColumn<FunctionViewModel, String> keyColumn;
@FXML
public TableColumn<FunctionViewModel, String> controllerColumn;
@FXML
public TableColumn<FunctionViewModel, String> iconColumn;
@FXML
public TableColumn<FunctionViewModel, String> descriptionColumn;
@FXML
public TableColumn<FunctionViewModel, Boolean> activeColumn;
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().set("功能管理");
}
@Override
protected FunctionManagerSkin createDefaultSkin() {
return new FunctionManagerSkin(this);
}
public void onCreateNewAction(ActionEvent event) {
}
public void onReBuildFilesAction(ActionEvent event) {
}
@Override
public FunctionService getViewModelService() {
return functionService;
}
public PermissionService getPermissionService() {
return permissionService;
}
}

View File

@@ -0,0 +1,50 @@
package com.ecep.contract.controller.permission;
import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.controller.ManagerSkin;
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
import com.ecep.contract.ds.other.service.PermissionService;
import com.ecep.contract.model.EmployeeRole;
import com.ecep.contract.vm.EmployeeRoleViewModel;
import javafx.scene.control.cell.CheckBoxTableCell;
import lombok.Setter;
public class EmployeeRoleManagerSkin
extends
AbstEntityManagerSkin<EmployeeRole, EmployeeRoleViewModel, EmployeeRoleManagerSkin, EmployeeRoleManagerWindowController>
implements ManagerSkin, EditableEntityTableTabSkin<EmployeeRole, EmployeeRoleViewModel> {
@Setter
private PermissionService permissionService;
public EmployeeRoleManagerSkin(EmployeeRoleManagerWindowController controller) {
super(controller);
}
@Override
public void initializeTable() {
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
controller.codeColumn.setCellValueFactory(param -> param.getValue().getCode());
controller.nameColumn.setCellValueFactory(param -> param.getValue().getName());
controller.systemAdministratorColumn.setCellValueFactory(param -> param.getValue().getSystemAdministrator());
controller.systemAdministratorColumn.setCellFactory(param -> new CheckBoxTableCell<>());
controller.managerColumn.setCellValueFactory(param -> param.getValue().getManager());
controller.managerColumn.setCellFactory(CheckBoxTableCell.forTableColumn(controller.managerColumn));
controller.activeColumn.setCellValueFactory(param -> param.getValue().getActive());
controller.activeColumn.setCellFactory(CheckBoxTableCell.forTableColumn(controller.activeColumn));
}
@Override
protected void onTableRowDoubleClickedAction(EmployeeRoleViewModel item) {
EmployeeRoleWindowController.show(item, controller.table.getScene().getWindow());
}
protected PermissionService getPermissionService() {
if (permissionService == null) {
permissionService = getBean(PermissionService.class);
}
return permissionService;
}
}

View File

@@ -0,0 +1,64 @@
package com.ecep.contract.controller.permission;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.controller.AbstManagerWindowController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.ds.other.service.EmployeeRoleService;
import com.ecep.contract.ds.other.service.PermissionService;
import com.ecep.contract.model.EmployeeRole;
import com.ecep.contract.vm.EmployeeRoleViewModel;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/employee/roles-manager.fxml")
public class EmployeeRoleManagerWindowController
extends AbstManagerWindowController<EmployeeRole, EmployeeRoleViewModel, EmployeeRoleManagerSkin> {
private static final Logger logger = LoggerFactory.getLogger(EmployeeRoleManagerWindowController.class);
@Autowired
private PermissionService permissionService;
@Autowired
EmployeeRoleService employeeRoleService;
public TableColumn<EmployeeRoleViewModel, Number> idColumn;
public TableColumn<EmployeeRoleViewModel, String> codeColumn;
public TableColumn<EmployeeRoleViewModel, String> nameColumn;
public TableColumn<EmployeeRoleViewModel, Boolean> systemAdministratorColumn;
public TableColumn<EmployeeRoleViewModel, Boolean> managerColumn;
public TableColumn<EmployeeRoleViewModel, Boolean> activeColumn;
@Override
protected EmployeeRoleManagerSkin createDefaultSkin() {
EmployeeRoleManagerSkin skin = new EmployeeRoleManagerSkin(this);
skin.setPermissionService(permissionService);
return skin;
}
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().set("角色管理");
}
public void onCreateNewAction(ActionEvent event) {
}
public void onReBuildFilesAction(ActionEvent event) {
}
@Override
public EmployeeRoleService getViewModelService() {
return employeeRoleService;
}
}

View File

@@ -0,0 +1,31 @@
package com.ecep.contract.controller.permission;
import com.ecep.contract.Desktop;
import com.ecep.contract.controller.tab.TabSkin;
import javafx.scene.control.Tab;
public class EmployeeRoleTabSkinBase extends AbstEmployeeRoleBasedTabSkin implements TabSkin {
public EmployeeRoleTabSkinBase(EmployeeRoleWindowController controller) {
super(controller);
}
@Override
public Tab getTab() {
return controller.baseInfoTab;
}
@Override
public void initializeTab() {
controller.nameField.textProperty().bindBidirectional(viewModel.getName());
controller.codeField.textProperty().bindBidirectional(viewModel.getCode());
if (Desktop.instance.getActiveEmployee().isSystemAdministrator()) {
controller.systemAdministratorField.selectedProperty().bindBidirectional(viewModel.getSystemAdministrator());
} else {
controller.systemAdministratorField.selectedProperty().bind(viewModel.getSystemAdministrator());
}
controller.managerField.selectedProperty().bindBidirectional(viewModel.getManager());
controller.activeField.selectedProperty().bindBidirectional(viewModel.getActive());
}
}

View File

@@ -0,0 +1,107 @@
package com.ecep.contract.controller.permission;
import java.util.List;
import org.controlsfx.control.ListSelectionView;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.model.EmployeeRole;
import com.ecep.contract.model.Function;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.ListChangeListener;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.Tab;
public class EmployeeRoleTabSkinFunctions extends AbstEmployeeRoleBasedTabSkin {
private final SimpleBooleanProperty changed = new SimpleBooleanProperty(false);
private ListSelectionView<Function> functionsField;
public EmployeeRoleTabSkinFunctions(EmployeeRoleWindowController controller) {
super(controller);
}
@Override
public void initializeUIComponents() {
super.initializeUIComponents();
functionsField = controller.functionsField;
}
@Override
public Tab getTab() {
return controller.functionsTab;
}
@Override
public void initializeTab() {
Tab tab = getTab();
if (viewModel.getSystemAdministrator().get()) {
tab.setDisable(true);
return;
}
initializeListView();
loadSelectedRoles();
}
private void loadSelectedRoles() {
List<Function> selectedRoles = getRoleService().getFunctionsByRoleId(viewModel.getId().get());
if (selectedRoles != null) {
functionsField.getTargetItems().setAll(selectedRoles);
}
changed.set(false);
}
private void initializeListView() {
// 非系统内置账户
Specification<Function> spec = (root, query, cb) -> cb.equal(root.get("active"), true);
List<Function> roles = getFunctionService().findAll(spec, Pageable.ofSize(500)).getContent();
functionsField.getSourceItems().setAll(roles);
functionsField.setCellFactory(param -> {
return new ListCell<>() {
@Override
protected void updateItem(Function item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(item.getName());
}
}
};
});
functionsField.getTargetItems().addListener((ListChangeListener<Function>) change -> {
while (change.next()) {
List<? extends Function> added = change.getAddedSubList();
List<? extends Function> removed = change.getRemoved();
if (!added.isEmpty() || !removed.isEmpty()) {
changed.set(true);
}
}
});
Node targetFooter = functionsField.getTargetFooter();
if (targetFooter instanceof Button btn) {
initializeSaveButton(btn);
}
targetFooter.disableProperty().bind(changed.not());
}
private void initializeSaveButton(Button button) {
button.setOnAction(this::saveRoles);
}
private void saveRoles(ActionEvent event) {
EmployeeRole entity = getEntity();
entity.setFunctions(functionsField.getTargetItems());
save(entity);
loadSelectedRoles();
}
}

View File

@@ -0,0 +1,101 @@
package com.ecep.contract.controller.permission;
import org.controlsfx.control.ListSelectionView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.ds.other.service.EmployeeRoleService;
import com.ecep.contract.ds.other.service.PermissionService;
import com.ecep.contract.model.EmployeeRole;
import com.ecep.contract.vm.EmployeeRoleViewModel;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/employee/role.fxml")
public class EmployeeRoleWindowController extends AbstEntityController<EmployeeRole, EmployeeRoleViewModel> {
private static final Logger logger = LoggerFactory.getLogger(EmployeeRoleWindowController.class);
public static void show(EmployeeRoleViewModel viewModel, Window window) {
show(EmployeeRoleWindowController.class, viewModel, window);
}
public BorderPane root;
public TabPane tabPane;
/*
*/
public Tab baseInfoTab;
public TextField nameField;
public TextField codeField;
public CheckBox systemAdministratorField;
public CheckBox managerField;
public DatePicker createdField;
@FXML
public CheckBox activeField;
public Label versionLabel;
/*
*/
public Tab functionsTab;
public ListSelectionView<com.ecep.contract.model.Function> functionsField;
/*
*/
public Tab permissionsTab;
@Autowired
PermissionService permissionService;
@Autowired
EmployeeRoleService employeeRoleService;
@Override
protected EmployeeRole loadEntity() {
return employeeRoleService.findById(viewModel.getId().get());
}
@Override
protected EmployeeRole saveEntity(EmployeeRole role) {
return employeeRoleService.save(role);
}
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
getTitle().bind(viewModel.getName().map(name -> "[" + viewModel.getId().get() + "] " + name + " 角色详情"));
}
@Override
protected void registerTabSkins() {
registerTabSkin(baseInfoTab, tab -> new EmployeeRoleTabSkinBase(this));
registerTabSkin(functionsTab, tab -> new EmployeeRoleTabSkinFunctions(this));
}
@Override
public EmployeeRoleService getViewModelService() {
return employeeRoleService;
}
}

View File

@@ -0,0 +1,82 @@
package com.ecep.contract.controller.permission;
import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.ds.other.service.FunctionService;
import com.ecep.contract.ds.other.service.PermissionService;
import com.ecep.contract.model.Function;
import com.ecep.contract.vm.FunctionViewModel;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.TextFieldTableCell;
public class FunctionManagerSkin
extends
AbstEntityManagerSkin<Function, FunctionViewModel, FunctionManagerSkin, EmployeeFunctionsManagerWindowController> {
public FunctionManagerSkin(EmployeeFunctionsManagerWindowController controller) {
super(controller);
}
@Override
public void initializeTable() {
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
controller.nameColumn.setCellValueFactory(param -> param.getValue().getName());
controller.nameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
controller.nameColumn.setOnEditCommit(this::onNameColumnEditCommit);
controller.keyColumn.setCellValueFactory(param -> param.getValue().getKey());
controller.keyColumn.setCellFactory(TextFieldTableCell.forTableColumn());
controller.keyColumn.setOnEditCommit(this::onKeyColumnEditCommit);
controller.controllerColumn.setCellValueFactory(param -> param.getValue().getController());
controller.controllerColumn.setCellFactory(TextFieldTableCell.forTableColumn());
controller.controllerColumn.setOnEditCommit(this::onControllerColumnEditCommit);
controller.iconColumn.setCellValueFactory(param -> param.getValue().getIcon());
controller.iconColumn.setCellFactory(TextFieldTableCell.forTableColumn());
controller.iconColumn.setOnEditCommit(this::onIconColumnEditCommit);
controller.descriptionColumn.setCellValueFactory(param -> param.getValue().getDescription());
controller.descriptionColumn.setCellFactory(TextFieldTableCell.forTableColumn());
controller.descriptionColumn.setOnEditCommit(this::onDescriptionColumnEditCommit);
controller.activeColumn.setCellValueFactory(param -> param.getValue().getActive());
controller.activeColumn.setCellFactory(CheckBoxTableCell.forTableColumn(controller.activeColumn));
}
private void onNameColumnEditCommit(TableColumn.CellEditEvent<FunctionViewModel, String> event) {
acceptCellEditEvent(event, FunctionViewModel::getName);
}
private void onKeyColumnEditCommit(TableColumn.CellEditEvent<FunctionViewModel, String> event) {
acceptCellEditEvent(event, FunctionViewModel::getKey);
}
private void onControllerColumnEditCommit(TableColumn.CellEditEvent<FunctionViewModel, String> event) {
acceptCellEditEvent(event, FunctionViewModel::getController);
}
private void onIconColumnEditCommit(TableColumn.CellEditEvent<FunctionViewModel, String> event) {
acceptCellEditEvent(event, FunctionViewModel::getIcon);
}
private void onDescriptionColumnEditCommit(TableColumn.CellEditEvent<FunctionViewModel, String> event) {
acceptCellEditEvent(event, FunctionViewModel::getDescription);
}
@Override
protected void onTableRowDoubleClickedAction(FunctionViewModel item) {
showInOwner(FunctionWindowController.class, item);
}
protected PermissionService getPermissionService() {
return controller.getPermissionService();
}
protected FunctionService getFunctionService() {
return controller.getViewModelService();
}
}

View File

@@ -0,0 +1,31 @@
package com.ecep.contract.controller.permission;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.model.Function;
import com.ecep.contract.vm.FunctionViewModel;
import javafx.scene.control.Tab;
public class FunctionTabSkinBase
extends AbstEntityBasedTabSkin<FunctionWindowController, Function, FunctionViewModel> {
public FunctionTabSkinBase(FunctionWindowController controller) {
super(controller);
}
@Override
public Tab getTab() {
return controller.baseInfoTab;
}
@Override
public void initializeTab() {
// 绑定UI元素与viewModel的属性
controller.nameField.textProperty().bindBidirectional(viewModel.getName());
controller.keyField.textProperty().bindBidirectional(viewModel.getKey());
controller.controllerField.textProperty().bindBidirectional(viewModel.getController());
controller.iconField.textProperty().bindBidirectional(viewModel.getIcon());
controller.descriptionField.textProperty().bindBidirectional(viewModel.getDescription());
controller.activeField.selectedProperty().bindBidirectional(viewModel.getActive());
}
}

View File

@@ -0,0 +1,70 @@
package com.ecep.contract.controller.permission;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.ds.other.service.PermissionService;
import com.ecep.contract.model.Function;
import com.ecep.contract.model.Permission;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vm.FunctionViewModel;
import com.ecep.contract.vm.PermissionViewModel;
import javafx.fxml.FXML;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
@FxmlPath("/ui/employee/function-tab-permission.fxml")
public class FunctionTabSkinPermission
extends
AbstEntityTableTabSkin<FunctionWindowController, Function, FunctionViewModel, Permission, PermissionViewModel> {
@FXML
public TableColumn<PermissionViewModel, Number> idColumn;
@FXML
public TableColumn<PermissionViewModel, String> nameColumn;
@FXML
public TableColumn<PermissionViewModel, String> keyColumn;
@FXML
public TableColumn<PermissionViewModel, String> descriptionColumn;
@Autowired
private PermissionService permissionService;
public FunctionTabSkinPermission(FunctionWindowController controller) {
super(controller);
}
@Override
public Tab getTab() {
return controller.permissionTab;
}
@Override
public PermissionService getViewModelService() {
if (permissionService == null) {
permissionService = getBean(PermissionService.class);
}
return permissionService;
}
@Override
public void initializeTable() {
super.initializeTable();
idColumn.setCellValueFactory(param -> param.getValue().getId());
nameColumn.setCellValueFactory(param -> param.getValue().getName());
keyColumn.setCellValueFactory(param -> param.getValue().getKey());
// descriptionColumn.setCellValueFactory(param ->
// param.getValue().getDescription());
}
@Override
public Specification<Permission> getSpecification(Function parent) {
return SpecificationUtils.and(getSpecification(), (root, query, criteriaBuilder) -> {
return criteriaBuilder.equal(root.get("function"), parent);
});
}
}

View File

@@ -0,0 +1,89 @@
package com.ecep.contract.controller.permission;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.ds.other.service.FunctionService;
import com.ecep.contract.model.Function;
import com.ecep.contract.vm.FunctionViewModel;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import lombok.Getter;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/employee/function.fxml")
public class FunctionWindowController extends AbstEntityController<Function, FunctionViewModel> {
private static final Logger logger = LoggerFactory.getLogger(FunctionWindowController.class);
/**
* 显示界面
*/
public static void show(FunctionViewModel viewModel, Window window) {
show(FunctionWindowController.class, viewModel, window);
}
@FXML
public BorderPane root;
@FXML
public TabPane tabPane;
@FXML
public Tab baseInfoTab;
@FXML
public Tab permissionTab;
@FXML
public TextField nameField;
@FXML
public TextField keyField;
@FXML
public TextField controllerField;
@FXML
public TextField iconField;
@FXML
public TextField descriptionField;
@FXML
public CheckBox activeField;
@FXML
public Label versionLabel;
public static void show(Function function, Window owner) {
FunctionViewModel model = FunctionViewModel.from(function);
show(model, owner);
}
@Autowired
@Getter
FunctionService functionService;
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
getTitle().bind(viewModel.getName().map(name -> "[" + viewModel.getId().get() + "] " + name + " 功能详情"));
}
@Override
protected void registerTabSkins() {
registerTabSkin(baseInfoTab, tab -> new FunctionTabSkinBase(this));
registerTabSkin(permissionTab, tab -> new FunctionTabSkinPermission(this));
}
@Override
public FunctionService getViewModelService() {
return functionService;
}
}

View File

@@ -0,0 +1,192 @@
package com.ecep.contract.controller.permission;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.UITools;
import com.ecep.contract.controller.ManagerSkin;
import com.ecep.contract.controller.table.TableTabSkin;
import com.ecep.contract.model.Permission;
import com.ecep.contract.vm.FunctionViewModel;
import com.ecep.contract.vm.PermissionViewModel;
import javafx.application.Platform;
import javafx.beans.property.Property;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import lombok.Setter;
public class PermissionManagerSkin implements ManagerSkin, TableTabSkin<Permission, PermissionViewModel> {
private static final Logger logger = LoggerFactory.getLogger(PermissionManagerSkin.class);
EmployeeFunctionsManagerWindowController controller;
@Setter
private FunctionViewModel viewModel;
@Setter
protected TableView<PermissionViewModel> table;
private final ObservableList<PermissionViewModel> dataSet = FXCollections.observableArrayList();
public PermissionManagerSkin(EmployeeFunctionsManagerWindowController controller) {
this.controller = controller;
}
@Override
public void handleException(String message, Throwable ex) {
if (controller != null) {
controller.handleException(message, ex);
return;
}
if (logger.isErrorEnabled()) {
logger.error(message, ex);
}
UITools.showExceptionAndWait(message, ex);
}
@Override
public TableView<PermissionViewModel> getTableView() {
return table;
}
@Override
public void install() {
initializeTable();
table.setItems(dataSet);
loadTableDataSet();
}
@Override
public void initializeTable() {
table.setEditable(true);
// controller.permissionTable_descriptionColumn.setCellValueFactory(param -> param.getValue().getDescription());
// controller.permissionTable_descriptionColumn.setCellFactory(TextFieldTableCell.forTableColumn());
// controller.permissionTable_descriptionColumn.setOnEditCommit(this::onSignMethodTableDescriptionColumnEditCommitAction);
table.setItems(dataSet);
if (table.contextMenuProperty().get() == null) {
ContextMenu contextMenu = new ContextMenu();
createContextMenu(contextMenu);
table.setContextMenu(contextMenu);
}
}
protected void createContextMenu(ContextMenu contextMenu) {
MenuItem item1 = new MenuItem("新建");
item1.setOnAction(this::onTableCreateNewAction);
MenuItem item2 = new MenuItem("刷新");
item2.setOnAction(this::onTableRefreshAction);
MenuItem item3 = new MenuItem("删除");
item3.setOnAction(this::onTableDeleteAction);
contextMenu.getItems().addAll(item1, item2, item3);
}
protected void onTableCreateNewAction(ActionEvent event) {
Permission newRow = new Permission();
com.ecep.contract.model.Function function = controller.functionService.findById(viewModel.getId().get());
newRow.setFunction(function);
Permission saved = controller.permissionService.save(newRow);
PermissionViewModel row = PermissionViewModel.from(saved);
dataSet.add(row);
}
protected void onTableRefreshAction(ActionEvent event) {
loadTableDataSet();
}
protected void onTableDeleteAction(ActionEvent event) {
ObservableList<PermissionViewModel> selectedItems = table.getSelectionModel().getSelectedItems();
if (selectedItems.isEmpty()) {
return;
}
if (!UITools.showConfirmDialog("删除行", "确认删除选中的" + selectedItems.size() + "条数据?")) {
return;
}
for (PermissionViewModel selectedItem : new ArrayList<>(selectedItems)) {
if (deleteRow(selectedItem)) {
dataSet.remove(selectedItem);
}
}
}
protected boolean deleteRow(PermissionViewModel row) {
throw new UnsupportedOperationException("未实现");
}
private void onNameColumnEditCommitAction(TableColumn.CellEditEvent<PermissionViewModel, String> event) {
acceptSignMethodCellEditEvent(event, PermissionViewModel::getName);
}
private void onKeyColumnEditCommitAction(TableColumn.CellEditEvent<PermissionViewModel, String> event) {
acceptSignMethodCellEditEvent(event, PermissionViewModel::getKey);
}
protected List<PermissionViewModel> loadTableData() {
if (viewModel == null) {
return List.of();
}
return controller.permissionService.findByFunctionId(viewModel.getId().get()).stream().map(PermissionViewModel::from).toList();
}
public void loadTableDataSet() {
Platform.runLater(() -> {
dataSet.clear();
CompletableFuture.runAsync(() -> {
List<PermissionViewModel> models = loadTableData();
Platform.runLater(() -> {
try {
dataSet.setAll(models);
} catch (Exception e) {
handleException(e);
}
});
}).exceptionally(this::handleException);
});
}
@Override
public Specification<Permission> getSpecification() {
return controller.permissionService.getSpecification(null);
}
private <K> void acceptSignMethodCellEditEvent(TableColumn.CellEditEvent<PermissionViewModel, K> event, Function<PermissionViewModel, Property<K>> function) {
PermissionViewModel row = event.getRowValue();
Property<K> property = function.apply(row);
property.setValue(event.getNewValue());
try {
save(row);
} catch (Exception e) {
UITools.showExceptionAndWait("保存出错", e);
}
}
protected void save(PermissionViewModel row) {
int id = row.getId().get();
Permission type = controller.permissionService.findById(id);
if (row.copyTo(type)) {
Permission saved = controller.permissionService.save(type);
row.update(saved);
}
}
}

View File

@@ -0,0 +1,21 @@
package com.ecep.contract.controller.project;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.ds.project.service.ProjectService;
import com.ecep.contract.model.Project;
import com.ecep.contract.vm.ProjectViewModel;
public abstract class AbstProjectBasedTabSkin
extends AbstEntityBasedTabSkin<ProjectWindowController, Project, ProjectViewModel>
implements TabSkin {
public AbstProjectBasedTabSkin(ProjectWindowController controller) {
super(controller);
viewModel = controller.getViewModel();
}
public ProjectService getProjectService() {
return controller.projectService;
}
}

View File

@@ -0,0 +1,42 @@
package com.ecep.contract.controller.project;
import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.ds.project.service.ProjectService;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.model.Project;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vm.IdentityViewModel;
import com.ecep.contract.vm.ProjectBasedViewModel;
import com.ecep.contract.vm.ProjectViewModel;
public abstract class AbstProjectTableTabSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>>
extends AbstEntityTableTabSkin<ProjectWindowController, Project, ProjectViewModel, T, TV>
implements TabSkin {
public AbstProjectTableTabSkin(ProjectWindowController controller) {
super(controller);
}
public ProjectService getProjectService() {
return controller.projectService;
}
@Override
protected TV createNewViewModel() {
TV model = super.createNewViewModel();
if (model instanceof ProjectBasedViewModel m) {
m.getProject().set(getEntity());
}
return model;
}
@Override
public Specification<T> getSpecification(Project parent) {
return SpecificationUtils.and(getSpecification(), (root, query, builder) -> {
return builder.equal(root.get("project"), parent);
});
}
}

View File

@@ -0,0 +1,210 @@
package com.ecep.contract.controller.project;
import java.time.LocalDate;
import java.util.Objects;
import java.util.function.Consumer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.Desktop;
import com.ecep.contract.controller.BaseController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.converter.EntityStringConverter;
import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.ds.project.service.ProjectService;
import com.ecep.contract.ds.project.service.SaleTypeService;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.Project;
import com.ecep.contract.model.ProjectSaleType;
import com.ecep.contract.vm.ProjectViewModel;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import javafx.util.converter.NumberStringConverter;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/project/apply-new-project.fxml")
public class ApplyNewProjectWindowController extends BaseController {
@Autowired
private ProjectService projectService;
@Autowired
private SaleTypeService saleTypeService;
@Autowired
private EmployeeService employeeService;
public ComboBox<ProjectSaleType> saleTypeComboBox;
public TextField codeYearField;
public Label codeYearLabel;
public TextField codeSequenceNumberField;
public Label codeSequenceNumberLabel;
public Label codePreviewLabel;
public Label codeLabel;
public Label applicantLabel;
public Button cancelBtn;
public Button applyBtn;
private ProjectViewModel viewModel = new ProjectViewModel();
private ObjectProperty<Consumer<Project>> onApplied = new SimpleObjectProperty<>();
public void setOnApplied(Consumer<Project> onApplied) {
this.onApplied.set(onApplied);
}
@Override
public Stage show(FXMLLoader loader, Window owner, Modality modality) {
return super.show(loader, owner, Modality.APPLICATION_MODAL);
}
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
getTitle().set("申请新项目编号");
viewModel.getCodeYear().set(25);
initializeSaleTypeField(saleTypeComboBox);
NumberStringConverter stringConverter = new NumberStringConverter();
Bindings.bindBidirectional(codeYearField.textProperty(), viewModel.getCodeYear(), stringConverter);
Bindings.bindBidirectional(codeSequenceNumberField.textProperty(), viewModel.getCodeSequenceNumber(), stringConverter);
initializeApplicantLabel(applicantLabel);
initializeCodePreviewLabel(codePreviewLabel);
cancelBtn.setOnAction(event -> {
cancelBtn.getScene().getWindow().hide();
});
applyBtn.disableProperty().bind(viewModel.getSaleType().isNull());
applyBtn.setOnAction(event -> {
Project newProject = new Project();
newProject.setCodeYear(viewModel.getCodeYear().get());
int codeSequenceNumber = viewModel.getCodeSequenceNumber().get();
if (codeSequenceNumber == 0) {
codeSequenceNumberLabel.setText("项目序号不能为0");
codeSequenceNumberLabel.setStyle("-fx-text-fill: #ffa2a2");
return;
} else {
codeSequenceNumberLabel.setText("");
codeSequenceNumberLabel.setStyle("");
}
newProject.setCodeSequenceNumber(codeSequenceNumber);
newProject.setSaleType(viewModel.getSaleType().get());
Project exist = projectService.findBySaleTypeAndCodeYearAndCodeSequenceNumber(newProject.getSaleType(), newProject.getCodeYear(), newProject.getCodeSequenceNumber());
if (exist != null) {
codeLabel.setText("项目编号已存在");
codeLabel.setStyle("-fx-text-fill: #ffa2a2");
return;
} else {
codeLabel.setText("");
codeLabel.setStyle("");
}
// 暂时的项目编号
newProject.setCode(codePreviewLabel.getText());
newProject.setApplicant(viewModel.getApplicant().get());
newProject.setCreated(LocalDate.now());
Project saved = projectService.save(newProject);
Consumer<Project> consumer = onApplied.get();
if (consumer != null) {
consumer.accept(saved);
}
applyBtn.getScene().getWindow().hide();
});
}
private void initializeApplicantLabel(Label applicantLabel) {
Employee operator = employeeService.findById(Desktop.instance.getActiveEmployeeId());
viewModel.getApplicant().set(operator);
applicantLabel.textProperty().bind(Bindings.createObjectBinding(() -> {
Employee employee = viewModel.getApplicant().get();
if (employee == null) {
return "- 异常,未取得当前操作员信息 -";
}
return employee.getName();
}, viewModel.getApplicant()));
}
private void initializeCodePreviewLabel(Label codePreviewLabel) {
codePreviewLabel.textProperty().bind(Bindings.createObjectBinding(() -> {
StringBuilder sb = new StringBuilder();
ProjectSaleType saleType = viewModel.getSaleType().get();
if (saleType == null) {
return "";
}
sb.append(saleType.getCode());
int year = viewModel.getCodeYear().get();
if (year < 10) {
sb.append("0");
}
sb.append(year);
int sn = viewModel.getCodeSequenceNumber().get();
if (sn < 10) {
sb.append("00");
} else if (sn < 100) {
sb.append("0");
}
sb.append(sn);
return sb.toString();
}, viewModel.getSaleType(), viewModel.getCodeYear(), viewModel.getCodeSequenceNumber()));
}
private void initializeSaleTypeField(ComboBox<ProjectSaleType> field) {
ObservableList<ProjectSaleType> list = FXCollections.observableArrayList();
saleTypeService.findAll().stream().filter(ProjectSaleType::isActive).forEach(list::add);
field.setItems(list);
field.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
viewModel.getSaleType().set(newValue);
// 当 SequenceNumber 为 0 时, 自动检索最新的 SequenceNumber
if (viewModel.getCodeYear().get() != 0) {
int nextCodeSequenceNumber = projectService.getNextCodeSequenceNumber(newValue, viewModel.getCodeYear().get());
viewModel.getCodeSequenceNumber().set(nextCodeSequenceNumber);
}
});
EntityStringConverter<ProjectSaleType> converter = new EntityStringConverter<>();
converter.setInitialized(saleType -> {
return list.stream().filter(v -> v != null && Objects.equals(v.getId(), saleType.getId())).findFirst().orElse(null);
});
converter.setFromString(name -> {
return list.stream().filter(v -> v != null && Objects.equals(v.getName(), name)).findFirst().orElse(null);
});
field.setConverter(converter);
field.valueProperty().bindBidirectional(viewModel.getSaleType());
}
}

View File

@@ -0,0 +1,171 @@
package com.ecep.contract.controller.project;
import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.controller.BaseController;
import com.ecep.contract.controller.ComboBoxUtils;
import com.ecep.contract.controller.ManagerSkin;
import com.ecep.contract.converter.EntityStringConverter;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.ds.project.service.ProductTypeService;
import com.ecep.contract.ds.project.service.ProjectService;
import com.ecep.contract.ds.project.service.ProjectTypeService;
import com.ecep.contract.ds.project.service.SaleTypeService;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.ProductType;
import com.ecep.contract.model.Project;
import com.ecep.contract.model.ProjectSaleType;
import com.ecep.contract.model.ProjectType;
import com.ecep.contract.ui.table.cell.CompanyTableCell;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vm.ProjectViewModel;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.util.converter.CurrencyStringConverter;
import lombok.Setter;
public class ProjectManagerSkin
extends AbstEntityManagerSkin<Project, ProjectViewModel, ProjectManagerSkin, ProjectManagerWindowController>
implements ManagerSkin {
@Setter
private CompanyService companyService;
@Setter
private ProjectTypeService projectTypeService;
@Setter
private SaleTypeService saleTypeService;
@Setter
private ProductTypeService productTypeService;
public ProjectManagerSkin(ProjectManagerWindowController controller) {
super(controller);
}
public ProjectService getProjectService() {
return controller.getViewModelService();
}
public CompanyService getCompanyService(){
if (companyService == null) {
companyService = getBean(CompanyService.class);
}
return companyService;
}
public SaleTypeService getSaleTypeService() {
if (saleTypeService == null) {
saleTypeService = getBean(SaleTypeService.class);
}
return saleTypeService;
}
public ProjectTypeService getProjectTypeService() {
if (projectTypeService == null) {
projectTypeService = getBean(ProjectTypeService.class);
}
return projectTypeService;
}
public ProductTypeService getProductTypeService() {
if (productTypeService == null) {
productTypeService = getBean(ProductTypeService.class);
}
return productTypeService;
}
@Override
public Specification<Project> getSpecification() {
Specification<Project> spec = super.getSpecification();
if (controller.saleTypeSelector.getValue() != null) {
spec = SpecificationUtils.and(spec, (root, query, builder) -> {
return builder.equal(root.get("saleType"), controller.saleTypeSelector.getValue());
});
}
return spec;
}
@Override
public void initializeTable() {
ComboBoxUtils.initialComboBox(controller.saleTypeSelector, getSaleTypeService().findAll(), true);
controller.saleTypeSelector.valueProperty().addListener((observable, oldValue, newValue) -> {
loadTableDataSet(false);
});
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
controller.codeColumn.setCellValueFactory(param -> param.getValue().getCode());
controller.nameColumn.setCellValueFactory(param -> param.getValue().getName());
controller.amountColumn.setCellValueFactory(param -> param.getValue().getAmount());
controller.amountColumn.setCellFactory(TextFieldTableCell.forTableColumn(new CurrencyStringConverter(getLocale())));//, "#,##0"
initializeSaleTypeColumn(controller.saleTypeColumn);
initializeCustomerColumn(controller.customerColumn);
initializeProjectTypeColumn(controller.projectTypeColumn);
initializeProductTypeColumn(controller.productTypeColumn);
controller.useBidColumn.setCellValueFactory(param -> param.getValue().getUseBid());
controller.useBidColumn.setCellFactory(CheckBoxTableCell.forTableColumn(controller.useBidColumn));
controller.useOfferColumn.setCellValueFactory(param -> param.getValue().getUseOffer());
controller.useOfferColumn.setCellFactory(CheckBoxTableCell.forTableColumn(controller.useOfferColumn));
controller.createdColumn.setCellValueFactory(param -> param.getValue().getCreated());
Platform.runLater(() -> {
getTableView().getSortOrder().add(controller.idColumn);
});
}
private void initializeCustomerColumn(TableColumn<ProjectViewModel, Company> column) {
column.setCellValueFactory(param -> param.getValue().getCustomer());
column.setCellFactory(param-> new CompanyTableCell<>(getCompanyService()));
}
private void initializeProductTypeColumn(TableColumn<ProjectViewModel, String> column) {
EntityStringConverter<ProductType> converter = new EntityStringConverter<>();
converter.setInitialized(productType -> getProductTypeService().findById(productType.getId()));
converter.setFromString(name -> getProductTypeService().findByName(name));
column.setCellValueFactory(param -> param.getValue().getProductType().map(converter::toString));
}
private void initializeProjectTypeColumn(TableColumn<ProjectViewModel, String> column) {
EntityStringConverter<ProjectType> converter = new EntityStringConverter<>();
converter.setInitialized(projectType -> getProjectTypeService().findById(projectType.getId()));
converter.setFromString(name -> getProjectTypeService().findByName(name));
column.setCellValueFactory(param -> param.getValue().getProjectType().map(converter::toString));
}
private void initializeSaleTypeColumn(TableColumn<ProjectViewModel, String> column) {
EntityStringConverter<ProjectSaleType> converter = new EntityStringConverter<>();
converter.setInitialized(saleType -> getSaleTypeService().findById(saleType.getId()));
converter.setFromString(name -> getSaleTypeService().findByName(name));
column.setCellValueFactory(param -> param.getValue().getSaleType().map(converter::toString));
}
@Override
protected void onTableRowDoubleClickedAction(ProjectViewModel item) {
showInOwner(item);
}
@Override
protected void onTableCreateNewAction(ActionEvent event) {
BaseController.show(ApplyNewProjectWindowController.class, getTableView().getScene().getWindow(), controller -> {
controller.setOnApplied(project -> {
ProjectViewModel viewModel = ProjectViewModel.from(project);
dataSet.addFirst(viewModel);
showInOwner(viewModel);
});
});
}
private void showInOwner(ProjectViewModel model) {
showInOwner(ProjectWindowController.class, model);
}
}

View File

@@ -0,0 +1,107 @@
package com.ecep.contract.controller.project;
import java.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.controller.AbstManagerWindowController;
import com.ecep.contract.controller.BaseController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.project.industry.ProjectIndustryManagerWindowController;
import com.ecep.contract.controller.project.product_type.ProductTypeManagerWindowController;
import com.ecep.contract.controller.project.project_type.ProjectTypeManagerWindowController;
import com.ecep.contract.controller.project.sale_type.ProjectSaleTypeManagerWindowController;
import com.ecep.contract.controller.project.usage.ProductUsageManagerWindowController;
import com.ecep.contract.ds.project.service.ProjectService;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.Project;
import com.ecep.contract.model.ProjectSaleType;
import com.ecep.contract.vm.ProjectViewModel;
import javafx.event.ActionEvent;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TableColumn;
import javafx.stage.WindowEvent;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/project/project-manager.fxml")
public class ProjectManagerWindowController
extends AbstManagerWindowController<Project, ProjectViewModel, ProjectManagerSkin> {
private static final Logger logger = LoggerFactory.getLogger(ProjectManagerWindowController.class);
public ComboBox<ProjectSaleType> saleTypeSelector;
public TableColumn<ProjectViewModel, Number> idColumn;
public TableColumn<ProjectViewModel, String> codeColumn;
public TableColumn<ProjectViewModel, String> nameColumn;
public TableColumn<ProjectViewModel, String> saleTypeColumn;
public TableColumn<ProjectViewModel, String> projectTypeColumn;
public TableColumn<ProjectViewModel, String> productTypeColumn;
public TableColumn<ProjectViewModel, LocalDate> createdColumn;
public TableColumn<ProjectViewModel, Company> customerColumn;
public TableColumn<ProjectViewModel, Boolean> useBidColumn;
public TableColumn<ProjectViewModel, Boolean> useOfferColumn;
public TableColumn<ProjectViewModel, Number> amountColumn;
@Autowired
private ProjectService projectService;
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
getTitle().set("项目管理");
}
@Override
public ProjectService getViewModelService() {
return projectService;
}
@Override
protected ProjectManagerSkin createDefaultSkin() {
return new ProjectManagerSkin(this);
}
public void onReBuildFilesAction(ActionEvent event) {
}
public void onBaseTableRefreshAction(ActionEvent event) {
getSkin().loadTableDataSet(true);
}
public void onCreateNewProjectAction(ActionEvent event) {
getSkin().onTableCreateNewAction(event);
}
public void onOpenProjectTypeManagerAction(ActionEvent event) {
showInOwner(ProjectTypeManagerWindowController.class);
}
public void onOpenSaleTypeManagerAction(ActionEvent event) {
showInOwner(ProjectSaleTypeManagerWindowController.class);
}
public void onOpenUsedIndustryManagerAction(ActionEvent event) {
showInOwner(ProjectIndustryManagerWindowController.class);
}
public void onOpenProductTypeManagerAction(ActionEvent event) {
showInOwner(ProductTypeManagerWindowController.class);
}
public void onOpenProductUsageManagerAction(ActionEvent event) {
showInOwner(ProductUsageManagerWindowController.class);
}
private <T extends BaseController> void showInOwner(Class<T> clz) {
show(clz, table.getScene().getWindow());
}
}

View File

@@ -0,0 +1,417 @@
package com.ecep.contract.controller.project;
import java.util.Objects;
import java.util.stream.Stream;
import org.springframework.util.StringUtils;
import com.ecep.contract.UITools;
import com.ecep.contract.controller.ComboBoxUtils;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.converter.EmployeeStringConverter;
import com.ecep.contract.converter.EntityStringConverter;
import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.ds.project.service.DeliverySignMethodService;
import com.ecep.contract.ds.project.service.ProductTypeService;
import com.ecep.contract.ds.project.service.ProductUsageService;
import com.ecep.contract.ds.project.service.ProjectCostService;
import com.ecep.contract.ds.project.service.ProjectIndustryService;
import com.ecep.contract.ds.project.service.ProjectTypeService;
import com.ecep.contract.ds.project.service.SaleTypeService;
import com.ecep.contract.model.DeliverySignMethod;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.ProductType;
import com.ecep.contract.model.ProductUsage;
import com.ecep.contract.model.Project;
import com.ecep.contract.model.ProjectCost;
import com.ecep.contract.model.ProjectIndustry;
import com.ecep.contract.model.ProjectSaleType;
import com.ecep.contract.model.ProjectType;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.Tab;
import javafx.scene.control.TextField;
import javafx.util.converter.LocalDateStringConverter;
import javafx.util.converter.NumberStringConverter;
import lombok.Setter;
/**
* 基础信息
*/
public class ProjectTabSkinBase extends AbstProjectBasedTabSkin implements TabSkin {
@Setter
EmployeeStringConverter employeeStringConverter;
@Setter
LocalDateStringConverter localDateStringConverter;
@Setter
EmployeeService employeeService;
@Setter
private ProjectTypeService projectTypeService;
@Setter
private ProductTypeService productTypeService;
@Setter
private SaleTypeService saleTypeService;
@Setter
private ProductUsageService productUsageService;
@Setter
private ProjectIndustryService projectIndustryService;
@Setter
private DeliverySignMethodService deliverySignMethodService;
public ProjectTabSkinBase(ProjectWindowController controller) {
super(controller);
}
@Override
public Tab getTab() {
return controller.baseInfoTab;
}
private EmployeeService getEmployeeService() {
if (employeeService == null) {
employeeService = getBean(EmployeeService.class);
}
return employeeService;
}
private ProjectTypeService getProjectTypeService() {
if (projectTypeService == null) {
projectTypeService = getBean(ProjectTypeService.class);
}
return projectTypeService;
}
private ProductTypeService getProductTypeService() {
if (productTypeService == null) {
productTypeService = getBean(ProductTypeService.class);
}
return productTypeService;
}
private SaleTypeService getSaleTypeService() {
if (saleTypeService == null) {
saleTypeService = getBean(SaleTypeService.class);
}
return saleTypeService;
}
private ProductUsageService getProductUsageService() {
if (productUsageService == null) {
productUsageService = getBean(ProductUsageService.class);
}
return productUsageService;
}
private ProjectIndustryService getProjectIndustryService() {
if (projectIndustryService == null) {
projectIndustryService = getBean(ProjectIndustryService.class);
}
return projectIndustryService;
}
private DeliverySignMethodService getDeliverySignMethodService() {
if (deliverySignMethodService == null) {
deliverySignMethodService = getBean(DeliverySignMethodService.class);
}
return deliverySignMethodService;
}
private EmployeeStringConverter getEmployeeStringConverter() {
if (employeeStringConverter == null) {
employeeStringConverter = getBean(EmployeeStringConverter.class);
}
return employeeStringConverter;
}
@Override
public void initializeTab() {
controller.deleteBtn.setOnAction(this::onDeleteAction);
controller.nameField.textProperty().bindBidirectional(viewModel.getName());
controller.codeField.textProperty().bind(viewModel.getCode());
NumberStringConverter stringConverter = new NumberStringConverter();
Bindings.bindBidirectional(controller.codeYearField.textProperty(), viewModel.getCodeYear(), stringConverter);
Bindings.bindBidirectional(controller.codeSequenceNumberField.textProperty(), viewModel.getCodeSequenceNumber(), stringConverter);
controller.addressField.textProperty().bindBidirectional(viewModel.getAddress());
controller.useBidField.selectedProperty().bindBidirectional(viewModel.getUseBid());
controller.useOfferField.selectedProperty().bindBidirectional(viewModel.getUseOffer());
controller.standardPayWayField.selectedProperty().bindBidirectional(viewModel.getStandardPayWay());
Bindings.bindBidirectional(controller.amountField.textProperty(), viewModel.getAmount(), new NumberStringConverter());
initializeSaleTypeField(controller.saleTypeField);
initializeSaleIndustryField(controller.industryField);
initializeProjectTypeField(controller.projectTypeField);
initializeProductTypeField(controller.productTypeField);
initializeDeliverySignMethodField(controller.saleTypeField, controller.deliverySignMethodField);
initializeProductUsageField(controller.productUsageField);
controller.descriptionField.textProperty().bindBidirectional(viewModel.getDescription());
employeeAutoCompletion(controller.applicantField, viewModel.getApplicant());
employeeAutoCompletion(controller.authorizerField, viewModel.getAuthorizer());
controller.createdField.setConverter(localDateStringConverter);
controller.createdField.valueProperty().bindBidirectional(viewModel.getCreated());
controller.plannedStartTimeField.setConverter(localDateStringConverter);
controller.plannedStartTimeField.valueProperty().bindBidirectional(viewModel.getPlannedStartTime());
controller.plannedCompletionTimeField.setConverter(localDateStringConverter);
controller.plannedCompletionTimeField.valueProperty().bindBidirectional(viewModel.getPlannedCompletionTime());
controller.versionLabel.textProperty().bind(viewModel.getVersion().asString());
}
private void onDeleteAction(ActionEvent event) {
ProjectCostService projectCostService = getBean(ProjectCostService.class);
Project project = getEntity();
for (ProjectCost cost : projectCostService.findAllByProject(project)) {
projectCostService.delete(cost);
}
getProjectService().delete(project);
controller.root.getScene().getWindow().hide();
}
private void initializeSaleIndustryField(ComboBox<ProjectIndustry> field) {
ComboBoxUtils.initialComboBox(field, viewModel.getIndustry(), getProjectIndustryService().findAll(), true);
}
private void initializeProjectTypeField(ComboBox<ProjectType> field) {
ComboBoxUtils.initialComboBox(field, viewModel.getProjectType(), getProjectTypeService().findAll(), true);
}
private void initializeProductTypeField(ComboBox<ProductType> field) {
ObservableList<ProductType> list = FXCollections.observableArrayList();
list.add(null);
list.addAll(getProductTypeService().findAll());
field.setItems(list);
field.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
viewModel.getProductType().set(newValue);
});
EntityStringConverter<ProductType> converter = new EntityStringConverter<>();
converter.setInitialized(type -> {
return list.stream().filter(v -> v != null && Objects.equals(v.getId(), type.getId())).findFirst().orElse(null);
});
converter.setFromString(name -> {
return list.stream().filter(v -> v != null && Objects.equals(v.getName(), name)).findFirst().orElse(null);
});
field.setConverter(converter);
field.valueProperty().bindBidirectional(viewModel.getProductType());
}
private void initializeDeliverySignMethodField(
ComboBox<ProjectSaleType> saleTypeField,
ComboBox<DeliverySignMethod> field
) {
ObservableList<DeliverySignMethod> list = FXCollections.observableArrayList();
list.add(null);
list.addAll(getDeliverySignMethodService().findAll());
field.setItems(list);
ProjectSaleType saleType = saleTypeField.getValue();
if (saleType != null) {
field.setItems(list.filtered(p -> {
return p == null || Objects.equals(p.getSaleType(), saleType);
}));
}
saleTypeField.valueProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == null) {
field.setItems(list);
} else {
field.setItems(list.filtered(p -> {
return p == null || Objects.equals(p.getSaleType(), newValue);
}));
}
});
field.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
viewModel.getDeliverySignMethod().set(newValue);
});
EntityStringConverter<DeliverySignMethod> converter = new EntityStringConverter<>();
converter.setInitialized(signMethod -> {
return list.stream().filter(v -> v != null && Objects.equals(v.getId(), signMethod.getId())).findFirst().orElse(null);
});
converter.setFromString(name -> {
return list.stream().filter(v -> v != null && Objects.equals(v.getName(), name)).findFirst().orElse(null);
});
field.setConverter(converter);
field.valueProperty().bindBidirectional(viewModel.getDeliverySignMethod());
}
private void initializeProductUsageField(ComboBox<ProductUsage> field) {
ObservableList<ProductUsage> list = FXCollections.observableArrayList();
list.add(null);
list.addAll(getProductUsageService().findAll());
field.setItems(list);
field.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
viewModel.getProductUsage().set(newValue);
});
EntityStringConverter<ProductUsage> converter = new EntityStringConverter<>();
converter.setInitialized(usage -> {
return list.stream().filter(v -> v != null && Objects.equals(v.getId(), usage.getId())).findFirst().orElse(null);
});
converter.setFromString(name -> {
return list.stream().filter(v -> v != null && Objects.equals(v.getName(), name)).findFirst().orElse(null);
});
field.setConverter(converter);
field.valueProperty().bindBidirectional(viewModel.getProductUsage());
}
private void initializeSaleTypeField(ComboBox<ProjectSaleType> field) {
ObservableList<ProjectSaleType> list = FXCollections.observableArrayList();
list.add(null);
list.addAll(getSaleTypeService().findAll());
field.setItems(list);
field.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
viewModel.getSaleType().set(newValue);
// 当 SequenceNumber 为 0 时, 自动检索最新的 SequenceNumber
if (viewModel.getCodeSequenceNumber().get() == 0 && viewModel.getCodeYear().get() != 0) {
int nextCodeSequenceNumber = getProjectService().getNextCodeSequenceNumber(newValue, viewModel.getCodeYear().get());
viewModel.getCodeSequenceNumber().set(nextCodeSequenceNumber);
}
});
field.setCellFactory(param -> new ListCell<>() {
@Override
protected void updateItem(ProjectSaleType item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText("-");
setStyle("");
} else {
setText(item.getName());
if (item.isActive()) {
this.setStyle("");
} else {
this.setStyle("-fx-text-fill: grey;");
}
}
}
});
EntityStringConverter<ProjectSaleType> converter = new EntityStringConverter<>();
converter.setInitialized(saleType -> {
return list.stream().filter(v -> v != null && Objects.equals(v.getId(), saleType.getId())).findFirst().orElse(null);
});
converter.setFromString(name -> {
return list.stream().filter(v -> v != null && Objects.equals(v.getName(), name)).findFirst().orElse(null);
});
field.setConverter(converter);
field.valueProperty().bindBidirectional(viewModel.getSaleType());
}
private void employeeAutoCompletion(TextField textField, SimpleObjectProperty<Employee> property) {
UITools.autoCompletion(textField, property, getEmployeeStringConverter());
}
public void save() {
makeProjectCode();
super.save();
}
private void makeProjectCode() {
StringBuilder sb = new StringBuilder();
ProjectSaleType saleType = controller.saleTypeField.getValue();
if (saleType == null) {
sb.append("_");
} else {
controller.saleTypeField.getItems().stream()
.filter(item -> item != null && item.getId().equals(saleType.getId()))
.findFirst()
.ifPresent(item -> sb.append(item.getCode()));
}
int year = 0;
{
String text = controller.codeYearField.getText();
if (StringUtils.hasText(text)) {
year = Integer.parseInt(text);
}
}
if (year < 10) {
sb.append("0");
}
sb.append(year);
int sn = 0;
{
String text = controller.codeSequenceNumberField.getText();
if (StringUtils.hasText(text)) {
sn = Integer.parseInt(text);
}
}
if (sn < 10) {
sb.append("00");
} else if (sn < 100) {
sb.append("0");
}
sb.append(sn);
DeliverySignMethod signMethod = controller.deliverySignMethodField.getValue();
ProductType productType = controller.productTypeField.getValue();
ProjectType projectType = controller.projectTypeField.getValue();
ProjectIndustry industry = controller.industryField.getValue();
ProductUsage productUsage = controller.productUsageField.getValue();
if (!Stream.of(signMethod, productType, projectType, industry, productUsage)
.allMatch(Objects::isNull)) {
if (signMethod == null) {
sb.append("_");
} else {
controller.deliverySignMethodField.getItems().stream()
.filter(item -> item != null && item.getId().equals(signMethod.getId()))
.findFirst()
.ifPresent(item -> sb.append(item.getCode()));
}
if (productType == null) {
sb.append("_");
} else {
controller.productTypeField.getItems().stream()
.filter(item -> item != null && item.getId().equals(productType.getId()))
.findFirst()
.ifPresent(item -> sb.append(item.getCode()));
}
if (projectType == null) {
sb.append("_");
} else {
controller.projectTypeField.getItems().stream()
.filter(item -> item != null && item.getId().equals(projectType.getId()))
.findFirst()
.ifPresent(item -> sb.append(item.getCode()));
}
if (industry != null) {
controller.industryField.getItems().stream()
.filter(item -> item != null && item.getId().equals(industry.getId()))
.findFirst()
.ifPresent(item -> sb.append(item.getCode()));
} else {
sb.append("_");
}
if (productUsage != null) {
controller.productUsageField.getItems().stream()
.filter(item -> item != null && item.getId().equals(productUsage.getId()))
.findFirst()
.ifPresent(item -> sb.append(item.getCode()));
}
}
viewModel.getCode().set(sb.toString());
}
}

View File

@@ -0,0 +1,223 @@
package com.ecep.contract.controller.project;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.project.bid.ProjectBidWindowController;
import com.ecep.contract.controller.tab.RefreshableSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
import com.ecep.contract.converter.EmployeeStringConverter;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.ds.customer.service.CompanyCustomerFileService;
import com.ecep.contract.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.ds.project.service.ProjectBidService;
import com.ecep.contract.model.CompanyCustomerEvaluationFormFile;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.Project;
import com.ecep.contract.model.ProjectBid;
import com.ecep.contract.service.CompanyStringConverter;
import com.ecep.contract.ui.table.cell.EmployeeTableCell;
import com.ecep.contract.vm.ProjectBidViewModel;
import javafx.scene.control.Tab;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.util.converter.LocalDateStringConverter;
import lombok.Setter;
@FxmlPath("/ui/project/project-tab-bid.fxml")
public class ProjectTabSkinBid
extends AbstProjectTableTabSkin<ProjectBid, ProjectBidViewModel>
implements TabSkin, EditableEntityTableTabSkin<ProjectBid, ProjectBidViewModel>, RefreshableSkin {
public TableColumn<ProjectBidViewModel, Number> idColumn;
public TableColumn<ProjectBidViewModel, Number> versionColumn;
public TableColumn<ProjectBidViewModel, CompanyCustomerEvaluationFormFile> evaluationFileColumn;
public TableColumn<ProjectBidViewModel, String> descriptionColumn;
public TableColumn<ProjectBidViewModel, Employee> applicantColumn;
public TableColumn<ProjectBidViewModel, LocalDateTime> applyTimeColumn;
public TableColumn<ProjectBidViewModel, Employee> authorizerColumn;
public TableColumn<ProjectBidViewModel, LocalDateTime> authorizationTimeColumn;
public TableColumn<ProjectBidViewModel, Number> levelColumn;
public TableColumn<ProjectBidViewModel, String> standardPayWayColumn;
public TableColumn<ProjectBidViewModel, Number> amountColumn;
@Setter
private ProjectBidService projectBidService;
@Setter
private LocalDateStringConverter localDateStringConverter;
@Setter
private EmployeeStringConverter employeeStringConverter;
@Setter
private CompanyStringConverter companyStringConverter;
@Setter
private CompanyService companyService;
@Setter
private CompanyCustomerService customerService;
@Setter
private CompanyCustomerFileService customerFileService;
public ProjectTabSkinBid(ProjectWindowController controller) {
super(controller);
}
@Override
protected ProjectBidService getViewModelService() {
return getProjectBidService();
}
@Override
public Tab getTab() {
return controller.bidTab;
}
@Override
public void initializeTable() {
super.initializeTable();
bindNumberColumn(idColumn, ProjectBidViewModel::getId);
// levelColumn.setCellValueFactory(param -> param.getValue().getLevel());
// levelColumn.setCellFactory(param -> new LevelTableCell());
// standardPayWayColumn.setCellValueFactory(param -> param.getValue().getStandardPayWay().map(value -> value == null ? "" : (value ? "标准" : "非标准")));
// evaluationFileColumn.setCellValueFactory(param -> param.getValue().getEvaluationFile());
// evaluationFileColumn.setCellFactory(param -> new EvaluationFileTableCell());
// amountColumn.setCellValueFactory(param -> param.getValue().getAmount());
// amountColumn.setCellFactory(TextFieldTableCell.forTableColumn(new CurrencyStringConverter(getLocale())));
applicantColumn.setCellValueFactory(param -> param.getValue().getApplicant());
applicantColumn.setCellFactory(cell -> new EmployeeTableCell<>(getEmployeeService()));
bindLocalDateTimeColumn(applyTimeColumn, ProjectBidViewModel::getApplyTime);
authorizerColumn.setCellValueFactory(param -> param.getValue().getAuthorizer());
authorizerColumn.setCellFactory(cell -> new EmployeeTableCell<>(getEmployeeService()));
bindLocalDateTimeColumn(authorizationTimeColumn, ProjectBidViewModel::getAuthorizationTime);
bindColumn(descriptionColumn, ProjectBidViewModel::getDescription);
}
@Override
protected ProjectBidViewModel createNewViewModel() {
ProjectBidViewModel model = new ProjectBidViewModel();
Project project = getParent();
model.getProject().set(project);
model.getApplicant().set(project.getApplicant());
if (project.getAmount() != null) {
model.getAmount().set(project.getAmount());
}
LocalDate created = project.getCreated();
if (created == null) {
created = LocalDate.now();
}
model.getApplyTime().set(LocalDateTime.of(created, LocalTime.now()));
saveRow(model);
return model;
}
@Override
protected void onTableRowDoubleClickedAction(ProjectBidViewModel item) {
showInOwner(ProjectBidWindowController.class, item);
}
private ProjectBidService getProjectBidService() {
if (projectBidService == null) {
projectBidService = getBean(ProjectBidService.class);
}
return projectBidService;
}
private LocalDateStringConverter getLocalDateStringConverter() {
if (localDateStringConverter == null) {
localDateStringConverter = new LocalDateStringConverter(DateTimeFormatter.ISO_LOCAL_DATE, null);
}
return localDateStringConverter;
}
private EmployeeStringConverter getEmployeeStringConverter() {
if (employeeStringConverter == null) {
employeeStringConverter = getBean(EmployeeStringConverter.class);
}
return employeeStringConverter;
}
public CompanyService getCompanyService() {
if (companyService == null) {
companyService = getBean(CompanyService.class);
}
return companyService;
}
private CompanyStringConverter getCompanyStringConverter() {
if (companyStringConverter == null) {
companyStringConverter = getBean(CompanyStringConverter.class);
}
return companyStringConverter;
}
private CompanyCustomerService getCompanyCustomerService() {
if (customerService == null) {
customerService = getBean(CompanyCustomerService.class);
}
return customerService;
}
private CompanyCustomerFileService getCompanyCustomerFileService() {
if (customerFileService == null) {
customerFileService = getBean(CompanyCustomerFileService.class);
}
return customerFileService;
}
@Override
public void deleteRowData(ProjectBid entity) {
getProjectBidService().delete(entity);
}
@Override
public ProjectBid loadRowData(ProjectBidViewModel row) {
return getProjectBidService().findById(row.getId().get());
}
@Override
public ProjectBid saveRowData(ProjectBid entity) {
return getProjectBidService().save(entity);
}
private static class LevelTableCell extends TableCell<ProjectBidViewModel, Number> {
@Override
protected void updateItem(Number item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText("");
return;
}
// 较差,一般,良好
int value = item.intValue();
if (value < 0) {
setText("-");
return;
}
if (value < 1) {
setText("较差");
return;
}
if (value < 2) {
setText("一般");
return;
}
if (value < 3) {
setText("良好");
return;
}
setText(String.valueOf(value));
}
}
}

View File

@@ -0,0 +1,72 @@
package com.ecep.contract.controller.project;
import java.time.LocalDate;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.project.ContractWindowController;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.model.Contract;
import com.ecep.contract.vm.ContractViewModel;
import javafx.application.Platform;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
@FxmlPath("/ui/project/project-tab-contract.fxml")
public class ProjectTabSkinContract
extends AbstProjectTableTabSkin<Contract, ContractViewModel>
implements TabSkin {
public TableColumn<ContractViewModel, Number> idColumn;
public TableColumn<ContractViewModel, String> codeColumn;
public TableColumn<ContractViewModel, String> nameColumn;
public TableColumn<ContractViewModel, LocalDate> setupDateColumn;
public TableColumn<ContractViewModel, LocalDate> inureDateColumn;
public TableColumn<ContractViewModel, LocalDate> orderDateColumn;
public TableColumn<ContractViewModel, LocalDate> varyDateColumn;
public MenuItem contractTable_menu_refresh;
public ProjectTabSkinContract(ProjectWindowController controller) {
super(controller);
}
@Override
protected ContractService getViewModelService() {
return getContractService();
}
@Override
public Tab getTab() {
return controller.contractTab;
}
@Override
public void initializeTable() {
super.initializeTable();
bindNumberColumn(idColumn, ContractViewModel::getId);
codeColumn.setCellValueFactory(param -> param.getValue().getCode());
nameColumn.setCellValueFactory(param -> param.getValue().getName());
bindLocalDateColumn(setupDateColumn, ContractViewModel::getSetupDate);
bindLocalDateColumn(inureDateColumn, ContractViewModel::getInureDate);
bindLocalDateColumn(orderDateColumn, ContractViewModel::getOrderDate);
bindLocalDateColumn(varyDateColumn, ContractViewModel::getVaryDate);
Platform.runLater(() -> {
getTableView().getSortOrder().add(setupDateColumn);
});
contractTable_menu_refresh.setOnAction(this::onTableRefreshAction);
}
@Override
protected void onTableRowDoubleClickedAction(ContractViewModel item) {
ContractWindowController.show(item, controller.root.getScene().getWindow());
}
private ContractService getContractService() {
return controller.contractService;
}
}

View File

@@ -0,0 +1,194 @@
package com.ecep.contract.controller.project;
import java.text.NumberFormat;
import java.time.LocalDateTime;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.project.cost.ProjectCostWindowController;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.ds.contract.service.ContractItemService;
import com.ecep.contract.ds.project.service.ProjectCostItemService;
import com.ecep.contract.ds.project.service.ProjectCostService;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.Project;
import com.ecep.contract.model.ProjectCost;
import com.ecep.contract.model.ProjectCostItem;
import com.ecep.contract.ui.table.cell.EmployeeTableCell;
import com.ecep.contract.ui.table.cell.LocalDateTimeTableCell;
import com.ecep.contract.vm.ProjectCostItemViewModel;
import com.ecep.contract.vm.ProjectCostViewModel;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.util.converter.CurrencyStringConverter;
import javafx.util.converter.NumberStringConverter;
import lombok.Setter;
@FxmlPath("/ui/project/project-tab-cost.fxml")
public class ProjectTabSkinCost
extends AbstProjectTableTabSkin<ProjectCost, ProjectCostViewModel>
implements TabSkin {
public TableColumn<ProjectCostViewModel, Number> idColumn;
public TableColumn<ProjectCostViewModel, Employee> applicantColumn;
public TableColumn<ProjectCostViewModel, LocalDateTime> applyTimeColumn;
public TableColumn<ProjectCostViewModel, Employee> authorizerColumn;
public TableColumn<ProjectCostViewModel, LocalDateTime> authorizationTimeColumn;
public TableColumn<ProjectCostViewModel, Number> grossProfitMarginColumn;
public TableColumn<ProjectCostViewModel, Number> inTaxAmountColumn;
public TableColumn<ProjectCostViewModel, Number> inExclusiveTaxAmountColumn;
public TableColumn<ProjectCostViewModel, Number> outTaxAmountColumn;
public TableColumn<ProjectCostViewModel, Number> outExclusiveTaxAmountColumn;
public TableColumn<ProjectCostViewModel, Number> versionColumn;
public TableColumn<ProjectCostViewModel, String> descriptionColumn;
@Setter
private ProjectCostService costService;
@Setter
private ProjectCostItemService costItemService;
@Setter
private ContractItemService contractItemService;
public ProjectTabSkinCost(ProjectWindowController controller) {
super(controller);
}
@Override
protected ProjectCostService getViewModelService() {
return getCostService();
}
private ProjectCostService getCostService() {
if (costService == null) {
costService = getBean(ProjectCostService.class);
}
return costService;
}
private ProjectCostItemService getCostItemService() {
if (costItemService == null) {
costItemService = getBean(ProjectCostItemService.class);
}
return costItemService;
}
private ContractItemService getContractItemService() {
if (contractItemService == null) {
contractItemService = getBean(ContractItemService.class);
}
return contractItemService;
}
@Override
public Tab getTab() {
return controller.costTab;
}
@Override
public void initializeTable() {
super.initializeTable();
idColumn.setCellValueFactory(param -> param.getValue().getId());
applicantColumn.setCellValueFactory(param -> param.getValue().getApplicant());
applicantColumn.setCellFactory(cell -> new EmployeeTableCell<>());
applyTimeColumn.setCellValueFactory(param -> param.getValue().getApplyTime());
applyTimeColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
authorizerColumn.setCellValueFactory(param -> param.getValue().getAuthorizer());
authorizerColumn.setCellFactory(cell -> new EmployeeTableCell<>());
authorizationTimeColumn.setCellValueFactory(param -> param.getValue().getAuthorizationTime());
authorizationTimeColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
inTaxAmountColumn.setCellValueFactory(param -> param.getValue().getInTaxAmount());
inTaxAmountColumn.setCellFactory(TextFieldTableCell.forTableColumn(new CurrencyStringConverter(getLocale())));
inExclusiveTaxAmountColumn.setCellValueFactory(param -> param.getValue().getInExclusiveTaxAmount());
inExclusiveTaxAmountColumn.setCellFactory(TextFieldTableCell.forTableColumn(new CurrencyStringConverter(getLocale())));
outTaxAmountColumn.setCellValueFactory(param -> param.getValue().getOutTaxAmount());
outTaxAmountColumn.setCellFactory(TextFieldTableCell.forTableColumn(new CurrencyStringConverter(getLocale())));
outExclusiveTaxAmountColumn.setCellValueFactory(param -> param.getValue().getOutExclusiveTaxAmount());
outExclusiveTaxAmountColumn.setCellFactory(TextFieldTableCell.forTableColumn(new CurrencyStringConverter(getLocale())));
grossProfitMarginColumn.setCellValueFactory(param -> param.getValue().getGrossProfitMargin());
NumberFormat numberInstance = NumberFormat.getNumberInstance(getLocale());
numberInstance.setMaximumFractionDigits(2);
numberInstance.setMinimumFractionDigits(2);
grossProfitMarginColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter(numberInstance)));
versionColumn.setCellValueFactory(param -> param.getValue().getVersion());
descriptionColumn.setCellValueFactory(param -> param.getValue().getDescription());
Platform.runLater(() -> {
table.getSortOrder().add(versionColumn);
});
}
@Override
protected ProjectCostViewModel createNewViewModel() {
ProjectCostViewModel model = new ProjectCostViewModel();
Project project = getParent();
model.getProject().set(project);
ProjectCost latest = getCostService().findLatestByProject(project);
if (latest != null) {
model.getVersion().set(latest.getVersion() + 1);
} else {
model.getVersion().set(1);
}
model.getStampTax().set(0.03f);
model.getTaxAndSurcharges().set(11);
model.getApplicant().set(project.getApplicant());
model.getApplyTime().set(LocalDateTime.now());
saveRow(model);
return model;
}
@Override
protected void onTableRowDoubleClickedAction(ProjectCostViewModel item) {
showInOwner(ProjectCostWindowController.class, item);
}
@Override
protected void createContextMenu(ContextMenu contextMenu) {
super.createContextMenu(contextMenu);
MenuItem menuItem = new MenuItem("复制");
menuItem.setOnAction(this::onTableCopyAction);
menuItem.visibleProperty().bind(table.getSelectionModel().selectedItemProperty().isNotNull());
contextMenu.getItems().add(menuItem);
}
private void onTableCopyAction(ActionEvent event) {
ProjectCostViewModel selectedItem = table.getSelectionModel().getSelectedItem();
ProjectCost selectedCost = loadRowData(selectedItem);
ProjectCost projectCost = new ProjectCost();
selectedItem.copyTo(projectCost);
projectCost.setId(null);
projectCost.setVersion(selectedItem.getVersion().get() + 1);
projectCost.setApplyTime(LocalDateTime.now());
projectCost = saveRowData(projectCost);
ProjectCostItemService costItemService = getCostItemService();
for (ProjectCostItem selectedCostItem : costItemService.findByCost(selectedCost)) {
ProjectCostItem item = new ProjectCostItem();
ProjectCostItemViewModel.from(selectedCostItem).copyTo(item);
item.setId(null);
item.setCost(projectCost);
costItemService.save(item);
}
ProjectCostViewModel model = new ProjectCostViewModel();
model.update(projectCost);
dataSet.add(model);
saveRow(model);
}
}

View File

@@ -0,0 +1,344 @@
package com.ecep.contract.controller.project;
import java.util.List;
import java.util.function.BiFunction;
import org.controlsfx.control.textfield.AutoCompletionBinding;
import org.hibernate.Hibernate;
import org.springframework.util.StringUtils;
import com.ecep.contract.SpringApp;
import com.ecep.contract.UITools;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.company.CompanyWindowController;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.converter.EntityStringConverter;
import com.ecep.contract.ds.company.service.CompanyBankAccountService;
import com.ecep.contract.ds.company.service.CompanyContactService;
import com.ecep.contract.ds.company.service.CompanyInvoiceInfoService;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.ds.other.service.BankService;
import com.ecep.contract.model.Bank;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyBankAccount;
import com.ecep.contract.model.CompanyContact;
import com.ecep.contract.model.CompanyInvoiceInfo;
import com.ecep.contract.service.CompanyStringConverter;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TextField;
import javafx.util.StringConverter;
@FxmlPath("/ui/project/project-tab-customer.fxml")
public class ProjectTabSkinCustomerInfo
extends AbstProjectBasedTabSkin
implements TabSkin {
@FXML
public TextField companyField;
@FXML
public Button companyDetailBtn;
@FXML
public Label companyLabel;
@FXML
public TextField bankAccountField;
@FXML
public Label bankAccountLabel;
@FXML
public TextField invoiceInfoField;
@FXML
public Label invoiceInfoLabel;
@FXML
public TextField mainContactField;
@FXML
public Label mainContactLabel;
@FXML
public TextField subContactField;
@FXML
public Label subContactLabel;
private CompanyService companyService;
private BankService bankService;
private CompanyInvoiceInfoService invoiceInfoService;
private CompanyBankAccountService bankAccountService;
private CompanyContactService contactService;
public ProjectTabSkinCustomerInfo(ProjectWindowController controller) {
super(controller);
}
@Override
public Tab getTab() {
return controller.companyTab;
}
@Override
public void initializeTab() {
initCompanyField();
initBankAccountField();
initInvoiceInfoField();
initMainContactField();
initSubContactField();
}
private void initCompanyField() {
companyAutoCompletion(companyField, companyLabel, viewModel.getCustomer()).setOnAutoCompleted(event -> {
Company company = event.getCompletion();
viewModel.getCustomer().set(company);
//
// updateAbsent(company.getUniscid(), companyTaxCodeField.textProperty());
// updateAbsent(company.getTelephone(), companyTelField.textProperty());
//
// updateAbsent(
// // 优先使用通讯地址
// StringUtils.hasText(company.getAddress()) ? company.getAddress() : company.getRegAddr(),
// companyAddressField.textProperty());
// viewModel.getInvoiceInfo();
});
companyDetailBtn.setOnAction(event -> {
Company company = viewModel.getCustomer().get();
if (company == null) {
return;
}
CompanyWindowController.show(company, controller.root.getScene().getWindow());
});
}
private void initBankAccountField() {
bankAccountAutoCompletion(bankAccountField, bankAccountLabel, viewModel.getBankAccount()).setOnAutoCompleted(event -> {
CompanyBankAccount account = event.getCompletion();
viewModel.getBankAccount().set(account);
});
}
private void initInvoiceInfoField() {
invoiceInfoAutoCompletion(invoiceInfoField, invoiceInfoLabel, viewModel.getInvoiceInfo()).setOnAutoCompleted(event -> {
CompanyInvoiceInfo invoiceInfo = event.getCompletion();
viewModel.getInvoiceInfo().set(invoiceInfo);
});
}
private void initMainContactField() {
contactAutoCompletion(mainContactField, mainContactLabel, viewModel.getMainContact()).setOnAutoCompleted(event -> {
CompanyContact contact = event.getCompletion();
viewModel.getMainContact().set(contact);
});
}
private void initSubContactField() {
contactAutoCompletion(subContactField, subContactLabel, viewModel.getSubContact()).setOnAutoCompleted(event -> {
CompanyContact contact = event.getCompletion();
viewModel.getSubContact().set(contact);
});
}
/**
* 更新 property 未为空时
*
* @param value 要设置的值
* @param property 要设置的 property
*/
private void updateAbsent(String value, StringProperty property) {
if (!StringUtils.hasText(value)) {
return;
}
if (StringUtils.hasText(property.get())) {
return;
}
property.set(value);
}
private AutoCompletionBinding<Company> companyAutoCompletion(TextField textField, Label label, SimpleObjectProperty<Company> property) {
CompanyStringConverter converter = SpringApp.getBean(CompanyStringConverter.class);
label.textProperty().bind(property.map(company -> {
if (company == null) {
return "未选择";
}
if (!Hibernate.isInitialized(company)) {
company = getCompanyService().findById(company.getId());
property.set(company);
}
StringBuilder sb = new StringBuilder();
sb.append("经营状态:");
sb.append(company.getEntStatus());
sb.append("\n");
sb.append("统一社会信用代码:");
sb.append(company.getUniscid());
sb.append("\n");
sb.append("注册电话:");
sb.append(company.getTelephone());
sb.append("\n");
sb.append("注册地址:");
sb.append(company.getRegAddr());
return sb.toString();
}));
return UITools.autoCompletion(textField, property, converter::suggest, converter);
}
private AutoCompletionBinding<CompanyBankAccount> bankAccountAutoCompletion(TextField textField, Label label, SimpleObjectProperty<CompanyBankAccount> property) {
EntityStringConverter<CompanyBankAccount> converter = new EntityStringConverter<>();
converter.setInitialized(account -> getBankAccountService().findById(account.getId()));
label.textProperty().bind(property.map(account -> {
if (account == null) {
return "未选择";
}
if (!Hibernate.isInitialized(account)) {
account = getBankAccountService().findById(account.getId());
property.set(account);
}
StringBuilder sb = new StringBuilder();
sb.append("开户行:");
Bank bank = account.getBank();
if (bank != null) {
if (!Hibernate.isInitialized(bank)) {
bank = getBankService().findById(bank.getId());
account.setBank(bank);
}
sb.append(bank.toPrettyString());
sb.append(" ");
}
sb.append(account.getOpeningBank());
sb.append("\n");
sb.append("开户账号:");
sb.append(account.getAccount());
return sb.toString();
}));
return autoCompletion(textField, property, getBankAccountService()::searchByCompany, converter);
}
private AutoCompletionBinding<CompanyInvoiceInfo> invoiceInfoAutoCompletion(TextField textField, Label label, SimpleObjectProperty<CompanyInvoiceInfo> property) {
EntityStringConverter<CompanyInvoiceInfo> converter = new EntityStringConverter<>();
converter.setInitialized(info -> getInvoiceInfoService().findById(info.getId()));
label.textProperty().bind(property.map(info -> {
if (info == null) {
return "未选择";
}
if (!Hibernate.isInitialized(info)) {
info = getInvoiceInfoService().findById(info.getId());
property.set(info);
}
StringBuilder sb = new StringBuilder();
sb.append("发票抬头:");
sb.append(info.getName());
sb.append("\n");
sb.append("纳税人识别号:");
sb.append(info.getTaxId());
sb.append("\n");
sb.append("地址:");
sb.append(info.getAddress());
sb.append("\n");
sb.append("电话:");
sb.append(info.getPhone());
return sb.toString();
}));
return autoCompletion(textField, property, getInvoiceInfoService()::searchByCompany, converter);
}
private AutoCompletionBinding<CompanyContact> contactAutoCompletion(TextField textField, Label label, SimpleObjectProperty<CompanyContact> property) {
EntityStringConverter<CompanyContact> converter = new EntityStringConverter<>();
converter.setInitialized(contact -> getContactService().findById(contact.getId()));
label.textProperty().bind(property.map(contact -> {
if (contact == null) {
return "未选择";
}
if (!Hibernate.isInitialized(contact)) {
contact = getContactService().findById(contact.getId());
property.set(contact);
}
StringBuilder sb = new StringBuilder();
sb.append("联系人:");
sb.append(contact.getName());
sb.append("\n");
sb.append("职位:");
sb.append(contact.getPosition());
sb.append("\n");
sb.append("电话:");
sb.append(contact.getPhone());
sb.append("\n");
sb.append("邮箱:");
sb.append(contact.getEmail());
return sb.toString();
}));
return autoCompletion(textField, property, getContactService()::searchByCompany, converter);
}
public <T> AutoCompletionBinding<T> autoCompletion(
TextField textField,
SimpleObjectProperty<T> property,
BiFunction<Company, String, List<T>> func,
StringConverter<T> converter
) {
return UITools.autoCompletion(textField, property, text -> {
Company company = viewModel.getCustomer().get();
if (company == null) {
return null;
}
return func.apply(company, text.getUserText());
}, converter);
}
public CompanyService getCompanyService() {
if (companyService == null) {
companyService = getBean(CompanyService.class);
}
return companyService;
}
public BankService getBankService() {
if (bankService == null) {
bankService = getBean(BankService.class);
}
return bankService;
}
public CompanyInvoiceInfoService getInvoiceInfoService() {
if (invoiceInfoService == null) {
invoiceInfoService = getBean(CompanyInvoiceInfoService.class);
}
return invoiceInfoService;
}
public CompanyBankAccountService getBankAccountService() {
if (bankAccountService == null) {
bankAccountService = getBean(CompanyBankAccountService.class);
}
return bankAccountService;
}
public CompanyContactService getContactService() {
if (contactService == null) {
contactService = getBean(CompanyContactService.class);
}
return contactService;
}
}

View File

@@ -0,0 +1,155 @@
package com.ecep.contract.controller.project;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.project.satisfaction_survey.CustomerSatisfactionSurveyWindowController;
import com.ecep.contract.controller.tab.RefreshableSkin;
import com.ecep.contract.converter.EmployeeStringConverter;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.ds.customer.service.CompanyCustomerFileService;
import com.ecep.contract.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.ds.project.service.CustomerSatisfactionSurveyService;
import com.ecep.contract.model.CustomerSatisfactionSurvey;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.Project;
import com.ecep.contract.service.CompanyStringConverter;
import com.ecep.contract.ui.table.cell.EmployeeTableCell;
import com.ecep.contract.vm.CustomerSatisfactionSurveyViewModel;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
import javafx.util.converter.LocalDateStringConverter;
import lombok.Setter;
@FxmlPath("/ui/project/project-tab-customer-satisfaction-survey.fxml")
public class ProjectTabSkinCustomerSatisfactionSurvey
extends AbstProjectTableTabSkin<CustomerSatisfactionSurvey, CustomerSatisfactionSurveyViewModel>
implements RefreshableSkin {
public TableColumn<CustomerSatisfactionSurveyViewModel, Number> idColumn;
public TableColumn<CustomerSatisfactionSurveyViewModel, String> codeColumn;
public TableColumn<CustomerSatisfactionSurveyViewModel, Number> totalScoreColumn;
public TableColumn<CustomerSatisfactionSurveyViewModel, Employee> applicantColumn;
public TableColumn<CustomerSatisfactionSurveyViewModel, LocalDateTime> applyTimeColumn;
public TableColumn<CustomerSatisfactionSurveyViewModel, String> descriptionColumn;
public TableColumn<CustomerSatisfactionSurveyViewModel, LocalDate> dateColumn;
@Setter
private CustomerSatisfactionSurveyService satisfactionSurveyService;
@Setter
private LocalDateStringConverter localDateStringConverter;
@Setter
private EmployeeStringConverter employeeStringConverter;
@Setter
private CompanyStringConverter companyStringConverter;
@Setter
private CompanyService companyService;
@Setter
private CompanyCustomerService customerService;
@Setter
private CompanyCustomerFileService customerFileService;
public ProjectTabSkinCustomerSatisfactionSurvey(ProjectWindowController controller) {
super(controller);
}
@Override
protected CustomerSatisfactionSurveyService getViewModelService() {
return getCustomerSatisfactionSurveyService();
}
@Override
public Tab getTab() {
return controller.satisfactionTab;
}
@Override
public void initializeTable() {
super.initializeTable();
bindNumberColumn(idColumn, CustomerSatisfactionSurveyViewModel::getId);
bindColumn(codeColumn, CustomerSatisfactionSurveyViewModel::getCode);
bindLocalDateColumn(dateColumn, CustomerSatisfactionSurveyViewModel::getDate);
bindNumberColumn(totalScoreColumn, CustomerSatisfactionSurveyViewModel::getTotalScore);
applicantColumn.setCellValueFactory(param -> param.getValue().getApplicant());
applicantColumn.setCellFactory(cell -> new EmployeeTableCell<>(getEmployeeService()));
bindLocalDateTimeColumn(applyTimeColumn, CustomerSatisfactionSurveyViewModel::getApplyTime);
bindColumn(descriptionColumn, CustomerSatisfactionSurveyViewModel::getDescription);
}
@Override
protected CustomerSatisfactionSurveyViewModel createNewViewModel() {
CustomerSatisfactionSurveyViewModel model = super.createNewViewModel();
Project project = getParent();
model.getApplicant().set(controller.getCurrentUser());
LocalDate created = project.getCreated();
if (created == null) {
created = LocalDate.now();
}
model.getApplyTime().set(LocalDateTime.of(created, LocalTime.now()));
saveRow(model);
showInOwner(CustomerSatisfactionSurveyWindowController.class, model);
return model;
}
@Override
protected void onTableRowDoubleClickedAction(CustomerSatisfactionSurveyViewModel item) {
showInOwner(CustomerSatisfactionSurveyWindowController.class, item);
}
private CustomerSatisfactionSurveyService getCustomerSatisfactionSurveyService() {
if (satisfactionSurveyService == null) {
satisfactionSurveyService = getBean(CustomerSatisfactionSurveyService.class);
}
return satisfactionSurveyService;
}
private LocalDateStringConverter getLocalDateStringConverter() {
if (localDateStringConverter == null) {
localDateStringConverter = new LocalDateStringConverter(DateTimeFormatter.ISO_LOCAL_DATE, null);
}
return localDateStringConverter;
}
private EmployeeStringConverter getEmployeeStringConverter() {
if (employeeStringConverter == null) {
employeeStringConverter = getBean(EmployeeStringConverter.class);
}
return employeeStringConverter;
}
public CompanyService getCompanyService() {
if (companyService == null) {
companyService = getBean(CompanyService.class);
}
return companyService;
}
private CompanyStringConverter getCompanyStringConverter() {
if (companyStringConverter == null) {
companyStringConverter = getBean(CompanyStringConverter.class);
}
return companyStringConverter;
}
private CompanyCustomerService getCompanyCustomerService() {
if (customerService == null) {
customerService = getBean(CompanyCustomerService.class);
}
return customerService;
}
private CompanyCustomerFileService getCompanyCustomerFileService() {
if (customerFileService == null) {
customerFileService = getBean(CompanyCustomerFileService.class);
}
return customerFileService;
}
}

View File

@@ -0,0 +1,273 @@
package com.ecep.contract.controller.project;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ecep.contract.ContractPayWay;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
import com.ecep.contract.ds.contract.service.ContractPayPlanService;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.ds.project.service.ProjectFundPlanService;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.ContractPayPlan;
import com.ecep.contract.model.Project;
import com.ecep.contract.model.ProjectFundPlan;
import com.ecep.contract.ui.table.cell.NumberTableCell;
import com.ecep.contract.util.NumberUtils;
import com.ecep.contract.vm.ProjectFundPlanViewModel;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
import lombok.Setter;
/**
* 项目资金计划
* 工单 #2
* fxml 文件在 /src/main/resources/ui/ 目录
*/
@FxmlPath("/ui/project/project-tab-fund-plan.fxml")
public class ProjectTabSkinFundPlan
extends AbstProjectTableTabSkin<ProjectFundPlan, ProjectFundPlanViewModel>
implements TabSkin, EditableEntityTableTabSkin<ProjectFundPlan, ProjectFundPlanViewModel> {
private static final Logger logger = LoggerFactory.getLogger(ProjectTabSkinFundPlan.class);
public TableColumn<ProjectFundPlanViewModel, Number> idColumn;
public TableColumn<ProjectFundPlanViewModel, LocalDate> payDateColumn;
public TableColumn<ProjectFundPlanViewModel, Number> payRatioColumn;
public TableColumn<ProjectFundPlanViewModel, Number> payCurrencyColumn;
public TableColumn<ProjectFundPlanViewModel, Number> balanceColumn;
public TableColumn<ProjectFundPlanViewModel, String> payTermColumn;
public TableColumn<ProjectFundPlanViewModel, LocalDateTime> updateDateColumn;
public TableColumn<ProjectFundPlanViewModel, String> payWayColumn;
public Button updatePlanBtn;
@Setter
private ProjectFundPlanService projectFundPlanService;
public ProjectTabSkinFundPlan(ProjectWindowController controller) {
super(controller);
}
@Override
protected ProjectFundPlanService getViewModelService() {
if (projectFundPlanService == null) {
projectFundPlanService = getBean(ProjectFundPlanService.class);
}
return projectFundPlanService;
}
public ContractPayPlanService getContractPayPlanService() {
return controller.getCachedBean(ContractPayPlanService.class);
}
private ContractService getContractService() {
return controller.getCachedBean(ContractService.class);
}
@Override
public Tab getTab() {
return controller.fundPlanTab;
}
@Override
public void initializeTable() {
super.initializeTable();
// 关闭所有的排序功能
idColumn.setSortable(false);
payDateColumn.setSortable(false);
payRatioColumn.setSortable(false);
payCurrencyColumn.setSortable(false);
payTermColumn.setSortable(false);
updateDateColumn.setSortable(false);
payWayColumn.setSortable(false);
balanceColumn.setSortable(false);
bindNumberColumn(idColumn, ProjectFundPlanViewModel::getId);
bindLocalDateColumn(payDateColumn, ProjectFundPlanViewModel::getPayDate);
bindColumn(payWayColumn, model -> {
javafx.beans.property.SimpleStringProperty property = new javafx.beans.property.SimpleStringProperty();
property.set(model.getPayWay().get() != null ? model.getPayWay().get().getText() : "");
return property;
});
bindNumberColumn(payRatioColumn, ProjectFundPlanViewModel::getPayRatio);
bindNumberColumn(payCurrencyColumn, ProjectFundPlanViewModel::getPayCurrency);
payCurrencyColumn.setCellFactory(
NumberTableCell.forTableColumn(controller.getCurrentEmployee().getNumberStringConverter()));
bindNumberColumn(balanceColumn, ProjectFundPlanViewModel::getBalance);
balanceColumn.setCellFactory(
NumberTableCell.forTableColumn(controller.getCurrentEmployee().getNumberStringConverter()));
bindColumn(payTermColumn, ProjectFundPlanViewModel::getPayTerm);
bindLocalDateTimeColumn(updateDateColumn, ProjectFundPlanViewModel::getUpdateDate);
updatePlanBtn.setOnAction(this::onUpdatePlanAction);
}
@Override
public List<ProjectFundPlanViewModel> loadTableData(Project parent) {
List<ProjectFundPlanViewModel> rows = super.loadTableData(parent);
if (rows == null) {
return null;
}
// TODO 按付款日期排序
rows = rows.stream().sorted(java.util.Comparator.comparing(
model -> model.getPayDate().get(),
java.util.Comparator.nullsFirst(java.util.Comparator.naturalOrder()))).collect(Collectors.toList());
double balance = 0;
for (ProjectFundPlanViewModel row : rows) {
if (row.getPayWay().get() == ContractPayWay.RECEIVE) {
balance += row.getPayCurrency().get();
} else if (row.getPayWay().get() == ContractPayWay.PAY) {
balance -= row.getPayCurrency().get();
}
row.getBalance().set(balance);
}
return rows;
}
/**
* 从合同的付款计划中更新项目的资金计划
* 1. 从项目相关的合同中获取付款计划
* 2. 更新项目的资金计划
*/
private void onUpdatePlanAction(ActionEvent event) {
try {
// 获取当前项目
Project project = getParent();
if (project == null || project.getId() == null) {
setStatus("提示, 无法获取项目信息");
return;
}
List<ProjectFundPlan> fundPlans = getViewModelService().findAllByProject(project);
// 将现有的项目资金计划转为以 ContractPayPlan 的id为key的map
java.util.Map<Number, ProjectFundPlan> fundPlansMap = fundPlans.stream()
.filter(plan -> plan.getContractPayPlan() != null && plan.getContractPayPlan().getId() != null)
.collect(Collectors.toMap(
plan -> plan.getContractPayPlan().getId(),
plan -> plan));
// 获取项目关联的所有合同
List<Contract> contracts = getContractService().findAllByProject(project);
if (contracts == null || contracts.isEmpty()) {
setStatus("提示, 未找到与项目关联的合同");
return;
}
// 遍历所有合同
for (Contract contract : contracts) {
// 获取合同的付款计划
List<ContractPayPlan> payPlans = getContractPayPlanService().findAllByContract(contract);
// 遍历每个付款计划
for (ContractPayPlan payPlan : payPlans) {
// 检查是否已存在相同的合同付款计划
if (fundPlansMap.containsKey(payPlan.getId())) {
// 更新
ProjectFundPlan fundPlan = fundPlansMap.remove(payPlan.getId());
// 检查是否需要更新
boolean needsUpdate = false;
if (!Objects.equals(fundPlan.getPayWay(), contract.getPayWay())) {
fundPlan.setPayWay(contract.getPayWay());
needsUpdate = true;
}
if (!Objects.equals(fundPlan.getPayDate(), payPlan.getPayDate())) {
fundPlan.setPayDate(payPlan.getPayDate());
needsUpdate = true;
}
if (!NumberUtils.equals(fundPlan.getPayRatio(), payPlan.getPayRatio())) {
fundPlan.setPayRatio(payPlan.getPayRatio());
needsUpdate = true;
}
if (!Objects.equals(fundPlan.getPayTerm(), payPlan.getPayTerm())) {
fundPlan.setPayTerm(payPlan.getPayTerm());
needsUpdate = true;
}
if (!NumberUtils.equals(fundPlan.getPayCurrency(), payPlan.getPayCurrency())) {
fundPlan.setPayCurrency(payPlan.getPayCurrency());
needsUpdate = true;
}
if (needsUpdate) {
fundPlan.setContractPayPlan(payPlan);
fundPlan.setUpdateDate(LocalDateTime.now());
saveRowData(fundPlan);
}
continue;
}
// 创建新的项目资金计划
ProjectFundPlan fundPlan = getViewModelService().newInstanceByProject(project);
// 设置资金计划的属性
fundPlan.setPayDate(payPlan.getPayDate());
fundPlan.setPayWay(contract.getPayWay());
fundPlan.setPayRatio(payPlan.getPayRatio());
fundPlan.setPayTerm(payPlan.getPayTerm());
fundPlan.setPayCurrency(payPlan.getPayCurrency());
fundPlan.setContractPayPlan(payPlan);
fundPlan.setUpdateDate(LocalDateTime.now());
saveRowData(fundPlan);
}
}
// 删除
if (!fundPlansMap.isEmpty()) {
for (ProjectFundPlan fundPlan : fundPlansMap.values()) {
getViewModelService().delete(fundPlan);
}
}
// 刷新表格数据
refresh();
} catch (Exception e) {
logger.error("更新项目资金计划失败", e);
}
}
@Override
protected ProjectFundPlanViewModel createNewViewModel() {
ProjectFundPlanViewModel model = new ProjectFundPlanViewModel();
Project project = getParent();
model.getProject().set(project);
model.getUpdateDate().set(LocalDateTime.now());
saveRow(model);
return model;
}
@Override
protected void onTableRowDoubleClickedAction(ProjectFundPlanViewModel item) {
// 可以在这里实现双击打开详情窗口的逻辑
// 目前暂不实现具体的窗口
}
private ProjectFundPlanService getProjectFundPlanService() {
if (projectFundPlanService == null) {
projectFundPlanService = getBean(ProjectFundPlanService.class);
}
return projectFundPlanService;
}
@Override
public void deleteRowData(ProjectFundPlan entity) {
getProjectFundPlanService().delete(entity);
}
@Override
public ProjectFundPlan loadRowData(ProjectFundPlanViewModel row) {
return getProjectFundPlanService().findById(row.getId().get());
}
@Override
public ProjectFundPlan saveRowData(ProjectFundPlan entity) {
return getProjectFundPlanService().save(entity);
}
}

View File

@@ -0,0 +1,213 @@
package com.ecep.contract.controller.project;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.project.quotation.ProjectQuotationWindowController;
import com.ecep.contract.controller.tab.RefreshableSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
import com.ecep.contract.converter.EmployeeStringConverter;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.ds.customer.service.CompanyCustomerFileService;
import com.ecep.contract.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.ds.project.service.ProjectQuotationService;
import com.ecep.contract.model.CompanyCustomerEvaluationFormFile;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.Project;
import com.ecep.contract.model.ProjectQuotation;
import com.ecep.contract.service.CompanyStringConverter;
import com.ecep.contract.ui.table.cell.EmployeeTableCell;
import com.ecep.contract.ui.table.cell.EvaluationFileTableCell;
import com.ecep.contract.ui.table.cell.LocalDateTimeTableCell;
import com.ecep.contract.vm.ProjectQuotationViewModel;
import javafx.scene.control.Tab;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.util.converter.CurrencyStringConverter;
import javafx.util.converter.LocalDateStringConverter;
import lombok.Setter;
@FxmlPath("/ui/project/project-tab-quotation.fxml")
public class ProjectTabSkinQuotation
extends AbstProjectTableTabSkin<ProjectQuotation, ProjectQuotationViewModel>
implements TabSkin, EditableEntityTableTabSkin<ProjectQuotation, ProjectQuotationViewModel>, RefreshableSkin {
public TableColumn<ProjectQuotationViewModel, Number> idColumn;
public TableColumn<ProjectQuotationViewModel, Number> versionColumn;
public TableColumn<ProjectQuotationViewModel, CompanyCustomerEvaluationFormFile> evaluationFileColumn;
public TableColumn<ProjectQuotationViewModel, String> descriptionColumn;
public TableColumn<ProjectQuotationViewModel, Employee> applicantColumn;
public TableColumn<ProjectQuotationViewModel, LocalDateTime> applyTimeColumn;
public TableColumn<ProjectQuotationViewModel, Employee> authorizerColumn;
public TableColumn<ProjectQuotationViewModel, LocalDateTime> authorizationTimeColumn;
public TableColumn<ProjectQuotationViewModel, Number> levelColumn;
public TableColumn<ProjectQuotationViewModel, String> standardPayWayColumn;
public TableColumn<ProjectQuotationViewModel, Number> amountColumn;
@Setter
private ProjectQuotationService projectQuotationService;
@Setter
private LocalDateStringConverter localDateStringConverter;
@Setter
private EmployeeStringConverter employeeStringConverter;
@Setter
private CompanyStringConverter companyStringConverter;
@Setter
private CompanyService companyService;
@Setter
private CompanyCustomerService customerService;
@Setter
private CompanyCustomerFileService customerFileService;
public ProjectTabSkinQuotation(ProjectWindowController controller) {
super(controller);
}
@Override
protected ProjectQuotationService getViewModelService() {
return getProjectQuotationService();
}
@Override
public Tab getTab() {
return controller.quotationApprovalTab;
}
@Override
public void initializeTable() {
super.initializeTable();
levelColumn.setCellValueFactory(param -> param.getValue().getLevel());
levelColumn.setCellFactory(param -> new LevelTableCell());
standardPayWayColumn.setCellValueFactory(param -> param.getValue().getStandardPayWay().map(value -> value == null ? "" : (value ? "标准" : "非标准")));
evaluationFileColumn.setCellValueFactory(param -> param.getValue().getEvaluationFile());
evaluationFileColumn.setCellFactory(param -> new EvaluationFileTableCell<>(getCompanyCustomerFileService()));
amountColumn.setCellValueFactory(param -> param.getValue().getAmount());
amountColumn.setCellFactory(TextFieldTableCell.forTableColumn(new CurrencyStringConverter(getLocale())));
applicantColumn.setCellValueFactory(param -> param.getValue().getApplicant());
applicantColumn.setCellFactory(column1 -> new EmployeeTableCell<>());
applyTimeColumn.setCellValueFactory(param -> param.getValue().getApplyTime());
applyTimeColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
authorizerColumn.setCellValueFactory(param -> param.getValue().getAuthorizer());
authorizerColumn.setCellFactory(column -> new EmployeeTableCell<>());
authorizationTimeColumn.setCellValueFactory(param -> param.getValue().getAuthorizationTime());
authorizationTimeColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
descriptionColumn.setCellValueFactory(param -> param.getValue().getDescription());
}
@Override
protected ProjectQuotationViewModel createNewViewModel() {
ProjectQuotationViewModel model = new ProjectQuotationViewModel();
Project project = getParent();
model.getProject().set(project);
model.getApplicant().set(project.getApplicant());
if (project.getAmount() != null) {
model.getAmount().set(project.getAmount());
}
LocalDate created = project.getCreated();
if (created == null) {
created = LocalDate.now();
}
model.getApplyTime().set(LocalDateTime.of(created, LocalTime.now()));
saveRow(model);
return model;
}
@Override
protected void onTableRowDoubleClickedAction(ProjectQuotationViewModel item) {
showInOwner(ProjectQuotationWindowController.class, item);
}
private ProjectQuotationService getProjectQuotationService() {
if (projectQuotationService == null) {
projectQuotationService = getBean(ProjectQuotationService.class);
}
return projectQuotationService;
}
private LocalDateStringConverter getLocalDateStringConverter() {
if (localDateStringConverter == null) {
localDateStringConverter = new LocalDateStringConverter(DateTimeFormatter.ISO_LOCAL_DATE, null);
}
return localDateStringConverter;
}
private EmployeeStringConverter getEmployeeStringConverter() {
if (employeeStringConverter == null) {
employeeStringConverter = getBean(EmployeeStringConverter.class);
}
return employeeStringConverter;
}
public CompanyService getCompanyService() {
if (companyService == null) {
companyService = getBean(CompanyService.class);
}
return companyService;
}
private CompanyStringConverter getCompanyStringConverter() {
if (companyStringConverter == null) {
companyStringConverter = getBean(CompanyStringConverter.class);
}
return companyStringConverter;
}
private CompanyCustomerService getCompanyCustomerService() {
if (customerService == null) {
customerService = getBean(CompanyCustomerService.class);
}
return customerService;
}
private CompanyCustomerFileService getCompanyCustomerFileService() {
if (customerFileService == null) {
customerFileService = getBean(CompanyCustomerFileService.class);
}
return customerFileService;
}
private static class LevelTableCell extends TableCell<ProjectQuotationViewModel, Number> {
@Override
protected void updateItem(Number item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText("");
return;
}
// 较差,一般,良好
int value = item.intValue();
if (value < 0) {
setText("-");
return;
}
if (value < 1) {
setText("较差");
return;
}
if (value < 2) {
setText("一般");
return;
}
if (value < 3) {
setText("良好");
return;
}
setText(String.valueOf(value));
}
}
}

View File

@@ -0,0 +1,210 @@
package com.ecep.contract.controller.project;
import java.time.format.DateTimeFormatter;
import org.hibernate.Hibernate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.SpringApp;
import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.ds.project.service.ProjectService;
import com.ecep.contract.model.DeliverySignMethod;
import com.ecep.contract.model.ProductType;
import com.ecep.contract.model.ProductUsage;
import com.ecep.contract.model.Project;
import com.ecep.contract.model.ProjectIndustry;
import com.ecep.contract.model.ProjectSaleType;
import com.ecep.contract.model.ProjectType;
import com.ecep.contract.vm.ProjectViewModel;
import javafx.beans.binding.Bindings;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import javafx.util.converter.LocalDateStringConverter;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/project/project.fxml")
public class ProjectWindowController extends AbstEntityController<Project, ProjectViewModel> {
private static final Logger logger = LoggerFactory.getLogger(ProjectWindowController.class);
public static void show(Project project, Window window) {
ProjectViewModel viewModel = new ProjectViewModel();
if (!Hibernate.isInitialized(project)) {
project = SpringApp.getBean(ProjectService.class).findById(project.getId());
}
viewModel.update(project);
show(viewModel, window);
}
public static void show(ProjectViewModel model, Window window) {
show(ProjectWindowController.class, model, window);
}
public BorderPane root;
public TabPane tabPane;
public Button refreshBtn;
public Button deleteBtn;
LocalDateStringConverter localDateStringConverter = new LocalDateStringConverter(DateTimeFormatter.ISO_LOCAL_DATE, null);
// 基本信息 Tab
public Tab baseInfoTab;
public TextField nameField;
public TextField codeField;
public TextField codeYearField;
public TextField codeSequenceNumberField;
public TextField addressField;
public TextArea descriptionField;
public CheckBox useBidField;
public CheckBox useOfferField;
public TextField amountField;
public CheckBox standardPayWayField;
public ComboBox<ProjectIndustry> industryField;
public ComboBox<ProjectSaleType> saleTypeField;
public ComboBox<ProjectType> projectTypeField;
public ComboBox<ProductType> productTypeField;
public ComboBox<DeliverySignMethod> deliverySignMethodField;
public ComboBox<ProductUsage> productUsageField;
public TextField applicantField;
public TextField authorizerField;
public DatePicker plannedStartTimeField;
public DatePicker plannedCompletionTimeField;
public DatePicker createdField;
public Label versionLabel;
/*
合同 Tab
*/
public Tab contractTab;
/* 成本审批 */
public Tab costTab;
// 文件 Tab
public Tab fileTab;
public TableView fileTable;
public TableColumn fileTable_idColumn;
public TableColumn fileTable_typeColumn;
public TableColumn fileTable_filePathColumn;
public TableColumn fileTable_applyDateColumn;
public TableColumn fileTable_descriptionColumn;
// 客户信息 Tab
public Tab companyTab;
// 报价审批 Tab
public Tab quotationApprovalTab;
/**
* 投标 Bid Tab
*/
public Tab bidTab;
/**
* 资金计划 Tab
*/
public Tab fundPlanTab;
/**
* 顾客满意度 Tab
*/
public Tab satisfactionTab;
@Autowired
ProjectService projectService;
@Autowired
private EmployeeService employeeService;
@Autowired
ContractService contractService;
public ProjectWindowController() {
}
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
refreshBtn.setOnAction(event -> refreshByButton((Button) event.getSource()));
getTitle().bind(Bindings.createStringBinding(() -> "[" + viewModel.getId().get() + "] " + viewModel.getCode().get() + " " + viewModel.getName().getValue() + " 项目详情", viewModel.getCode(), viewModel.getName()));
}
@Override
protected Project loadEntity() {
return projectService.findById(viewModel.getId().get());
}
@Override
protected Project saveEntity(Project entity) {
return projectService.save(entity);
}
@Override
protected void registerTabSkins() {
registerTabSkin(baseInfoTab, this::createBaseTabSkin);
registerTabSkin(companyTab, tab -> new ProjectTabSkinCustomerInfo(this));
registerTabSkin(contractTab, tab -> new ProjectTabSkinContract(this));
registerTabSkin(costTab, tab -> new ProjectTabSkinCost(this));
registerTabSkin(quotationApprovalTab, tab -> new ProjectTabSkinQuotation(this));
registerTabSkin(bidTab, tab -> new ProjectTabSkinBid(this));
registerTabSkin(fundPlanTab, tab -> new ProjectTabSkinFundPlan(this));
// registerTabSkin(costItemTab, this::createCostItemTabSkin);
registerTabSkin(satisfactionTab, tab -> new ProjectTabSkinCustomerSatisfactionSurvey(this));
}
@Override
public ProjectService getViewModelService() {
return projectService;
}
private ProjectTabSkinBase createBaseTabSkin(Tab tab) {
ProjectTabSkinBase skin = new ProjectTabSkinBase(this);
skin.setLocalDateStringConverter(localDateStringConverter);
skin.setEmployeeService(employeeService);
return skin;
}
public void onFileReBuildingAction(ActionEvent event) {
}
public void onFileTableContextMenuShowing(WindowEvent windowEvent) {
}
public void onFileTableRefreshAction(ActionEvent event) {
}
public void onFileTableDeleteAction(ActionEvent event) {
}
public void onGetNextCodeSNAction(ActionEvent event) {
ProjectSaleType projectSaleType = viewModel.getSaleType().get();
int year = viewModel.getCodeYear().get();
int sn = viewModel.getCodeSequenceNumber().get();
int nextSequenceNumber = projectService.getNextCodeSequenceNumber(projectSaleType, year);
viewModel.getCodeSequenceNumber().set(nextSequenceNumber);
}
}

Some files were not shown because too many files have changed in this diff Show More