refactor: 重构偏好模块.

This commit is contained in:
lijiahang
2023-12-19 17:48:48 +08:00
parent 581c14d8d7
commit bb83fe447b
24 changed files with 237 additions and 211 deletions

View File

@@ -517,7 +517,7 @@ public class RedisMaps extends RedisUtils {
* @param <V> V * @param <V> V
* @return entity * @return entity
*/ */
public static <K, V> Map<String, V> entitiesJson(String key, Class<V> clazz) { public static <V> Map<String, V> entitiesJson(String key, Class<V> clazz) {
return entitiesJson(key, Function.identity(), clazz); return entitiesJson(key, Function.identity(), clazz);
} }

View File

@@ -1,8 +1,13 @@
package com.orion.ops.module.infra.handler.preference.model; package com.orion.ops.module.infra.handler.preference.model;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.orion.lang.define.wrapper.Ref;
import com.orion.lang.utils.collect.Maps;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Function;
/** /**
* 偏好 * 偏好
@@ -18,8 +23,9 @@ public interface PreferenceModel {
* *
* @return map * @return map
*/ */
default Map<String, Object> toMap() { default Map<String, String> toMap() {
return JSON.parseObject(JSON.toJSONString(this)); JSONObject map = JSON.parseObject(JSON.toJSONString(this));
return Maps.map(map, Function.identity(), v -> JSON.toJSONString(Ref.of(v)));
} }
} }

View File

@@ -5,8 +5,8 @@ Authorization: {{token}}
{ {
"type": "SYSTEM", "type": "SYSTEM",
"config": { "item": "",
} "value": ""
} }

View File

@@ -1,8 +1,9 @@
package com.orion.ops.module.infra.controller; package com.orion.ops.module.infra.controller;
import com.orion.lang.define.wrapper.HttpWrapper;
import com.orion.ops.framework.web.core.annotation.RestWrapper; import com.orion.ops.framework.web.core.annotation.RestWrapper;
import com.orion.ops.module.infra.entity.request.preference.PreferenceUpdatePartialRequest;
import com.orion.ops.module.infra.entity.request.preference.PreferenceUpdateRequest; import com.orion.ops.module.infra.entity.request.preference.PreferenceUpdateRequest;
import com.orion.ops.module.infra.entity.vo.PreferenceVO;
import com.orion.ops.module.infra.service.PreferenceService; import com.orion.ops.module.infra.service.PreferenceService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
@@ -12,6 +13,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Map;
/** /**
* 用户偏好 api * 用户偏好 api
@@ -33,21 +35,22 @@ public class PreferenceController {
private PreferenceService preferenceService; private PreferenceService preferenceService;
@PutMapping("/update") @PutMapping("/update")
@Operation(summary = "更新用户偏好-整体") @Operation(summary = "更新用户偏好-单个")
public Integer updatePreference(@Validated @RequestBody PreferenceUpdateRequest request) { public Integer updatePreference(@Validated @RequestBody PreferenceUpdateRequest request) {
return preferenceService.updatePreference(request, false); return preferenceService.updatePreference(request);
} }
@PutMapping("/update-partial") @PutMapping("/update-partial")
@Operation(summary = "更新用户偏好-部分") @Operation(summary = "更新用户偏好-部分")
public Integer updatePreferencePartial(@Validated @RequestBody PreferenceUpdateRequest request) { public HttpWrapper<?> updatePreferencePartial(@Validated @RequestBody PreferenceUpdatePartialRequest request) {
return preferenceService.updatePreference(request, true); preferenceService.updatePreferencePartial(request);
return HttpWrapper.ok();
} }
@GetMapping("/get") @GetMapping("/get")
@Operation(summary = "查询用户偏好") @Operation(summary = "查询用户偏好")
@Parameter(name = "type", description = "type", required = true) @Parameter(name = "type", description = "type", required = true)
public PreferenceVO getPreference(@RequestParam("type") String type) { public Map<String, Object> getPreference(@RequestParam("type") String type) {
return preferenceService.getPreferenceByType(type); return preferenceService.getPreferenceByType(type);
} }

View File

@@ -1,28 +0,0 @@
package com.orion.ops.module.infra.convert;
import com.orion.ops.module.infra.entity.domain.PreferenceDO;
import com.orion.ops.module.infra.entity.request.preference.PreferenceUpdateRequest;
import com.orion.ops.module.infra.entity.vo.PreferenceVO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* 用户偏好 内部对象转换器
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-9-27 18:37
*/
@Mapper
public interface PreferenceConvert {
PreferenceConvert MAPPER = Mappers.getMapper(PreferenceConvert.class);
@Mapping(target = "config", ignore = true)
PreferenceDO to(PreferenceUpdateRequest request);
@Mapping(target = "config", ignore = true)
PreferenceVO to(PreferenceDO domain);
}

