保存用户自选主题

This commit is contained in:
wenli 2024-09-08 11:18:50 +08:00
parent 5b94f1c7bd
commit bba3297181
11 changed files with 226 additions and 43 deletions

View File

@ -84,6 +84,20 @@ public class ConfigController {
return success(config.getValue());
}
@GetMapping(value = "/get-by-key")
@Operation(summary = "根据参数键名查询参数值", description = "不可见的配置,不允许返回给前端")
@Parameter(name = "key", description = "参数键", required = true, example = "yunai.biz.username")
public CommonResult<ConfigRespVO> getConfig(@RequestParam("key") String key) {
ConfigDO config = configService.getConfigByKey(key);
if (config == null) {
return success(null);
}
if (!config.getVisible()) {
throw exception(ErrorCodeConstants.CONFIG_GET_VALUE_ERROR_IF_VISIBLE);
}
return success(ConfigConvert.INSTANCE.convert(config));
}
@GetMapping("/page")
@Operation(summary = "获取参数配置分页")
@PreAuthorize("@ss.hasPermission('infra:config:query')")

View File

@ -33,6 +33,11 @@ public interface ConfigFeign extends BaseFeignApi {
@RequestLine("GET /admin-api/infra/config/get-value-by-key?key={key}")
public CommonResult<String> getConfigKey(@Param("key") String key);
//"根据参数键名查询参数值", description = "不可见的配置,不允许返回给前端")
@RequestLine("GET /admin-api/infra/config/get-by-key?key={key}")
public CommonResult<ConfigRespVO> getConfig(@Param("key") String key);
//"获取参数配置分页")
@RequestLine("GET /admin-api/infra/config/page")
public CommonResult<PageResult<ConfigRespVO>> getConfigPage(@QueryMap Map<String, Object> pageReqVO);

View File

@ -4,6 +4,7 @@ import com.lw.dillon.admin.framework.common.pojo.CommonResult;
import com.lw.dillon.admin.module.system.controller.admin.auth.vo.AuthPermissionInfoRespVO;
import com.lw.dillon.admin.module.system.controller.admin.dict.vo.data.DictDataSimpleRespVO;
import com.lw.fx.request.Request;
import com.lw.ui.request.api.config.ConfigFeign;
import com.lw.ui.request.api.system.DictDataFeign;
import com.lw.ui.utils.DictTypeEnum;
import io.datafx.core.concurrent.ProcessChain;
@ -26,6 +27,8 @@ public class AppStore {
return token;
}
public static String userTheme;
public static void setToken(String token) {
AppStore.token = token;
}
@ -77,6 +80,13 @@ public class AppStore {
return reultMap;
}
public static String getUserTheme() {
return userTheme;
}
public static void setUserTheme(String userTheme) {
AppStore.userTheme = userTheme;
}
public static void loadDictData() {
@ -93,4 +103,6 @@ public class AppStore {
.run();
}
}

View File

@ -31,14 +31,14 @@ import static java.nio.charset.StandardCharsets.UTF_8;
public final class ThemeManager {
static final String DUMMY_STYLESHEET = getResource("assets/styles/empty.css").toString();
static final String[] APP_STYLESHEETS = new String[] {
Resources.resolve("assets/styles/index.css")
static final String[] APP_STYLESHEETS = new String[]{
Resources.resolve("assets/styles/index.css")
};
static final Set<Class<? extends Theme>> PROJECT_THEMES = Set.of(
PrimerLight.class, PrimerDark.class,
NordLight.class, NordDark.class,
CupertinoLight.class, CupertinoDark.class,
Dracula.class
PrimerLight.class, PrimerDark.class,
NordLight.class, NordDark.class,
CupertinoLight.class, CupertinoDark.class,
Dracula.class
);
private static final PseudoClass DARK = PseudoClass.getPseudoClass("dark");
@ -87,6 +87,21 @@ public final class ThemeManager {
return getRepository().getAll().get(0);
}
public void setTheme(String themeName) {
SamplerTheme samplerTheme = getDefaultTheme();
// 使用Stream查找name为"abc"的SamplerTheme
Optional<SamplerTheme> result = getRepository().getAll().stream()
.filter(theme -> themeName.equals(theme.getName()))
.findFirst();
// 处理查找到的结果
if (result.isPresent()) {
samplerTheme = result.get();
}
setTheme(samplerTheme);
}
/**
* See {@link SamplerTheme}.
*/
@ -134,11 +149,11 @@ public final class ThemeManager {
public void setFontSize(int size) {
if (!SUPPORTED_FONT_SIZE.contains(size)) {
throw new IllegalArgumentException(
String.format("Font size must in the range %d-%dpx. Actual value is %d.",
SUPPORTED_FONT_SIZE.get(0),
SUPPORTED_FONT_SIZE.get(SUPPORTED_FONT_SIZE.size() - 1),
size
));
String.format("Font size must in the range %d-%dpx. Actual value is %d.",
SUPPORTED_FONT_SIZE.get(0),
SUPPORTED_FONT_SIZE.get(SUPPORTED_FONT_SIZE.size() - 1),
size
));
}
setCustomDeclaration("-fx-font-size", size + "px");
@ -148,8 +163,8 @@ public final class ThemeManager {
var rawZoom = (int) Math.ceil((size * 1.0 / DEFAULT_FONT_SIZE) * 100);
this.zoom = SUPPORTED_ZOOM.stream()
.min(Comparator.comparingInt(i -> Math.abs(i - rawZoom)))
.orElseThrow(NoSuchElementException::new);
.min(Comparator.comparingInt(i -> Math.abs(i - rawZoom)))
.orElseThrow(NoSuchElementException::new);
reloadCustomCSS();
EVENT_BUS.publish(new ThemeEvent(ThemeEvent.EventType.FONT_CHANGE));
@ -166,7 +181,7 @@ public final class ThemeManager {
public void setZoom(int zoom) {
if (!SUPPORTED_ZOOM.contains(zoom)) {
throw new IllegalArgumentException(
String.format("Zoom value must one of %s. Actual value is %d.", SUPPORTED_ZOOM, zoom)
String.format("Zoom value must one of %s. Actual value is %d.", SUPPORTED_ZOOM, zoom)
);
}
@ -260,10 +275,10 @@ public final class ThemeManager {
Objects.requireNonNull(colorName);
if (color != null) {
setCustomDeclaration(colorName, JColor.color(
(float) color.getRed(),
(float) color.getGreen(),
(float) color.getBlue(),
(float) color.getOpacity()).getColorHexWithAlpha()
(float) color.getRed(),
(float) color.getGreen(),
(float) color.getBlue(),
(float) color.getOpacity()).getColorHexWithAlpha()
);
} else {
removeCustomDeclaration(colorName);
@ -278,8 +293,8 @@ public final class ThemeManager {
root.getChildren().add(imageView); // add snapshot on top
var transition = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(imageView.opacityProperty(), 1, Interpolator.EASE_OUT)),
new KeyFrame(duration, new KeyValue(imageView.opacityProperty(), 0, Interpolator.EASE_OUT))
new KeyFrame(Duration.ZERO, new KeyValue(imageView.opacityProperty(), 1, Interpolator.EASE_OUT)),
new KeyFrame(duration, new KeyValue(imageView.opacityProperty(), 0, Interpolator.EASE_OUT))
);
transition.setOnFinished(e -> root.getChildren().remove(imageView));
transition.play();
@ -315,7 +330,7 @@ public final class ThemeManager {
getScene().getRoot().getStylesheets().removeIf(uri -> uri.startsWith("data:text/css"));
getScene().getRoot().getStylesheets().add(
"data:text/css;base64," + Base64.getEncoder().encodeToString(css.toString().getBytes(UTF_8))
"data:text/css;base64," + Base64.getEncoder().encodeToString(css.toString().getBytes(UTF_8))
);
getScene().getRoot().pseudoClassStateChanged(USER_CUSTOM, true);
}

View File

@ -62,6 +62,7 @@ public class LoginViewModel implements ViewModel {
public BooleanProperty loginStautsPropertyProperty() {
return loginStautsProperty;
}

View File

@ -1,10 +1,13 @@
package com.lw.fx.view.main;
import com.lw.dillon.admin.framework.common.pojo.CommonResult;
import com.lw.dillon.admin.module.system.controller.admin.auth.vo.AuthPermissionInfoRespVO;
import com.lw.fx.request.Request;
import com.lw.fx.store.AppStore;
import com.lw.fx.theme.ThemeManager;
import com.lw.fx.util.MessageType;
import com.lw.fx.websocket.SSLWebSocketClient;
import com.lw.ui.request.api.config.ConfigFeign;
import com.lw.ui.request.api.system.AuthFeign;
import com.lw.ui.request.api.system.NotifyMessageFeign;
import de.saxsys.mvvmfx.MvvmFX;
@ -13,6 +16,7 @@ import de.saxsys.mvvmfx.ViewModel;
import io.datafx.core.concurrent.ProcessChain;
import javafx.beans.property.*;
import javafx.scene.control.TreeItem;
import net.datafaker.App;
public class MainViewModel implements ViewModel, SceneLifecycle {
@ -71,12 +75,7 @@ public class MainViewModel implements ViewModel, SceneLifecycle {
if (r.isSuccess()) {
SSLWebSocketClient.getInstance().loginOut();
if (exit) {
System.exit(0);
} else {
MvvmFX.getNotificationCenter().publish("showLoginRegister");
MvvmFX.getNotificationCenter().publish("message", "退出成功!", MessageType.SUCCESS);
}
}
})
@ -84,6 +83,12 @@ public class MainViewModel implements ViewModel, SceneLifecycle {
e.printStackTrace();
})
.withFinal(() -> {
if (exit) {
System.exit(0);
} else {
MvvmFX.getNotificationCenter().publish("showLoginRegister");
MvvmFX.getNotificationCenter().publish("message", "退出成功!", MessageType.SUCCESS);
}
})
.run();
}
@ -142,12 +147,19 @@ public class MainViewModel implements ViewModel, SceneLifecycle {
for (AuthPermissionInfoRespVO.MenuVO menu : r.getData().getMenus()) {
rootItem.getChildren().add(createTreeItem(menu));
}
treeItemObjectProperty.set(rootItem);
} else {
}
})
.addSupplierInExecutor(() -> {
String key = "fx.theme.userid." + AppStore.getAuthPermissionInfoRespVO().getUser().getId();
CommonResult<String> commonResult = Request.connector(ConfigFeign.class).getConfigKey(key);
return commonResult.getCheckedData();
}).addConsumerInPlatformThread(userTheme -> {
var tm = ThemeManager.getInstance();
tm.setTheme(userTheme);
})
.addSupplierInExecutor(() -> Request.connector(NotifyMessageFeign.class).getUnreadNotifyMessageCount())
.addConsumerInPlatformThread(rel -> {
if (rel.isSuccess()) {
@ -155,6 +167,7 @@ public class MainViewModel implements ViewModel, SceneLifecycle {
}
})
.onException(e -> {
unreadCount.set("");
e.printStackTrace();

View File

@ -2,9 +2,19 @@
package com.lw.fx.view.main;
import com.lw.dillon.admin.module.infra.controller.admin.config.vo.ConfigRespVO;
import com.lw.dillon.admin.module.infra.controller.admin.config.vo.ConfigSaveReqVO;
import com.lw.fx.request.Request;
import com.lw.fx.store.AppStore;
import com.lw.fx.theme.SamplerTheme;
import com.lw.fx.theme.ThemeManager;
import com.lw.fx.util.MessageType;
import com.lw.fx.view.control.ModalDialog;
import com.lw.fx.websocket.SSLWebSocketClient;
import com.lw.ui.request.api.config.ConfigFeign;
import com.lw.ui.request.api.system.AuthFeign;
import de.saxsys.mvvmfx.MvvmFX;
import io.datafx.core.concurrent.ProcessChain;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.ToggleGroup;
@ -31,10 +41,50 @@ final class ThemeDialog extends ModalDialog {
thumbnailsGroup.selectedToggleProperty().addListener((obs, old, val) -> {
if (val != null && val.getUserData() instanceof SamplerTheme theme) {
ThemeManager.getInstance().setTheme(theme);
updateThemeConfig(theme);
}
});
}
private void updateThemeConfig(SamplerTheme theme){
ProcessChain.create()
.addSupplierInExecutor(() -> {
String key = "fx.theme.userid." + AppStore.getAuthPermissionInfoRespVO().getUser().getId();
ConfigRespVO configRespVO = Request.connector(ConfigFeign.class).getConfig(key).getCheckedData();
if (configRespVO == null) {
ConfigSaveReqVO saveReqVO = new ConfigSaveReqVO();
saveReqVO.setKey(key);
saveReqVO.setValue(theme.getName());
saveReqVO.setVisible(true);
saveReqVO.setCategory("ui");
saveReqVO.setName("用户主题");
return Request.connector(ConfigFeign.class).createConfig(saveReqVO);
}else {
ConfigSaveReqVO saveReqVO = new ConfigSaveReqVO();
saveReqVO.setId(configRespVO.getId());
saveReqVO.setName(configRespVO.getName());
saveReqVO.setCategory(configRespVO.getCategory());
saveReqVO.setValue(theme.getName());
saveReqVO.setKey(configRespVO.getKey());
saveReqVO.setVisible(true);
return Request.connector(ConfigFeign.class).updateConfig(saveReqVO);
}
})
.addConsumerInPlatformThread(r -> {
if (r.isSuccess()) {
}
})
.onException(e -> {
e.printStackTrace();
})
.run();
}
private VBox createContent() {
thumbnailsPane.setAlignment(Pos.TOP_CENTER);
thumbnailsPane.setPrefColumns(3);

View File

@ -113,7 +113,7 @@ public class RoleAssignMenuFormViewModel implements ViewModel {
}
private void findSelectedItems(CheckBoxTreeItem<MenuSimpleRespVO> item, Set<Long> selMenuIds) {
if (item.isSelected()) {
if (item.isSelected()||item.isIndeterminate()) {
selMenuIds.add(item.getValue().getId());
}

View File

@ -96,7 +96,7 @@ public class RoleDataPermissionFormViewModel implements ViewModel {
}
private void findSelectedItems(CheckBoxTreeItem<DeptSimpleRespVO> item, Set<Long> selMenuIds) {
if (item.isSelected()) {
if (item.isSelected()||item.isIndeterminate()) {
selMenuIds.add(item.getValue().getId());
}

View File

@ -14,7 +14,9 @@ import com.lw.dillon.admin.module.system.controller.admin.auth.vo.AuthLoginRespV
import com.lw.dillon.admin.module.system.controller.admin.auth.vo.AuthPermissionInfoRespVO;
import com.lw.swing.request.Request;
import com.lw.swing.store.AppStore;
import com.lw.swing.theme.LightTheme;
import com.lw.swing.websocket.SSLWebSocketClient;
import com.lw.ui.request.api.config.ConfigFeign;
import com.lw.ui.request.api.system.AuthFeign;
import net.miginfocom.swing.MigLayout;
@ -349,12 +351,15 @@ public class LoginPane extends JPanel {
AuthLoginReqVO authLoginReqVO = new AuthLoginReqVO();
authLoginReqVO.setUsername(username);
authLoginReqVO.setPassword(password);
SwingWorker<CommonResult, String> worker = new SwingWorker<CommonResult, String>() {
SwingWorker<Map<String, String>, String> worker = new SwingWorker<Map<String, String>, String>() {
@Override
protected CommonResult doInBackground() throws Exception {
protected Map<String, String> doInBackground() throws Exception {
CommonResult<AuthLoginRespVO> commonResult = Request.connector(AuthFeign.class).login(authLoginReqVO);
String key = "swing.theme.userid." + commonResult.getCheckedData().getUserId();
String userTheme = null;
if (commonResult.isSuccess()) {
AppStore.setAuthLoginRespVO(commonResult.getData());
@ -362,18 +367,23 @@ public class LoginPane extends JPanel {
if (permissionInfoRespVOCommonResult.isSuccess()) {
AppStore.setAuthPermissionInfoRespVO(permissionInfoRespVOCommonResult.getData());
userTheme = Request.connector(ConfigFeign.class).getConfigKey(key).getCheckedData();
}
AppStore.loadDictData();
}
return commonResult;
Map<String, String> stringStringMap = new HashMap<>();
stringStringMap.put("login", commonResult.isSuccess() ? "登录成功" : "登录失败");
stringStringMap.put("msg", commonResult.getMsg());
stringStringMap.put("userTheme", StrUtil.isBlank(userTheme) ? LightTheme.class.getName() : userTheme);
return stringStringMap;
}
@Override
protected void done() {
try {
if (get().isSuccess()) {
if (StrUtil.equals(get().get("login"), "登录成功")) {
SSLWebSocketClient.getInstance().start();
msgLabel.setVisible(false);
@ -387,12 +397,13 @@ public class LoginPane extends JPanel {
MainPrefs.getState().put(KEY_USER_CUR, json);
}
UIManager.setLookAndFeel(get().get("userTheme"));
MainFrame.getInstance().showMainPane();
} else {
usernameTextFiled.setRequestFocusEnabled(true);
msgLabel.setVisible(true);
msgLabel.setText(get().getMsg());
msgLabel.setText(get().get("msg"));
}
} catch (Exception e) {
msgLabel.setText("无法连接服务器,请检查服务器是否启动。");

View File

@ -9,6 +9,8 @@ import com.formdev.flatlaf.extras.FlatSVGIcon;
import com.formdev.flatlaf.util.LoggingFacade;
import com.jidesoft.swing.JideTabbedPane;
import com.lw.dillon.admin.framework.common.pojo.CommonResult;
import com.lw.dillon.admin.module.infra.controller.admin.config.vo.ConfigRespVO;
import com.lw.dillon.admin.module.infra.controller.admin.config.vo.ConfigSaveReqVO;
import com.lw.dillon.admin.module.system.controller.admin.auth.vo.AuthPermissionInfoRespVO;
import com.lw.swing.components.ReflectionRepaintManager;
import com.lw.swing.components.ui.MainTabbedPaneUI;
@ -21,6 +23,7 @@ import com.lw.swing.utils.IconLoader;
import com.lw.swing.view.system.notice.MyNotifyMessagePane;
import com.lw.swing.view.system.user.PersonalCenterPanel;
import com.lw.swing.websocket.SSLWebSocketClient;
import com.lw.ui.request.api.config.ConfigFeign;
import com.lw.ui.request.api.system.AuthFeign;
import javax.swing.*;
@ -509,9 +512,68 @@ public class MainFrame extends JFrame {
FlatAnimatedLafChange.hideSnapshotWithAnimation();
});
updateUserTheme(theme);
}
public void updateUserTheme(String theme) {
String userTheme = LightTheme.class.getName();
switch (theme) {
case "深色": {
userTheme = DarkTheme.class.getName();
break;
}
case "白色": {
userTheme = (LightTheme.class.getName());
break;
}
case "玻璃": {
userTheme = GlazzedTheme.class.getName();
break;
}
default: {
userTheme = LightTheme.class.getName();
break;
}
}
String finalUserTheme = userTheme;
SwingWorker<String, String> stringSwingWorker = new SwingWorker<String, String>() {
@Override
protected String doInBackground() throws Exception {
String key = "swing.theme.userid." + AppStore.getAuthPermissionInfoRespVO().getUser().getId();
ConfigRespVO configRespVO = Request.connector(ConfigFeign.class).getConfig(key).getCheckedData();
if (configRespVO == null) {
ConfigSaveReqVO saveReqVO = new ConfigSaveReqVO();
saveReqVO.setKey(key);
saveReqVO.setValue(finalUserTheme);
saveReqVO.setVisible(true);
saveReqVO.setCategory("ui");
saveReqVO.setName("用户主题");
Request.connector(ConfigFeign.class).createConfig(saveReqVO);
} else {
ConfigSaveReqVO saveReqVO = new ConfigSaveReqVO();
saveReqVO.setId(configRespVO.getId());
saveReqVO.setName(configRespVO.getName());
saveReqVO.setCategory(configRespVO.getCategory());
saveReqVO.setValue(finalUserTheme);
saveReqVO.setKey(configRespVO.getKey());
saveReqVO.setVisible(true);
Request.connector(ConfigFeign.class).updateConfig(saveReqVO);
}
return "";
}
};
stringSwingWorker.execute();
}
private void showMenuBar(boolean isShow) {
setJMenuBar(isShow ? getTitleMenuBar() : null);
this.revalidate();
@ -591,17 +653,17 @@ public class MainFrame extends JFrame {
try {
if (get().isSuccess()) {
SSLWebSocketClient.getInstance().loginOut();
if (exit) {
System.exit(0);
} else {
showLogin();
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (exit) {
System.exit(0);
} else {
showLogin();
}
}
}
};