View File

@@ -1,9 +1,9 @@
package com.orion.ops.module.infra.define.cache; package com.orion.ops.module.infra.define.cache;
import com.alibaba.fastjson.JSONObject;
import com.orion.lang.define.cache.key.CacheKeyBuilder; import com.orion.lang.define.cache.key.CacheKeyBuilder;
import com.orion.lang.define.cache.key.CacheKeyDefine; import com.orion.lang.define.cache.key.CacheKeyDefine;
import com.orion.lang.define.cache.key.struct.RedisCacheStruct; import com.orion.lang.define.cache.key.struct.RedisCacheStruct;
import com.orion.lang.define.wrapper.Ref;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -19,8 +19,8 @@ public interface PreferenceCacheKeyDefine {
CacheKeyDefine PREFERENCE = new CacheKeyBuilder() CacheKeyDefine PREFERENCE = new CacheKeyBuilder()
.key("user:preference:{}:{}") .key("user:preference:{}:{}")
.desc("用户偏好 ${userId} ${type}") .desc("用户偏好 ${userId} ${type}")
.type(JSONObject.class) .type(Ref.class)
.struct(RedisCacheStruct.STRING) .struct(RedisCacheStruct.HASH)
.timeout(1, TimeUnit.DAYS) .timeout(1, TimeUnit.DAYS)
.build(); .build();

View File

@@ -38,8 +38,12 @@ public class PreferenceDO extends BaseDO {
@TableField("type") @TableField("type")
private String type; private String type;
@Schema(description = "偏好配置") @Schema(description = "配置")
@TableField("config") @TableField("item")
private String config; private String item;
@Schema(description = "配置值")
@TableField("value")
private String value;
} }

View File

@@ -0,0 +1,38 @@
package com.orion.ops.module.infra.entity.request.preference;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.Map;
/**
* 用户偏好 部分更新请求对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-9-27 18:37
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "PreferenceUpdatePartialRequest", description = "用户偏好 部分更新请求对象")
public class PreferenceUpdatePartialRequest implements Serializable {
@NotBlank
@Size(max = 12)
@Schema(description = "类型")
private String type;
@NotEmpty
@Schema(description = "偏好配置")
private Map<String, Object> config;
}

View File

@@ -7,10 +7,8 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import java.io.Serializable; import java.io.Serializable;
import java.util.Map;
/** /**
* 用户偏好 更新请求对象 * 用户偏好 更新请求对象
@@ -31,8 +29,12 @@ public class PreferenceUpdateRequest implements Serializable {
@Schema(description = "类型") @Schema(description = "类型")
private String type; private String type;
@NotEmpty @NotBlank
@Size(max = 32)
@Schema(description = "偏好配置") @Schema(description = "偏好配置")
private Map<String, Object> config; private String item;
@Schema(description = "偏好配置")
private Object value;
} }

View File

@@ -1,31 +0,0 @@
package com.orion.ops.module.infra.entity.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Map;
/**
* 用户偏好 视图响应对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-9-27 18:37
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "PreferenceVO", description = "用户偏好 视图响应对象")
public class PreferenceVO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "偏好配置")
private Map<String, Object> config;
}

View File

@@ -17,7 +17,7 @@ public class TerminalPreferenceStrategy implements IPreferenceStrategy<TerminalP
@Override @Override
public TerminalPreferenceModel getDefault() { public TerminalPreferenceModel getDefault() {
return TerminalPreferenceModel.builder() return TerminalPreferenceModel.builder()
.darkTheme("dark") .darkTheme("auto")
.newConnectionType("group") .newConnectionType("group")
.displaySetting(TerminalPreferenceModel.DisplaySettingModel.builder() .displaySetting(TerminalPreferenceModel.DisplaySettingModel.builder()
.fontFamily("_") .fontFamily("_")

View File

@@ -1,7 +1,7 @@
package com.orion.ops.module.infra.service; package com.orion.ops.module.infra.service;
import com.orion.ops.module.infra.entity.request.preference.PreferenceUpdatePartialRequest;
import com.orion.ops.module.infra.entity.request.preference.PreferenceUpdateRequest; import com.orion.ops.module.infra.entity.request.preference.PreferenceUpdateRequest;
import com.orion.ops.module.infra.entity.vo.PreferenceVO;
import com.orion.ops.module.infra.enums.PreferenceTypeEnum; import com.orion.ops.module.infra.enums.PreferenceTypeEnum;
import java.util.Map; import java.util.Map;
@@ -17,21 +17,27 @@ import java.util.concurrent.Future;
public interface PreferenceService { public interface PreferenceService {
/** /**
* 更新用户偏好 * 更新用户偏好-单个
* *
* @param request request * @param request request
* @param partial 是否为部分更新
* @return effect * @return effect
*/ */
Integer updatePreference(PreferenceUpdateRequest request, boolean partial); Integer updatePreference(PreferenceUpdateRequest request);
/**
* 更新用户偏好-部分
*
* @param request request
*/
void updatePreferencePartial(PreferenceUpdatePartialRequest request);
/** /**
* 查询用户偏好 * 查询用户偏好
* *
* @param type type * @param type type
* @return row * @return rows
*/ */
PreferenceVO getPreferenceByType(String type); Map<String, Object> getPreferenceByType(String type);
/** /**
* 获取用户偏好 * 获取用户偏好

View File

@@ -1,17 +1,18 @@
package com.orion.ops.module.infra.service.impl; package com.orion.ops.module.infra.service.impl;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.define.wrapper.Ref;
import com.orion.lang.function.Functions;
import com.orion.lang.utils.collect.Maps; import com.orion.lang.utils.collect.Maps;
import com.orion.ops.framework.common.utils.Valid; import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.framework.redis.core.utils.RedisStrings; import com.orion.ops.framework.redis.core.utils.RedisMaps;
import com.orion.ops.framework.security.core.utils.SecurityUtils; import com.orion.ops.framework.security.core.utils.SecurityUtils;
import com.orion.ops.module.infra.dao.PreferenceDAO; import com.orion.ops.module.infra.dao.PreferenceDAO;
import com.orion.ops.module.infra.define.cache.PreferenceCacheKeyDefine; import com.orion.ops.module.infra.define.cache.PreferenceCacheKeyDefine;
import com.orion.ops.module.infra.entity.domain.PreferenceDO; import com.orion.ops.module.infra.entity.domain.PreferenceDO;
import com.orion.ops.module.infra.entity.request.preference.PreferenceUpdatePartialRequest;
import com.orion.ops.module.infra.entity.request.preference.PreferenceUpdateRequest; import com.orion.ops.module.infra.entity.request.preference.PreferenceUpdateRequest;
import com.orion.ops.module.infra.entity.vo.PreferenceVO;
import com.orion.ops.module.infra.enums.PreferenceTypeEnum; import com.orion.ops.module.infra.enums.PreferenceTypeEnum;
import com.orion.ops.module.infra.service.PreferenceService; import com.orion.ops.module.infra.service.PreferenceService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -24,6 +25,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@@ -41,51 +43,73 @@ public class PreferenceServiceImpl implements PreferenceService {
private PreferenceDAO preferenceDAO; private PreferenceDAO preferenceDAO;
@Override @Override
public Integer updatePreference(PreferenceUpdateRequest request, boolean partial) { public Integer updatePreference(PreferenceUpdateRequest request) {
Long userId = SecurityUtils.getLoginUserId(); Long userId = SecurityUtils.getLoginUserId();
String type = request.getType(); String type = request.getType();
String item = request.getItem();
Valid.valid(PreferenceTypeEnum::of, type); Valid.valid(PreferenceTypeEnum::of, type);
// 查询 // 查询
PreferenceDO preference = preferenceDAO.of() PreferenceDO preference = preferenceDAO.of()
.wrapper(this.buildQueryWrapper(userId, type)) .createWrapper()
.eq(PreferenceDO::getUserId, userId)
.eq(PreferenceDO::getType, type)
.eq(PreferenceDO::getItem, item)
.then()
.getOne(); .getOne();
int effect; int effect;
if (preference == null) { if (preference == null) {
// 直接插入 // 插入
PreferenceDO insertRecord = new PreferenceDO(); PreferenceDO insertRecord = new PreferenceDO();
insertRecord.setUserId(userId); insertRecord.setUserId(userId);
insertRecord.setType(type); insertRecord.setType(type);
insertRecord.setConfig(JSON.toJSONString(request.getConfig())); insertRecord.setItem(item);
insertRecord.setValue(this.toJsonValue(request.getValue()));
effect = preferenceDAO.insert(insertRecord); effect = preferenceDAO.insert(insertRecord);
} else { } else {
// 更新 // 更新
PreferenceDO updateRecord = new PreferenceDO(); PreferenceDO updateRecord = new PreferenceDO();
updateRecord.setId(preference.getId()); updateRecord.setId(preference.getId());
if (partial) { updateRecord.setValue(this.toJsonValue(request.getValue()));
// 部分更新
JSONObject config = JSON.parseObject(preference.getConfig());
config.putAll(request.getConfig());
updateRecord.setConfig(JSON.toJSONString(config));
} else {
// 全部更新
updateRecord.setConfig(JSON.toJSONString(request.getConfig()));
}
effect = preferenceDAO.updateById(updateRecord); effect = preferenceDAO.updateById(updateRecord);
// 删除缓存
RedisStrings.delete(PreferenceCacheKeyDefine.PREFERENCE.format(userId, type));
} }
// 删除缓存
RedisMaps.delete(PreferenceCacheKeyDefine.PREFERENCE.format(userId, type));
return effect; return effect;
} }
@Override @Override
public PreferenceVO getPreferenceByType(String type) { public void updatePreferencePartial(PreferenceUpdatePartialRequest request) {
Long userId = SecurityUtils.getLoginUserId();
String type = request.getType();
Map<String, Object> config = request.getConfig();
Valid.valid(PreferenceTypeEnum::of, type);
// 删除配置
LambdaQueryWrapper<PreferenceDO> wrapper = preferenceDAO.lambda()
.eq(PreferenceDO::getUserId, userId)
.eq(PreferenceDO::getType, type)
.in(PreferenceDO::getItem, config.keySet());
preferenceDAO.delete(wrapper);
// 插入配置
List<PreferenceDO> records = config.entrySet()
.stream()
.map(s -> {
PreferenceDO insertRecord = new PreferenceDO();
insertRecord.setUserId(userId);
insertRecord.setType(type);
insertRecord.setItem(s.getKey());
insertRecord.setValue(this.toJsonValue(s.getValue()));
return insertRecord;
}).collect(Collectors.toList());
preferenceDAO.insertBatch(records);
// 删除缓存
RedisMaps.delete(PreferenceCacheKeyDefine.PREFERENCE.format(userId, type));
}
@Override
public Map<String, Object> getPreferenceByType(String type) {
Long userId = SecurityUtils.getLoginUserId(); Long userId = SecurityUtils.getLoginUserId();
PreferenceTypeEnum typeEnum = Valid.valid(PreferenceTypeEnum::of, type); PreferenceTypeEnum typeEnum = Valid.valid(PreferenceTypeEnum::of, type);
Map<String, Object> config = this.getPreferenceByCache(userId, typeEnum); return this.getPreferenceByCache(userId, typeEnum);
// 返回
return PreferenceVO.builder()
.config(config)
.build();
} }
@Override @Override
@@ -104,7 +128,7 @@ public class PreferenceServiceImpl implements PreferenceService {
List<String> deleteKeys = Arrays.stream(PreferenceTypeEnum.values()) List<String> deleteKeys = Arrays.stream(PreferenceTypeEnum.values())
.map(s -> PreferenceCacheKeyDefine.PREFERENCE.format(userId, s)) .map(s -> PreferenceCacheKeyDefine.PREFERENCE.format(userId, s))
.collect(Collectors.toList()); .collect(Collectors.toList());
RedisStrings.delete(deleteKeys); RedisMaps.delete(deleteKeys);
} }
/** /**
@@ -118,47 +142,58 @@ public class PreferenceServiceImpl implements PreferenceService {
String typeValue = type.getType(); String typeValue = type.getType();
// 查询缓存 用 string 防止数据类型丢失 // 查询缓存 用 string 防止数据类型丢失
String key = PreferenceCacheKeyDefine.PREFERENCE.format(userId, type); String key = PreferenceCacheKeyDefine.PREFERENCE.format(userId, type);
Map<String, Object> config = RedisStrings.getJson(key); Map<String, String> config = RedisMaps.entities(key);
boolean setCache = Maps.isEmpty(config); boolean setCache = Maps.isEmpty(config);
// 查询数据库 // 查询数据库
if (Maps.isEmpty(config)) { if (Maps.isEmpty(config)) {
config = preferenceDAO.of() config = preferenceDAO.of()
.wrapper(this.buildQueryWrapper(userId, typeValue)) .createWrapper()
.optionalOne() .eq(PreferenceDO::getUserId, userId)
.map(PreferenceDO::getConfig) .eq(PreferenceDO::getType, type)
.map(JSON::parseObject) .then()
.orElse(null); .stream()
.collect(Collectors.toMap(
PreferenceDO::getItem,
PreferenceDO::getValue,
Functions.right())
);
} }
// 初始化 // 初始化
if (Maps.isEmpty(config)) { if (Maps.isEmpty(config)) {
// 获取默认值
config = type.getStrategy() config = type.getStrategy()
.getDefault() .getDefault()
.toMap(); .toMap();
// 插入 // 插入默认值
PreferenceDO entity = new PreferenceDO(); List<PreferenceDO> entities = config
entity.setUserId(userId); .entrySet()
entity.setType(typeValue); .stream()
entity.setConfig(JSON.toJSONString(config)); .map(s -> {
preferenceDAO.insert(entity); PreferenceDO entity = new PreferenceDO();
entity.setUserId(userId);
entity.setType(typeValue);
entity.setItem(s.getKey());
entity.setValue(s.getValue());
return entity;
}).collect(Collectors.toList());
preferenceDAO.insertBatch(entities);
} }
// 设置缓存 // 设置缓存
if (setCache) { if (setCache) {
RedisStrings.setJson(key, PreferenceCacheKeyDefine.PREFERENCE, config); RedisMaps.putAll(key, PreferenceCacheKeyDefine.PREFERENCE, config);
} }
return config; // unref
return Maps.map(config, Function.identity(), v -> JSON.parseObject(v, Ref.class).getValue());
} }
/** /**
* 构建查询 wrapper * 转为 json 对象
* *
* @param userId userId * @param o o
* @param type type * @return value
* @return wrapper
*/ */
private LambdaQueryWrapper<PreferenceDO> buildQueryWrapper(Long userId, String type) { private String toJsonValue(Object o) {
return preferenceDAO.wrapper() return JSON.toJSONString(Ref.of(o));
.eq(PreferenceDO::getUserId, userId)
.eq(PreferenceDO::getType, type);
} }
} }

View File

@@ -12,12 +12,13 @@
<result column="deleted" property="deleted"/> <result column="deleted" property="deleted"/>
<result column="user_id" property="userId"/> <result column="user_id" property="userId"/>
<result column="type" property="type"/> <result column="type" property="type"/>
<result column="config" property="config"/> <result column="item" property="item"/>
<result column="value" property="value"/>
</resultMap> </resultMap>
<!-- 通用查询结果列 --> <!-- 通用查询结果列 -->
<sql id="Base_Column_List"> <sql id="Base_Column_List">
id, user_id, type, config, create_time, update_time, creator, updater, deleted id, user_id, type, item, value, create_time, update_time, creator, updater, deleted
</sql> </sql>
</mapper> </mapper>

View File

@@ -1,24 +1,26 @@
import axios from 'axios'; import axios from 'axios';
type Preference = 'SYSTEM' | 'TERMINAL' type PreferenceType = 'SYSTEM' | 'TERMINAL'
/** /**
* 用户偏好更新请求 * 用户偏好更新请求-单个
*/ */
export interface PreferenceUpdateRequest { export interface PreferenceUpdateRequest {
type: Preference; type: PreferenceType;
config: object; item: string;
value: any;
} }
/** /**
* 用户偏好查询响应 * 用户偏好更新请求-部分
*/ */
export interface PreferenceQueryResponse<T> { export interface PreferenceUpdatePartialRequest {
config: T; type: PreferenceType;
config: Record<string, any> | object;
} }
/** /**
* 更新用户偏好-整体 * 更新用户偏好-单个
*/ */
export function updatePreference(request: PreferenceUpdateRequest) { export function updatePreference(request: PreferenceUpdateRequest) {
return axios.put('/infra/preference/update', request); return axios.put('/infra/preference/update', request);
@@ -27,14 +29,14 @@ export function updatePreference(request: PreferenceUpdateRequest) {
/** /**
* 更新用户偏好-部分 * 更新用户偏好-部分
*/ */
export function updatePreferencePartial(request: PreferenceUpdateRequest) { export function updatePreferencePartial(request: PreferenceUpdatePartialRequest) {
return axios.put('/infra/preference/update-partial', request); return axios.put('/infra/preference/update-partial', request);
} }
/** /**
* 查询用户偏好 * 查询用户偏好
*/ */
export function getPreference<T>(type: Preference) { export function getPreference<T>(type: PreferenceType) {
return axios.get<PreferenceQueryResponse<T>>('/infra/preference/get', { params: { type } }); return axios.get<T>('/infra/preference/get', { params: { type } });
} }

View File

@@ -24,8 +24,7 @@
import type { SelectOption } from '@arco-design/web-vue/es/select/interface'; import type { SelectOption } from '@arco-design/web-vue/es/select/interface';
import { useAppStore } from '@/store'; import { useAppStore } from '@/store';
import FormWrapper from './form-wrapper.vue'; import FormWrapper from './form-wrapper.vue';
import { updatePreferencePartial } from '@/api/user/preference'; import { updatePreference } from '@/api/user/preference';
import { Message } from '@arco-design/web-vue';
interface OptionsProps { interface OptionsProps {
name: string; name: string;
@@ -61,20 +60,15 @@
}); });
} }
// 修改配置 // 修改配置
const updateConfig = { [key]: value }; appStore.updateSettings({ [key]: value });
appStore.updateSettings(updateConfig);
// 同步偏好 // 同步偏好
Message.clear();
const loading = Message.loading('同步中...');
try { try {
await updatePreferencePartial({ await updatePreference({
type: 'SYSTEM', type: 'SYSTEM',
config: updateConfig item: key,
value
}); });
Message.success('同步成功');
} catch (e) { } catch (e) {
} finally {
loading.close();
} }
}; };
</script> </script>

View File

@@ -1,8 +1,8 @@
import type { TerminalDisplaySetting, TerminalPreference, TerminalState, TerminalThemeSchema } from './types'; import type { TerminalDisplaySetting, TerminalPreference, TerminalState, TerminalThemeSchema } from './types';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { getPreference, updatePreferencePartial } from '@/api/user/preference'; import { getPreference, updatePreference } from '@/api/user/preference';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import { useDark, useDebounceFn } from '@vueuse/core'; import { useDark } from '@vueuse/core';
import { DEFAULT_SCHEMA } from '@/views/host-ops/terminal/types/terminal.theme'; import { DEFAULT_SCHEMA } from '@/views/host-ops/terminal/types/terminal.theme';
// 暗色主题 // 暗色主题
@@ -36,14 +36,14 @@ export default defineStore('terminal', {
try { try {
const { data } = await getPreference<TerminalPreference>('TERMINAL'); const { data } = await getPreference<TerminalPreference>('TERMINAL');
// 设置默认终端主题 // 设置默认终端主题
if (!data.config.themeSchema?.name) { if (!data.themeSchema?.name) {
data.config.themeSchema = DEFAULT_SCHEMA; data.themeSchema = DEFAULT_SCHEMA;
} }
this.preference = data.config; this.preference = data;
// 设置暗色主题 // 设置暗色主题
const userDarkTheme = data.config.darkTheme; const userDarkTheme = data.darkTheme;
if (userDarkTheme === DarkTheme.AUTO) { if (userDarkTheme === DarkTheme.AUTO) {
this.isDarkTheme = data.config.themeSchema?.dark === true; this.isDarkTheme = data.themeSchema?.dark === true;
} else { } else {
this.isDarkTheme = userDarkTheme === DarkTheme.DARK; this.isDarkTheme = userDarkTheme === DarkTheme.DARK;
} }
@@ -53,7 +53,7 @@ export default defineStore('terminal', {
}, },
// 修改暗色主题 // 修改暗色主题
changeDarkTheme(darkTheme: string) { async changeDarkTheme(darkTheme: string) {
this.preference.darkTheme = darkTheme; this.preference.darkTheme = darkTheme;
if (darkTheme === DarkTheme.DARK) { if (darkTheme === DarkTheme.DARK) {
// 暗色 // 暗色
@@ -66,53 +66,47 @@ export default defineStore('terminal', {
this.isDarkTheme = this.preference.themeSchema.dark; this.isDarkTheme = this.preference.themeSchema.dark;
} }
// 同步配置 // 同步配置
this.updateTerminalPreference(); await this.updateTerminalPreference('darkTheme', darkTheme);
}, },
// 修改显示配置 // 修改显示配置
changeDisplaySetting(displaySetting: TerminalDisplaySetting) { async changeDisplaySetting(displaySetting: TerminalDisplaySetting) {
this.preference.displaySetting = displaySetting; this.preference.displaySetting = displaySetting;
// 同步配置 // 同步配置
this.updateTerminalPreference(); await this.updateTerminalPreference('displaySetting', displaySetting);
}, },
// 选择终端主题 // 选择终端主题
changeThemeSchema(themeSchema: TerminalThemeSchema) { async changeThemeSchema(themeSchema: TerminalThemeSchema) {
this.preference.themeSchema = themeSchema; this.preference.themeSchema = themeSchema;
// 切换主题配色 // 切换主题配色
if (this.preference.darkTheme === DarkTheme.AUTO) { if (this.preference.darkTheme === DarkTheme.AUTO) {
this.isDarkTheme = themeSchema.dark; this.isDarkTheme = themeSchema.dark;
} }
// 同步配置 // 同步配置
this.updateTerminalPreference(); await this.updateTerminalPreference('themeSchema', themeSchema);
}, },
// 切换新建连接类型 // 切换新建连接类型
changeNewConnectionType(newConnectionType: string) { async changeNewConnectionType(newConnectionType: string) {
this.preference.newConnectionType = newConnectionType; this.preference.newConnectionType = newConnectionType;
// 同步配置 // 同步配置
this.updateTerminalPreference(); await this.updateTerminalPreference('newConnectionType', newConnectionType);
}, },
// 更新终端偏好-防抖 // 更新终端偏好-防抖
updateTerminalPreference() { async updateTerminalPreference(item: string, value: any) {
// 初始化函数 try {
if (!this.updateTerminalPreferenceFn) { // 修改配置
this.updateTerminalPreferenceFn = useDebounceFn(async () => { await updatePreference({
try { type: 'TERMINAL',
// 修改配置 item,
await updatePreferencePartial({ value
type: 'TERMINAL', });
config: this.preference } catch (e) {
}); Message.error('同步失败');
} catch (e) {
Message.error('同步失败');
}
}, 1500);
} }
// 更新
this.updateTerminalPreferenceFn();
} }
}, },
}); });

View File

@@ -3,8 +3,6 @@ import type { Ref } from 'vue';
export interface TerminalState { export interface TerminalState {
isDarkTheme: Ref<boolean>; isDarkTheme: Ref<boolean>;
preference: TerminalPreference; preference: TerminalPreference;
updateTerminalPreferenceFn?: () => void;
} }
// 终端配置 // 终端配置

View File

@@ -10,7 +10,7 @@
<!-- 新建连接 --> <!-- 新建连接 -->
<new-connection-view v-if="tab.key === InnerTabs.NEW_CONNECTION.key" /> <new-connection-view v-if="tab.key === InnerTabs.NEW_CONNECTION.key" />
<!-- 显示设置 --> <!-- 显示设置 -->
<terminal-view-setting v-else-if="tab.key === InnerTabs.THEME_SETTING.key" /> <terminal-view-setting v-else-if="tab.key === InnerTabs.VIEW_SETTING.key" />
<span v-else> <span v-else>
{{ tab.key }} {{ tab.key }}
{{ tab.title }} {{ tab.title }}

View File

@@ -55,8 +55,6 @@
import { useFullscreen } from '@vueuse/core'; import { useFullscreen } from '@vueuse/core';
import { computed } from 'vue'; import { computed } from 'vue';
import IconActions from '../layout/icon-actions.vue'; import IconActions from '../layout/icon-actions.vue';
import { useTerminalStore } from '@/store';
import { DarkTheme } from '@/store/modules/terminal';
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@@ -72,7 +70,6 @@
const emits = defineEmits(['update:modelValue', 'clickTab', 'deleteTab', 'share']); const emits = defineEmits(['update:modelValue', 'clickTab', 'deleteTab', 'share']);
const { isFullscreen, toggle: toggleFullScreen } = useFullscreen(); const { isFullscreen, toggle: toggleFullScreen } = useFullscreen();
const terminalStore = useTerminalStore();
// 顶部操作 // 顶部操作
const actions = computed<Array<SidebarAction>>(() => [ const actions = computed<Array<SidebarAction>>(() => [
@@ -82,11 +79,6 @@
visible: false, visible: false,
click: () => emits('share') click: () => emits('share')
}, },
{
icon: terminalStore.isDarkTheme ? 'icon-sun-fill' : 'icon-moon-fill',
content: terminalStore.isDarkTheme ? '点击切换为亮色模式' : '点击切换为暗色模式',
click: () => terminalStore.changeDarkTheme(terminalStore.isDarkTheme ? DarkTheme.LIGHT : DarkTheme.DARK)
},
{ {
icon: isFullscreen.value ? 'icon-fullscreen-exit' : 'icon-fullscreen', icon: isFullscreen.value ? 'icon-fullscreen-exit' : 'icon-fullscreen',
content: isFullscreen.value ? '点击退出全屏模式' : '点击切换全屏模式', content: isFullscreen.value ? '点击退出全屏模式' : '点击切换全屏模式',
@@ -113,7 +105,7 @@
.terminal-header { .terminal-header {
--logo-width: 168px; --logo-width: 168px;
--right-avatar-width: calc(28px * 5 - 7px * 4); --right-avatar-width: calc(28px * 5 - 7px * 4);
--right-action-width: calc(var(--header-height) * 3); --right-action-width: calc(var(--sidebar-icon-wrapper-size) * 2);
} }
.terminal-header { .terminal-header {

View File

@@ -43,7 +43,7 @@
{ {
icon: 'icon-palette', icon: 'icon-palette',
content: '外观设置', content: '外观设置',
click: () => emits('switchTab', InnerTabs.THEME_SETTING) click: () => emits('switchTab', InnerTabs.VIEW_SETTING)
}, },
]; ];

View File

@@ -20,11 +20,16 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { SidebarAction } from '../../types/terminal.const'; import type { SidebarAction } from '../../types/terminal.const';
import IconActions from './icon-actions.vue'; import IconActions from './icon-actions.vue';
import { computed } from 'vue';
import { useTerminalStore } from '@/store';
import { DarkTheme } from '@/store/modules/terminal';
const emits = defineEmits(['openSnippet', 'openSftp', 'openTransfer', 'openHistory', 'screenshot']); const emits = defineEmits(['openSnippet', 'openSftp', 'openTransfer', 'openHistory', 'screenshot']);
const terminalStore = useTerminalStore();
// 顶部操作 // 顶部操作
const topActions: Array<SidebarAction> = [ const topActions = computed<Array<SidebarAction>>(() => [
{ {
icon: 'icon-code-block', icon: 'icon-code-block',
content: '打开命令片段', content: '打开命令片段',
@@ -49,7 +54,12 @@
visible: false, visible: false,
click: () => emits('openHistory') click: () => emits('openHistory')
}, },
]; {
icon: terminalStore.isDarkTheme ? 'icon-sun-fill' : 'icon-moon-fill',
content: terminalStore.isDarkTheme ? '点击切换为亮色模式' : '点击切换为暗色模式',
click: () => terminalStore.changeDarkTheme(terminalStore.isDarkTheme ? DarkTheme.LIGHT : DarkTheme.DARK)
},
]);
// 底部操作 // 底部操作
const bottomActions: Array<SidebarAction> = [ const bottomActions: Array<SidebarAction> = [

View File

@@ -6,12 +6,12 @@
主题设置 主题设置
</h3> </h3>
<!-- 暗色选择 --> <!-- 暗色选择 -->
<a-radio-group :default-value="preference.darkTheme" <a-radio-group v-model="preference.darkTheme"
class="usn" class="usn"
size="mini" size="mini"
type="button" type="button"
@change="changeDarkTheme" :options="toOptions(darkThemeKey)"
:options="toOptions(darkThemeKey)"> @change="changeDarkTheme">
</a-radio-group> </a-radio-group>
</div> </div>
<!-- 内容区域 --> <!-- 内容区域 -->

View File

@@ -36,9 +36,9 @@ export const InnerTabs = {
title: '快捷键设置', title: '快捷键设置',
type: TabType.SETTING type: TabType.SETTING
}, },
THEME_SETTING: { VIEW_SETTING: {
key: 'themeSetting', key: 'viewSetting',
title: '主题设置', title: '外观设置',
type: TabType.SETTING type: TabType.SETTING
}, },
}; };