Compare commits

..

9 Commits

Author SHA1 Message Date
longguancheng
27dfb2d878 Merge branch 'refs/heads/main' into dev_20260130_RemoveRedis
# Conflicts:
#	backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/jm/controller/JmImageController.java
#	backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/jm/service/impl/JmJobStatusServiceImpl.java
#	backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/controller/SysUserController.java
2026-03-05 16:22:25 +08:00
Andy Yang
1031ac1578 bug: (no ticket tracked) start a created job, the backend won't check whether sar is on or off 2026-03-04 17:29:13 +08:00
1ee4a59f7d feat: fix i18n string in service implementation 2026-03-04 16:53:18 +08:00
2c50a37610 feat: do controller layer i18n 2026-03-04 15:35:43 +08:00
Andy Yang
ed5ac67942 feat: add service manager logic to use i18n 2026-03-03 17:37:55 +08:00
Andy Yang
6e61714019 add i18n feature similar as ruoyi-plus 2026-03-03 17:35:24 +08:00
8a37ad4428 feat: add image enhancer to handle Low Dynamic Range (LDR) and Local Contrast Imbalance 2026-03-03 10:31:03 +08:00
wxs
32d8f2e5fb Merge branch 'main' of http://182.92.203.107:3000/libingkun/skyeyesystem 2026-03-02 14:10:26 +08:00
wxs
859be27901 feat:增加国际化处理 2026-03-02 14:09:49 +08:00
26 changed files with 1313 additions and 584 deletions

View File

@ -0,0 +1,21 @@
package com.zhangy.skyeye.common.extend.config;
import com.zhangy.skyeye.common.extend.core.I18nLocaleResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
/**
* 国际化配置
*
* @author zhangy
*/
@Configuration
public class I18nConfig {
@Bean
public LocaleResolver localeResolver() {
return new I18nLocaleResolver();
}
}

View File

@ -0,0 +1,35 @@
package com.zhangy.skyeye.common.extend.core;
import org.springframework.web.servlet.LocaleResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
/**
* 获取请求头国际化信息
*
* @author zhangy
*/
public class I18nLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
String language = httpServletRequest.getHeader("content-language");
Locale locale = Locale.ENGLISH;
if (language != null && language.length() > 0) {
String[] split = language.split("_");
if (split.length > 1) {
locale = new Locale(split[0], split[1]);
} else {
locale = new Locale(split[0]);
}
}
return locale;
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
}

View File

@ -0,0 +1,31 @@
package com.zhangy.skyeye.common.extend.util;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.i18n.LocaleContextHolder;
import java.util.Locale;
/**
* 获取i18n资源文件
*
* @author zhangy
*/
public class MessageUtils {
/**
* 根据消息键和参数 获取消息 委托给spring messageSource
*
* @param code 消息键
* @param args 参数
* @return 获取国际化翻译值
*/
public static String message(String code, Object... args) {
MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
try {
return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
} catch (NoSuchMessageException e) {
return code;
}
}
}

View File

@ -0,0 +1,66 @@
package com.zhangy.skyeye.common.extend.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* spring工具类 方便在非spring管理环境中获取bean
*
* @author zhangy
*/
@Component
public class SpringUtils implements ApplicationContextAware {
/**
* 当前IOC上下文对象
*/
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringUtils.applicationContext = applicationContext;
}
/**
* 获取applicationContext
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 通过name获取 Bean.
*
* @param name bean名称
* @return bean对象
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 通过class获取Bean.
*
* @param clazz bean类型
* @param <T> 泛型
* @return bean对象
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* 通过name,以及Clazz返回指定的Bean
*
* @param name bean名称
* @param clazz bean类型
* @param <T> 泛型
* @return bean对象
*/
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}

View File

@ -1,5 +1,6 @@
package com.zhangy.skyeye.device.controller; package com.zhangy.skyeye.device.controller;
import com.zhangy.skyeye.common.extend.util.MessageUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -77,6 +78,6 @@ public class UavController {
@PostMapping("/remove") @PostMapping("/remove")
public Object delete(@RequestBody Long... id) { public Object delete(@RequestBody Long... id) {
uavService.delete(id); uavService.delete(id);
return "操作成功"; return MessageUtils.message("device.uav.remove.success");
} }
} }

View File

@ -3,6 +3,7 @@ package com.zhangy.skyeye.jm.controller;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import com.zhangy.skyeye.common.extend.anno.IgnoreAuth; import com.zhangy.skyeye.common.extend.anno.IgnoreAuth;
import com.zhangy.skyeye.common.extend.exception.ServiceException; import com.zhangy.skyeye.common.extend.exception.ServiceException;
import com.zhangy.skyeye.common.extend.util.MessageUtils;
import com.zhangy.skyeye.jm.dto.JmImageKtyDTO; import com.zhangy.skyeye.jm.dto.JmImageKtyDTO;
import com.zhangy.skyeye.jm.dto.JmImagePageDTO; import com.zhangy.skyeye.jm.dto.JmImagePageDTO;
import com.zhangy.skyeye.jm.dto.JmImageUpdDTO; import com.zhangy.skyeye.jm.dto.JmImageUpdDTO;
@ -64,7 +65,7 @@ public class JmImageController {
public Object selectList(@Valid @RequestBody JmImageUpdDTO param) { public Object selectList(@Valid @RequestBody JmImageUpdDTO param) {
JmImage e = BeanUtil.copyProperties(param, JmImage.class); JmImage e = BeanUtil.copyProperties(param, JmImage.class);
sarImageService.updateNotNull(e); sarImageService.updateNotNull(e);
return "操作成功"; return MessageUtils.message("sar.image.update.success");
} }
/** /**
@ -73,14 +74,17 @@ public class JmImageController {
@PostMapping("/addHigh") @PostMapping("/addHigh")
public Object addHighImage(@Valid @RequestBody JmJobImageDTO dto) { public Object addHighImage(@Valid @RequestBody JmJobImageDTO dto) {
String lockKey = SAR_IMAGE_UPLOAD_LOCK; String lockKey = SAR_IMAGE_UPLOAD_LOCK;
boolean locked = localLockUtil.tryLock(lockKey, 60); // 尝试 60 秒获取锁比原来 1 分钟更灵活 // 尝试 60 秒获取锁比原来 1 分钟更灵活
boolean locked = localLockUtil.tryLock(lockKey, 60);
if (!locked) { if (!locked) {
throw ServiceException.noLog("正在上传其它图像,请稍后重试!"); throw ServiceException.noLog("正在上传其它图像,请稍后重试!");
} }
try { try {
// 执行高精度图像添加逻辑
sarImageService.addHighImage(dto); sarImageService.addHighImage(dto);
} finally { } finally {
localLockUtil.unlock(lockKey); // 释放锁 // 释放锁
localLockUtil.unlock(lockKey);
} }
return "操作完成"; return "操作完成";
} }
@ -99,7 +103,7 @@ public class JmImageController {
@PostMapping("/remove") @PostMapping("/remove")
public Object delete(@RequestBody Long... id) { public Object delete(@RequestBody Long... id) {
sarImageService.delete(id); sarImageService.delete(id);
return "操作完成"; return MessageUtils.message("sar.image.remove.success");
} }
// 空天院只查询sar低精度图 // 空天院只查询sar低精度图

View File

@ -5,6 +5,7 @@ import com.zhangy.skyeye.common.extend.anno.IgnoreAuth;
import com.zhangy.skyeye.common.extend.enums.EnumUtil; import com.zhangy.skyeye.common.extend.enums.EnumUtil;
import com.zhangy.skyeye.common.extend.exception.ServiceException; import com.zhangy.skyeye.common.extend.exception.ServiceException;
import com.zhangy.skyeye.common.extend.util.DateUtil; import com.zhangy.skyeye.common.extend.util.DateUtil;
import com.zhangy.skyeye.common.extend.util.MessageUtils;
import com.zhangy.skyeye.common.pojo.result.Result; import com.zhangy.skyeye.common.pojo.result.Result;
import com.zhangy.skyeye.jm.consts.JmJobModeEnum; import com.zhangy.skyeye.jm.consts.JmJobModeEnum;
import com.zhangy.skyeye.jm.dto.JmJobDTO; import com.zhangy.skyeye.jm.dto.JmJobDTO;
@ -23,6 +24,7 @@ import org.springframework.web.bind.annotation.*;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.util.Collections; import java.util.Collections;
import java.util.Objects;
@Validated @Validated
@RestController @RestController
@ -114,7 +116,10 @@ public class JmJobController {
* 重飞 * 重飞
*/ */
@GetMapping("/retry") @GetMapping("/retry")
public Object retry(@NotNull(message = "任务ID不能为空") Long id) { public Object retry(Long id) {
if(Objects.isNull(id)) {
return MessageUtils.message("sar.job.retry.enullid");
}
JmJobDTO e = jobService.selectDetail(id); JmJobDTO e = jobService.selectDetail(id);
clearId(e); clearId(e);
e.setName(DateUtil.getTimeStr(DateUtil.DF_2)); e.setName(DateUtil.getTimeStr(DateUtil.DF_2));
@ -135,12 +140,12 @@ public class JmJobController {
// 查询执行任务的无人机 // 查询执行任务的无人机
JmJobDTO job = jobService.selectDetail(id); JmJobDTO job = jobService.selectDetail(id);
if (job == null) { if (job == null) {
throw ServiceException.noLog("找不到任务id=" + id); throw ServiceException.noLog(MessageUtils.message("sar.job.start.enosuchjob", id));
} else if (EnumUtil.parseEx(ExecStatusEnum.class, job.getStatus()) == ExecStatusEnum.PROCESSING) { } else if (EnumUtil.parseEx(ExecStatusEnum.class, job.getStatus()) == ExecStatusEnum.PROCESSING) {
throw ServiceException.noLog("任务状态为执行中,无法起飞"); throw ServiceException.noLog(MessageUtils.message("sar.job.start.einexecuting"));
} }
jobService.start(job); jobService.start(job);
return "开始执行"; return MessageUtils.message("sar.job.start.success");
} }
/** /**
@ -177,7 +182,7 @@ public class JmJobController {
@GetMapping("/exit") @GetMapping("/exit")
public Object exit(@RequestParam Long id) { public Object exit(@RequestParam Long id) {
jobService.exit(id); jobService.exit(id);
return "操作成功"; return MessageUtils.message("sar.job.exit.success");
} }
/** /**

View File

@ -1,6 +1,7 @@
package com.zhangy.skyeye.jm.controller; package com.zhangy.skyeye.jm.controller;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.zhangy.skyeye.common.extend.util.MessageUtils;
import com.zhangy.skyeye.jm.dto.JmJobDTO; import com.zhangy.skyeye.jm.dto.JmJobDTO;
import com.zhangy.skyeye.jm.dto.JmJobPageDTO; import com.zhangy.skyeye.jm.dto.JmJobPageDTO;
import com.zhangy.skyeye.jm.entity.JmImage; import com.zhangy.skyeye.jm.entity.JmImage;
@ -60,6 +61,6 @@ public class JmJobExecController {
@RequestMapping("/brightness") @RequestMapping("/brightness")
public Object adjustBrightness(@Valid @RequestBody JmImage param) { public Object adjustBrightness(@Valid @RequestBody JmImage param) {
jobExecService.adjustBrightness(param); jobExecService.adjustBrightness(param);
return "成功调整亮度"; return MessageUtils.message("sar.job_exec.brightness.success");
} }
} }

View File

@ -0,0 +1,23 @@
package com.zhangy.skyeye.jm.controller;
import com.zhangy.skyeye.common.extend.anno.IgnoreAuth;
import com.zhangy.skyeye.common.extend.util.MessageUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* i18n 测试
*
* @author zhangy
*/
@RestController
@RequestMapping("/test/i18n")
public class TestI18nController {
@IgnoreAuth
@GetMapping
public String test(String code) {
return MessageUtils.message(code);
}
}

View File

@ -1,6 +1,8 @@
package com.zhangy.skyeye.jm.service.impl; package com.zhangy.skyeye.jm.service.impl;
import com.zhangy.skyeye.cache.sar.SarCache;
import com.zhangy.skyeye.common.extend.exception.ServiceException; import com.zhangy.skyeye.common.extend.exception.ServiceException;
import com.zhangy.skyeye.common.extend.util.MessageUtils;
import com.zhangy.skyeye.jm.dto.*; import com.zhangy.skyeye.jm.dto.*;
import com.zhangy.skyeye.jm.entity.JmJobPayload; import com.zhangy.skyeye.jm.entity.JmJobPayload;
import com.zhangy.skyeye.jm.entity.JmJobUav; import com.zhangy.skyeye.jm.entity.JmJobUav;
@ -9,6 +11,7 @@ import com.zhangy.skyeye.jm.event.JmJobStatusEvent;
import com.zhangy.skyeye.jm.event.JmPayloadStatusEvent; import com.zhangy.skyeye.jm.event.JmPayloadStatusEvent;
import com.zhangy.skyeye.jm.service.JmJobStatusService; import com.zhangy.skyeye.jm.service.JmJobStatusService;
import com.zhangy.skyeye.jm.service.JmStatusLogService; import com.zhangy.skyeye.jm.service.JmStatusLogService;
import com.zhangy.skyeye.publics.consts.CacheKey;
import com.zhangy.skyeye.publics.consts.ExecStatusEnum; import com.zhangy.skyeye.publics.consts.ExecStatusEnum;
import com.zhangy.skyeye.publics.consts.WebSocketKey; import com.zhangy.skyeye.publics.consts.WebSocketKey;
import com.zhangy.skyeye.publics.utils.CoordUtil; import com.zhangy.skyeye.publics.utils.CoordUtil;
@ -42,6 +45,12 @@ public class JmJobStatusServiceImpl implements JmJobStatusService {
@Autowired @Autowired
private SarImageUdpProcessor imageProcessService; private SarImageUdpProcessor imageProcessService;
@Autowired
private SarCache sarCache;
// @Autowired
// private ISmpSubscriptService subscriptService;
/** /**
* 缓存所有执行中的任务的状态 key 任务配置IDvalue 任务状态 * 缓存所有执行中的任务的状态 key 任务配置IDvalue 任务状态
*/ */
@ -170,11 +179,15 @@ public class JmJobStatusServiceImpl implements JmJobStatusService {
// 校验新任务中的无人机和载荷是否冲突 // 校验新任务中的无人机和载荷是否冲突
for (JmJobUav newUav : newJobUavs) { for (JmJobUav newUav : newJobUavs) {
if (runningUavIds.contains(newUav.getUavId())) { if (runningUavIds.contains(newUav.getUavId())) {
throw ServiceException.warnLog("无人机正在任务中,无法执行新任务"); throw ServiceException.warnLog(MessageUtils.message("sar.control.turnon.euavinexec"));
} }
for (JmJobPayload payload : newUav.getPayloadList()) { for (JmJobPayload payload : newUav.getPayloadList()) {
// 校验雷达连接状态
if (!sarCache.isConnected(payload.getIp())) {
throw ServiceException.warnLog(MessageUtils.message("device.sar.offline", payload.getPayloadName()));
}
if (runningPayloadIds.contains(payload.getPayloadId())) { if (runningPayloadIds.contains(payload.getPayloadId())) {
throw ServiceException.warnLog("载荷正在任务中,无法执行新任务"); throw ServiceException.warnLog(MessageUtils.message("sar.control.turnon.esarinexec"));
} }
} }
} }
@ -281,11 +294,11 @@ public class JmJobStatusServiceImpl implements JmJobStatusService {
} }
endAirline(jobId, jobVo, uavVo, airlineVo, wsVo, nextAirline == null); endAirline(jobId, jobVo, uavVo, airlineVo, wsVo, nextAirline == null);
} else if (isBoot == 1 && aStatus == ExecStatusEnum.PROCESSING && overButSending) { } else if (isBoot == 1 && aStatus == ExecStatusEnum.PROCESSING && overButSending) {
// 前一航线的图片没传完就开始了新航线 // 前一航线的图片没传完就开始了新航线
endAirline(jobId, jobVo, uavVo, airlineVo, wsVo, nextAirline == null); endAirline(jobId, jobVo, uavVo, airlineVo, wsVo, nextAirline == null);
if (nextAirline != null) { if (nextAirline != null) {
startAirline(nextAirline, wsVo); startAirline(nextAirline, wsVo);
} }
} // else { } // else {
// return; // return;
// } // }

View File

@ -6,9 +6,11 @@ import com.zhangy.skyeye.common.extend.anno.IgnoreAuth;
import com.zhangy.skyeye.common.extend.anno.OperationLog; import com.zhangy.skyeye.common.extend.anno.OperationLog;
import com.zhangy.skyeye.common.extend.dto.PageDTO; import com.zhangy.skyeye.common.extend.dto.PageDTO;
import com.zhangy.skyeye.common.extend.exception.ServiceException; import com.zhangy.skyeye.common.extend.exception.ServiceException;
import com.zhangy.skyeye.common.extend.util.MessageUtils;
import com.zhangy.skyeye.common.extend.util.ObjectUtil; import com.zhangy.skyeye.common.extend.util.ObjectUtil;
import com.zhangy.skyeye.common.pojo.result.Result; import com.zhangy.skyeye.common.pojo.result.Result;
import com.zhangy.skyeye.common.utils.JwtUtil; import com.zhangy.skyeye.common.utils.JwtUtil;
import com.zhangy.skyeye.redis.utils.RedisUtil;
import com.zhangy.skyeye.publics.consts.CacheKey; import com.zhangy.skyeye.publics.consts.CacheKey;
import com.zhangy.skyeye.publics.dto.RegisterDTO; import com.zhangy.skyeye.publics.dto.RegisterDTO;
import com.zhangy.skyeye.publics.dto.SysUserPwdDTO; import com.zhangy.skyeye.publics.dto.SysUserPwdDTO;
@ -20,13 +22,11 @@ import com.zhangy.skyeye.publics.utils.SecurityUtil;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.List; import java.util.List;
import java.util.Objects;
@Validated @Validated
@RestController @RestController
@ -35,120 +35,120 @@ import java.util.Objects;
@RequestMapping("/user") @RequestMapping("/user")
public class SysUserController { public class SysUserController {
@Autowired @Autowired
private ISysUserService sysUserService; private ISysUserService sysUserService;
@Autowired @Autowired
private CacheManager cacheManager; private RedisUtil redisUtil;
/** /** token失效时间 */
* 登录接口 private final int TOKEN_EXPIRE = 60 * 60 * 24;
*
* @return
*/
@IgnoreAuth
@OperationLog(value = "登录", type = SysLog.TYPE_USER)
@PostMapping("/login")
public Result login(@RequestBody RegisterDTO registerDTO) {
SysUser user = sysUserService.selectByAccount(registerDTO.getUsername(), registerDTO.getPassword().toLowerCase());
if (user == null) {
throw ServiceException.noLog("用户名或密码错误!");
}
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
String payload = JSONUtil.toJsonStr(userDTO); // 保持原逻辑
String accessToken = JwtUtil.sign(payload, user.getPassword());
// 存到 Caffeine cache name "user-tokens"
String tokenKey = userDTO.getTokenKey(); // skyeye:user:token:id@account
Objects.requireNonNull(cacheManager.getCache("user-tokens")).put(tokenKey, accessToken);
userDTO.setToken(accessToken);
return Result.successData("登录成功", userDTO);
}
/** /**
* 修改密码 * 登录接口
* * @return
* @param dto */
* @return @IgnoreAuth
*/ @OperationLog(value = "登录", type = SysLog.TYPE_USER)
@PostMapping("/updpwd") @PostMapping("/login")
public Object updatePwd(@Valid SysUserPwdDTO dto) { public Result login(@RequestBody RegisterDTO registerDTO) {
List<SysUser> list = sysUserService.selectById(dto.getUserId()); SysUser user = sysUserService.selectByAccount(registerDTO.getUsername(), registerDTO.getPassword().toLowerCase());
if (ObjectUtil.isEmpty(list)) { if (user == null) {
throw ServiceException.noLog("用户名或密码错误!"); throw ServiceException.noLog(MessageUtils.message("user.login.error"));
} }
SysUser user = list.get(0); UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
if (user.getPassword().equals(dto.getOldPwd())) { String key = JSONUtil.toJsonStr(userDTO);
throw ServiceException.noLog("旧密码错误!"); String accessToken = JwtUtil.sign(key, user.getPassword());
} redisUtil.set(userDTO.getTokenKey(), accessToken, TOKEN_EXPIRE);
user.setPassword(dto.getNewPwd()); userDTO.setToken(accessToken);
sysUserService.update(user); return Result.successData(MessageUtils.message("user.login.success"), userDTO);
return "修改成功"; }
}
/** /**
* 退出接口 * 修改密码
*/ * @param dto
@IgnoreAuth * @return
@OperationLog(value = "退出登录", type = SysLog.TYPE_USER) */
@GetMapping("/logout") @PostMapping("/updpwd")
public Result logout() { public Object updatePwd(@Valid SysUserPwdDTO dto) {
UserDTO userDTO = SecurityUtil.getUser(); List<SysUser> list = sysUserService.selectById(dto.getUserId());
if (userDTO != null) { if (ObjectUtil.isEmpty(list)) {
String key = CacheKey.getToken(userDTO.getId(), userDTO.getAccount()); throw ServiceException.noLog(MessageUtils.message("user.updpwd.error"));
Objects.requireNonNull(cacheManager.getCache("user-tokens")).evict(key); }
} SysUser user = list.get(0);
return Result.status(true); if (user.getPassword().equals(dto.getOldPwd())) {
} throw ServiceException.noLog(MessageUtils.message("user.updpwd.nochg"));
}
user.setPassword(dto.getNewPwd());
sysUserService.update(user);
return MessageUtils.message("user.updpwd.success");
}
/**
* 退出接口
*/
@IgnoreAuth
@OperationLog(value = "退出登录", type = SysLog.TYPE_USER)
@RequestMapping("/logout")
@GetMapping
public Result logout() {
UserDTO userDTO = SecurityUtil.getUser();
if (userDTO != null) {
String key = CacheKey.getToekn(userDTO.getId(), userDTO.getAccount());
redisUtil.del(key);
}
return Result.status(true);
}
/** /**
* 分页查询 * 分页查询
*/ */
@RequestMapping("/page") @RequestMapping("/page")
public Object selectPage(@Valid @RequestBody PageDTO param) { public Object selectPage(@Valid @RequestBody PageDTO param) {
return sysUserService.selectPage(param); return sysUserService.selectPage(param);
} }
/** /**
* 列表查询 * 列表查询
*/ */
@RequestMapping("/list") @RequestMapping("/list")
public Object selectList(@Valid @RequestBody PageDTO param) { public Object selectList(@Valid @RequestBody PageDTO param) {
return sysUserService.selectList(param); return sysUserService.selectList(param);
} }
/** /**
* 查询详情 * 查询详情
*/ */
@GetMapping("/detail") @GetMapping("/detail")
public Object selectById(@RequestParam Long... id) { public Object selectById(@RequestParam Long... id) {
return sysUserService.selectById(id); return sysUserService.selectById(id);
} }
/** /**
* 新增 * 新增
*/ */
@PostMapping("/save") @PostMapping("/save")
@OperationLog(value = "用户新增", type = SysLog.TYPE_USER) @OperationLog(value = "用户新增", type = SysLog.TYPE_USER)
public Object insert(@RequestBody SysUser e) { public Object insert(@RequestBody SysUser e) {
return sysUserService.insert(e); return sysUserService.insert(e);
} }
/** /**
* 修改 * 修改
*/ */
@PostMapping("/update") @PostMapping("/update")
@OperationLog(value = "用户修改", type = SysLog.TYPE_USER) @OperationLog(value = "用户修改", type = SysLog.TYPE_USER)
public Object update(@RequestBody SysUser e) { public Object update(@RequestBody SysUser e) {
return sysUserService.update(e); return sysUserService.update(e);
} }
/** /**
* 删除 * 删除
*/ */
@OperationLog(value = "用户删除", type = SysLog.TYPE_USER) @OperationLog(value = "用户删除", type = SysLog.TYPE_USER)
@PostMapping("/remove") @PostMapping("/remove")
public Object delete(@RequestBody Long... id) { public Object delete(@RequestBody Long... id) {
return sysUserService.delete(id); return sysUserService.delete(id);
} }
} }

View File

@ -1,5 +1,6 @@
package com.zhangy.skyeye.sar.controller; package com.zhangy.skyeye.sar.controller;
import com.zhangy.skyeye.common.extend.util.MessageUtils;
import com.zhangy.skyeye.common.pojo.result.Result; import com.zhangy.skyeye.common.pojo.result.Result;
import com.zhangy.skyeye.sar.service.ISarControlService; import com.zhangy.skyeye.sar.service.ISarControlService;
import com.zhangy.skyeye.sar.dto.SarControlParamDTO; import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
@ -31,7 +32,7 @@ public class SarControlController {
public Result send(@RequestBody SarControlParamDTO param) { public Result send(@RequestBody SarControlParamDTO param) {
param.checkOrSetData(); param.checkOrSetData();
controlInfoService.sendUdp(param); controlInfoService.sendUdp(param);
return Result.successData("发送成功"); return Result.successData(MessageUtils.message("sar.control.send.success"));
} }
/** /**
@ -50,16 +51,16 @@ public class SarControlController {
// 检查是否成功获取 // 检查是否成功获取
if (ipVal == null || ipVal.isEmpty()) { if (ipVal == null || ipVal.isEmpty()) {
// 根据你的业务逻辑返回错误例如 // 根据你的业务逻辑返回错误例如
return Result.error("请求体中缺少 'payloadId' 或其值为空"); return Result.error(MessageUtils.message("sar.control.turnon.enoip"));
} }
} catch (Exception e) { } catch (Exception e) {
// 处理JSON解析异常 // 处理JSON解析异常
// 记录日志 e.g., log.error("Failed to parse turnOn payload", e); // 记录日志 e.g., log.error("Failed to parse turnOn payload", e);
return Result.error("请求体JSON格式错误"); return Result.error(MessageUtils.message("sar.control.turnon.eexcept"));
} }
// 3. 调用服务层 // 3. 调用服务层
controlInfoService.turnOn(ipVal); controlInfoService.turnOn(ipVal);
return Result.successData("发送成功"); return Result.successData(MessageUtils.message("sar.control.turnon.success"));
} }
/** /**
@ -80,16 +81,16 @@ public class SarControlController {
// 检查是否成功获取 // 检查是否成功获取
if (ipVal == null || ipVal.isEmpty()) { if (ipVal == null || ipVal.isEmpty()) {
// 根据你的业务逻辑返回错误例如 // 根据你的业务逻辑返回错误例如
return Result.error("请求体中缺少 'payloadId' 或其值为空"); return Result.error(MessageUtils.message("sar.control.endall.enoip"));
} }
} catch (Exception e) { } catch (Exception e) {
// 处理JSON解析异常 // 处理JSON解析异常
// 记录日志 e.g., log.error("Failed to parse turnOn payload", e); // 记录日志 e.g., log.error("Failed to parse turnOn payload", e);
return Result.error("请求体JSON格式错误"); return Result.error(MessageUtils.message("sar.control.endall.eexcept"));
} }
// 3. 调用服务层 // 3. 调用服务层
controlInfoService.endAll(ipVal); controlInfoService.endAll(ipVal);
return Result.successData("发送成功"); return Result.successData(MessageUtils.message("sar.control.endall.success"));
} }
} }

View File

@ -0,0 +1,311 @@
package com.zhangy.skyeye.sar.image.enhancer;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.imgproc.CLAHE;
import org.opencv.photo.Photo;
import org.opencv.photo.TonemapReinhard;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* ImageEnhancer.java
* ==================
* 两步组合图像亮度增强脚本自适应参数 + 多格式兼容 Java
*
* 支持的输入格式
* - JPG : 8 bit BGR直接兼容
* - PNG : 8/16 bitRGB/RGBA自动处理透明通道与位深
* - TIF : 8/16/32 bit灰度/彩色/含透明通道自动处理所有组合
*
* 增强步骤
* 1. Reinhard 局部色调映射 压缩动态范围统一全局亮度
* 2. CLAHE 局部对比度增强 LAB 空间对 L 通道进行局部直方图均衡化
*
* 所有关键参数均根据输入图像的统计特征自动计算无需手动调参
*
* 依赖
* - OpenCV for Java (e.g., opencv-4.x.x.jar)
*
* 编译与运行
* 1. 确保 opencv-4.x.x.jar classpath
* 2. 确保 OpenCV native library (e.g., opencv_java4xx.dll/so) java.library.path
* javac -cp .:/path/to/opencv-4.x.x.jar ImageEnhancer.java
* java -cp .:/path/to/opencv-4.x.x.jar -Djava.library.path=/path/to/native/libs ImageEnhancer <input_path> [output_dir]
*/
public class ImageEnhancer {
static {
try {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load.\n" + e);
System.exit(1);
}
}
/**
* 存放自适应计算出的参数
*/
public static class AdaptiveParams {
double gamma, intensity, lightAdapt, colorAdapt = 0.0, clipLimit;
Size tileGrid;
@Override
public String toString() {
return String.format(
" gamma = %.3f%n" +
" intensity = %.3f%n" +
" light_adapt = %.3f%n" +
" clip_limit = %.3f%n" +
" tile_grid = %s",
gamma, intensity, lightAdapt, clipLimit, tileGrid
);
}
}
/**
* 存放归一化结果和日志
*/
public static class NormalizedImage {
Mat image;
String log;
}
/**
* uint16 图像做百分位截断线性拉伸后转为 uint8
*/
private static Mat uint16ToUint8Stretch(Mat imgU16, double lowPct, double highPct) {
// 将所有通道像素合并后统一计算 lo/hi避免各通道独立拉伸导致色偏
int totalPixels = (int) (imgU16.total() * imgU16.channels());
short[] pixels = new short[totalPixels];
imgU16.get(0, 0, pixels);
// 转换为 int 以避免排序时的符号问题
int[] pixelsInt = new int[totalPixels];
for (int i = 0; i < totalPixels; i++) {
pixelsInt[i] = pixels[i] & 0xFFFF;
}
Arrays.sort(pixelsInt);
// 使用全局统一的 lo/hi所有通道共用保持色彩比例
double lo = pixelsInt[(int) (totalPixels * lowPct / 100.0)];
double hi = pixelsInt[(int) (totalPixels * highPct / 100.0)];
// 使用 convertTo(alpha, beta) 做线性变换dst = src * alpha + beta
// 等价于 (src - lo) * 255/(hi-lo) = src * (255/(hi-lo)) + (-lo * 255/(hi-lo))
double alpha = 255.0 / (hi - lo + 1e-6);
double beta = -lo * alpha;
Mat result = new Mat();
// convertTo 对所有通道均匀应用相同的 alpha/beta不存在 Scalar 多通道问题
imgU16.convertTo(result, CvType.CV_8U, alpha, beta);
return result;
}
/**
* 将任意常见格式的图像统一转换为 BGR uint8
*/
public static NormalizedImage normalizeToBgrUint8(Mat img) {
NormalizedImage result = new NormalizedImage();
StringBuilder log = new StringBuilder();
log.append(String.format("原始格式dtype=%s, shape=(%d, %d, %d)%n",
CvType.typeToString(img.type()), img.rows(), img.cols(), img.channels()));
Mat currentImg = img.clone();
List<String> steps = new ArrayList<>();
// 1. 位深归一化
if (img.depth() == CvType.CV_16U) {
currentImg = uint16ToUint8Stretch(img, 2.0, 98.0);
steps.add("uint16 → uint8 (百分位拉伸 2%~98%)");
} else if (img.depth() == CvType.CV_32F || img.depth() == CvType.CV_64F) {
img.convertTo(currentImg, CvType.CV_8U, 255.0);
steps.add(CvType.typeToString(img.type()) + " → uint8 (×255 缩放)");
}
// 2. 通道数归一化
if (currentImg.channels() == 1) {
Imgproc.cvtColor(currentImg, currentImg, Imgproc.COLOR_GRAY2BGR);
steps.add("灰度(1ch) → BGR(3ch)");
} else if (currentImg.channels() == 4) {
Imgproc.cvtColor(currentImg, currentImg, Imgproc.COLOR_BGRA2BGR);
steps.add("BGRA(4ch) → BGR(3ch, 丢弃 Alpha)");
}
log.append(" 转换步骤:").append(steps.isEmpty() ? "无需转换 (已是 BGR uint8)" : String.join(" | ", steps)).append("\n");
log.append(String.format(" 归一化后dtype=%s, shape=(%d, %d, %d)",
CvType.typeToString(currentImg.type()), currentImg.rows(), currentImg.cols(), currentImg.channels()));
result.image = currentImg;
result.log = log.toString();
return result;
}
/**
* 根据输入图像的统计特征自适应计算增强参数
*/
public static AdaptiveParams computeAdaptiveParams(Mat imgBgr) {
Mat gray = new Mat();
Imgproc.cvtColor(imgBgr, gray, Imgproc.COLOR_BGR2GRAY);
Mat grayF = new Mat();
gray.convertTo(grayF, CvType.CV_32F);
int H = gray.rows(), W = gray.cols(), totalPixels = H * W;
AdaptiveParams params = new AdaptiveParams();
// Reinhard gamma 语义说明
// OpenCV Reinhard gamma 越大 输出越亮与传统 gamma 校正语义相反
// 由实验扫描拟合la=0.8, intensity=0mean_out 79.2 × gamma
// target_mean 根据图像均值自适应图越暗 目标越高最大提亮到 128
// 1. 图像均值 gamma
double meanVal = Core.mean(grayF).val[0];
double meanNorm = meanVal / 255.0;
double targetMean = Math.min(128.0, 128.0 * Math.pow(1.0 - meanNorm, 0.3));
params.gamma = Math.max(0.8, Math.min(3.0, targetMean / 79.2));
// 2. intensity 固定为 0亮度控制完全由 gamma 承担
// 避免 intensity gamma 叠加导致方向混乱
params.intensity = 0.0;
// 3. 全局标准差 light_adapt
// std 越小对比度越低 越需要局部自适应 light_adapt 越大
// 上限 0.91.0 会产生 NaN下限 0.5
MatOfDouble mean = new MatOfDouble(), stddev = new MatOfDouble();
Core.meanStdDev(grayF, mean, stddev);
double stdGlobal = stddev.get(0, 0)[0] / 255.0;
params.lightAdapt = Math.max(0.5, Math.min(0.9, 0.9 - stdGlobal));
// 4. 局部标准差均值 clipLimit
int tile = 8;
List<Double> localStds = new ArrayList<>();
for (int y = 0; y < H - tile; y += tile) {
for (int x = 0; x < W - tile; x += tile) {
Mat patch = new Mat(grayF, new Rect(x, y, tile, tile));
Core.meanStdDev(patch, mean, stddev);
localStds.add(stddev.get(0, 0)[0]);
}
}
double meanLocalStd = localStds.stream().mapToDouble(d -> d).average().orElse(20.0);
double clipLimit = 2.0 * (30.0 / (meanLocalStd + 1e-6));
params.clipLimit = Math.max(1.0, Math.min(6.0, clipLimit));
// 5. 图像短边分辨率 tileGridSize
int tileSize = Math.max(8, Math.min(H, W) / 16);
params.tileGrid = new Size(tileSize, tileSize);
gray.release(); grayF.release(); mean.release(); stddev.release();
return params;
}
/**
* 第一步Reinhard 局部色调映射
*/
public static Mat step1Reinhard(Mat imgBgr, AdaptiveParams params) {
Mat imgF32 = new Mat();
imgBgr.convertTo(imgF32, CvType.CV_32FC3, 1.0 / 255.0);
TonemapReinhard tonemap = Photo.createTonemapReinhard(
(float) params.gamma, (float) params.intensity, (float) params.lightAdapt, (float) params.colorAdapt);
Mat mapped = new Mat();
tonemap.process(imgF32, mapped);
// NaN/Inf 替换为 0再裁剪到合法范围
Core.patchNaNs(mapped, 0.0);
Mat result = new Mat();
mapped.convertTo(result, CvType.CV_8UC3, 255.0);
imgF32.release(); mapped.release();
return result;
}
/**
* 第二步CLAHE 局部对比度增强
*/
public static Mat step2Clahe(Mat imgBgr, AdaptiveParams params) {
Mat lab = new Mat();
Imgproc.cvtColor(imgBgr, lab, Imgproc.COLOR_BGR2Lab);
List<Mat> labPlanes = new ArrayList<>(3);
Core.split(lab, labPlanes);
CLAHE clahe = Imgproc.createCLAHE(params.clipLimit, params.tileGrid);
Mat lEnhanced = new Mat();
clahe.apply(labPlanes.get(0), lEnhanced);
labPlanes.set(0, lEnhanced);
Mat resultLab = new Mat();
Core.merge(labPlanes, resultLab);
Mat result = new Mat();
Imgproc.cvtColor(resultLab, result, Imgproc.COLOR_Lab2BGR);
lab.release(); lEnhanced.release(); resultLab.release();
for(Mat p : labPlanes) p.release();
return result;
}
/**
* 主流程对输入图像执行两步增强并保存结果
*/
public static void enhance(String inputPath, String outputDir) {
Mat imgRaw = Imgcodecs.imread(inputPath, Imgcodecs.IMREAD_UNCHANGED);
if (imgRaw.empty()) {
System.err.println("无法读取图像: " + inputPath);
return;
}
String baseName = inputPath.substring(
inputPath.lastIndexOf("/") >= 0 ? inputPath.lastIndexOf("/") + 1 : 0,
inputPath.lastIndexOf(".")
);
// 1. 格式归一化
System.out.println("── 格式归一化 ──────────────────────────────");
NormalizedImage normResult = normalizeToBgrUint8(imgRaw);
Mat imgBgr = normResult.image;
System.out.println(normResult.log);
// 2. 计算自适应参数
System.out.println("── 自适应参数 ──────────────────────────────");
AdaptiveParams params = computeAdaptiveParams(imgBgr);
System.out.println(params);
System.out.println("────────────────────────────────────────────");
// 3. 第一步Reinhard 色调映射
Mat resultStep1 = step1Reinhard(imgBgr, params);
String pathStep1 = outputDir + "/" + baseName + "_step1_tonemap.png";
Imgcodecs.imwrite(pathStep1, resultStep1);
System.out.println("第一步结果已保存: " + pathStep1);
// 4. 第二步CLAHE 局部增强
Mat resultStep2 = step2Clahe(resultStep1, params);
String pathStep2 = outputDir + "/" + baseName + "_step2_clahe.png";
Imgcodecs.imwrite(pathStep2, resultStep2);
System.out.println("第二步结果已保存: " + pathStep2);
imgRaw.release(); imgBgr.release(); resultStep1.release(); resultStep2.release();
}
public static void main(String[] args) {
if (args.length < 1) {
System.out.println("用法: java ImageEnhancer <input_path> [output_dir]");
String inputPath = "/home/ubuntu/upload/pasted_file_a5uhAu_8986130c06b1e8661387e859b5d2ac93.png";
String outputDir = "/home/ubuntu";
System.out.println("\n使用默认路径进行处理:");
enhance(inputPath, outputDir);
return;
}
String inputPath = args[0];
String outputDir = (args.length > 1) ? args[1] : new java.io.File(inputPath).getParent();
if (outputDir == null) outputDir = ".";
new java.io.File(outputDir).mkdirs();
enhance(inputPath, outputDir);
}
}

View File

@ -14,6 +14,9 @@ spring:
allow-circular-references: true allow-circular-references: true
application: application:
name: @artifactId@ name: @artifactId@
messages:
basename: i18n/messages
fallback-to-system-locale: false
profiles: profiles:
# active: dev # active: dev
config: config:

View File

@ -0,0 +1,47 @@
# Default messages
# device management
device.uav.remove.success=Remove success
device.uav.remove.fail=Remove failed
device.uav.offline=UAV [{0}] disconnected
device.sar.offline=SAR [{0}] disconnected
# SAR图片管理
sar.image.remove.success=Remove success
sar.image.remove.fail=Remove failed
sar.image.add_highres.success=Operation success
sar.image.add_highres.eagain=Other uploading in progress, try later
sar.image.add_highres.fail=Operation failed
sar.image.update.success=Update success
sar.image.update.fail=Update failed
# SAR Job管理
sar.job.update.success=Operation success
sar.job.update.failed=Operation failed
sar.job.retry.enullid=Job ID can't be blank
sar.job.start.success=Start executing
sar.job.start.enosuchjob=Find no job, id={0}
sar.job.start.einexecuting=Job in executing, operation failed
sar.job.exit.success=Operation success
sar.job.exit.failed=Operation failed
sar.job_exec.brightness.success=Operation success
sar.job_exec.brightness.failed=Operation failed
# SAR控制
sar.control.send.success=Send success
sar.control.send.failed=Send failed
sar.control.turnon.success=Send success
sar.control.turnon.enoip=Failed to extract payload IP address
sar.control.turnon.eexcept=Failed to parse request body
sar.control.turnon.euavinexec=Configured UAV in middle of executing
sar.control.turnon.esarinexec=Configured SAR in middle of executing
sar.control.endall.success=Send success
sar.control.endall.enoip=Failed to extract payload IP address
sar.control.endall.eexcept=Failed to parse request body
# 用户管理
user.login.success=Login success
user.login.error=Login error
user.updpwd.success=Change password success
user.updpwd.error=User name or password error!
user.updpwd.nochg=Old password error!

View File

@ -0,0 +1,45 @@
# 中文消息
# 设备管理
device.uav.remove.success=操作成功
device.uav.remove.fail=操作失败
device.uav.offline=无人机[{0}]未连接
device.sar.offline=雷达[{0}]未连接
# SAR图片管理
sar.image.remove.success=操作完成
sar.image.remove.fail=操作失败
sar.image.add_highres.success=操作完成
sar.image.add_highres.eagain=正在上传其它图像,请稍后重试!
sar.image.add_highres.fail=操作失败
sar.image.update.success=操作成功
sar.image.update.fail=操作失败
# SAR Job管理
sar.job.update.success=操作成功
sar.job.update.failed=操作失败
sar.job.retry.enullid=任务ID不能为空
sar.job.start.success=开始执行
sar.job.start.enosuchjob=找不到任务id={0}
sar.job.start.einexecuting=任务状态为执行中,无法起飞
sar.job.exit.success=操作成功
sar.job.exit.failed=操作失败
# SAR控制
sar.control.send.success=发送成功
sar.control.send.failed=发送失败
sar.control.turnon.success=发送成功
sar.control.turnon.enoip=请求体中缺少 'payloadId' 或其值为空
sar.control.turnon.eexcept=请求体JSON格式错误
sar.control.turnon.euavinexec=无人机正在任务中,无法执行新任务
sar.control.turnon.esarinexec=载荷正在任务中,无法执行新任务
sar.control.endall.success=发送成功
sar.control.endall.enoip=请求体中缺少 'payloadId' 或其值为空
sar.control.endall.eexcept=请求体JSON格式错误
# 用户管理
user.login.success=登录成功
user.login.error=用户名或密码错误!
user.updpwd.success=修改成功
user.updpwd.error=用户名或密码错误!
user.updpwd.nochg=旧密码错误!

View File

@ -32,6 +32,7 @@
"sockjs-client": "^1.5.1", "sockjs-client": "^1.5.1",
"stompjs": "^2.3.3", "stompjs": "^2.3.3",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-i18n": "^8.28.2",
"vue-router": "^3.1.5", "vue-router": "^3.1.5",
"vuex": "^3.1.2", "vuex": "^3.1.2",
"vuex-persistedstate": "^4.1.0" "vuex-persistedstate": "^4.1.0"

View File

@ -0,0 +1,29 @@
// English translations
export default {
app: {
title: 'Skyeye Twin System',
},
header: {
login: 'Login',
logout: 'Logout',
},
login: {
username: 'Please enter username',
password: 'Please enter password',
title: 'Login',
submit: 'Log In',
validation: {
required: 'Cannot be empty',
passwordMin: 'Password must be at least 6 characters',
},
},
common: {
capsLockOn: 'Caps lock is On',
},
menu: {
device: 'Device Management',
task: 'Task Management',
picture: 'Picture Management',
user: 'User Management'
}
}

View File

@ -0,0 +1,21 @@
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import zh from './zh';
import en from './en';
Vue.use(VueI18n)
// load locale messages
const messages = {
zh,
en,
}
const locale = localStorage.getItem('locale') || 'zh'
const i18n = new VueI18n({
locale,
fallbackLocale: 'zh',
messages,
})
export default i18n

View File

@ -0,0 +1,29 @@
// Chinese translations
export default {
app: {
title: '空域快视系统',
},
header: {
login: '登录',
logout: '退出登录',
},
login: {
username: '请输入用户名',
password: '请输入用户密码',
title: '登录',
submit: '登 录',
validation: {
required: '不能为空',
passwordMin: '密码不少于6位字符',
},
},
common: {
capsLockOn: '大写锁定已打开',
},
menu: {
device: '设备管理',
task: '任务管理',
picture: '图片管理',
user: '用户管理'
}
}

View File

@ -18,7 +18,7 @@ export default {
} }
const validatePassword = (rule, value, callback) => { const validatePassword = (rule, value, callback) => {
if (value.length < 6) { if (value.length < 6) {
callback(new Error('密码不少于6位字符')) callback(new Error(this.$t('login.validation.passwordMin')))
} else { } else {
callback() callback()
} }
@ -51,7 +51,7 @@ export default {
loginRules: { loginRules: {
username: [ username: [
// { required: true, trigger: 'blur', validator: validateUsername } // { required: true, trigger: 'blur', validator: validateUsername }
{ required: true, message: '请输入用户名', trigger: 'blur' } { required: true, message: this.$t('login.username'), trigger: 'blur' }
], ],
password: [ password: [
{ required: true, trigger: 'blur', validator: validatePassword } { required: true, trigger: 'blur', validator: validatePassword }
@ -67,22 +67,22 @@ export default {
activeIndex: '1', activeIndex: '1',
menus: [ menus: [
{ {
label: "设备管理", key: 'device',
value: '2', value: '2',
show: true show: true
}, },
{ {
label: "任务管理", key: 'task',
value: '3', value: '3',
show: true show: true
}, },
{ {
label: "图片管理", key: 'picture',
value: '4', value: '4',
show: true show: true
}, },
{ {
label: "用户管理", key: 'user',
value: '5', value: '5',
show: true show: true
}, },
@ -175,6 +175,10 @@ export default {
handleSelect(key) { handleSelect(key) {
this.SET_MENUS_CHOSE(key) this.SET_MENUS_CHOSE(key)
}, },
changeLocale(lang) {
this.$i18n.locale = lang
localStorage.setItem('locale', lang)
},
goHome() { goHome() {
this.SET_MENUS_CHOSE('1') this.SET_MENUS_CHOSE('1')
}, },

View File

@ -25,7 +25,7 @@ $light_gray: #eee;
align-items: center; align-items: center;
.logo { .logo {
width: 250px; width: 350px;
height: 32px; height: 32px;
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
@ -129,7 +129,7 @@ $light_gray: #eee;
} }
.header-nav-r { .header-nav-r {
width: 250px; width: 350px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;

View File

@ -4,7 +4,7 @@
<div class="logo" @click="goHome"> <div class="logo" @click="goHome">
<img src="@/assets/img/common/logo3.png" alt /> <img src="@/assets/img/common/logo3.png" alt />
<!--span :class="tokenKey">灵动孪生智控系统</span--> <!--span :class="tokenKey">灵动孪生智控系统</span-->
<span :class="tokenKey">空域快视系统</span> <span :class="tokenKey">{{ $t('app.title') }}</span>
</div> </div>
</div> </div>
<div class="header-nav-c"> <div class="header-nav-c">
@ -19,14 +19,17 @@
v-if="item.show" v-if="item.show"
:index="item.value" :index="item.value"
:key="item.value" :key="item.value"
>{{ item.label }}</el-menu-item >{{ $t('menu.' + item.key) }}</el-menu-item
> >
</template> </template>
</el-menu> </el-menu>
</div> </div>
<ul class="header-nav-r"> <ul class="header-nav-r">
<!-- <li class="lang-select">
<a @click.prevent="changeLocale('zh')">中文</a> | <a @click.prevent="changeLocale('en')">EN</a>
</li> -->
<li v-if="roleIdsLocale.indexOf(2) !== -1"> <li v-if="roleIdsLocale.indexOf(2) !== -1">
<span class="user-login" @click="showLogin">登录</span> <span class="user-login" @click="showLogin">{{ $t('header.login') }}</span>
</li> </li>
<li v-if="roleIdsLocale.indexOf(2) === -1"> <li v-if="roleIdsLocale.indexOf(2) === -1">
<span class="user-photo"> <span class="user-photo">
@ -42,14 +45,14 @@
<!-- <el-dropdown-item command="modifyPassword" <!-- <el-dropdown-item command="modifyPassword"
>修改密码</el-dropdown-item >修改密码</el-dropdown-item
> --> > -->
<el-dropdown-item command="logout">退出登录</el-dropdown-item> <el-dropdown-item command="logout">{{ $t('header.logout') }}</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
</li> </li>
</ul> </ul>
<dt-dialog <dt-dialog
title="登录" :title="$t('header.login')"
:visible.sync="visible.login" :visible.sync="visible.login"
top="20vh" top="20vh"
width="22%" width="22%"
@ -70,7 +73,7 @@
class="username" class="username"
clearable clearable
v-model="loginForm.username" v-model="loginForm.username"
placeholder="请输入用户名" :placeholder="$t('login.username')"
name="username" name="username"
type="text" type="text"
size="mini" size="mini"
@ -82,7 +85,7 @@
<el-tooltip <el-tooltip
v-model="capsTooltip" v-model="capsTooltip"
content="Caps lock is On" :content="$t('common.capsLockOn')"
placement="right" placement="right"
manual manual
> >
@ -93,7 +96,7 @@
class="password" class="password"
v-model="loginForm.password" v-model="loginForm.password"
:type="passwordType" :type="passwordType"
placeholder="请输入用户密码" :placeholder="$t('login.password')"
name="password" name="password"
tabindex="2" tabindex="2"
size="mini" size="mini"
@ -122,8 +125,7 @@
size="mini" size="mini"
style="width: 100%" style="width: 100%"
@click.native.prevent="handleLogin" @click.native.prevent="handleLogin"
> </el-button >{{ $t('login.submit') }}</el-button>
>
</el-form-item> </el-form-item>
</el-form> </el-form>
</dt-dialog> </dt-dialog>

View File

@ -4,6 +4,7 @@
*/ */
import Vue from 'vue' import Vue from 'vue'
import appLoader from './App.Loader'; import appLoader from './App.Loader';
import i18n from '@/lang/index';
(async () => { (async () => {
await appLoader.install() await appLoader.install()
Promise.all([ Promise.all([
@ -17,6 +18,7 @@ import appLoader from './App.Loader';
el: '#app', el: '#app',
router, router,
store, store,
i18n,
render: h => h(App) render: h => h(App)
}) })
}) })

View File

@ -12,7 +12,7 @@
label-position="left" label-position="left"
> >
<div class="title-container"> <div class="title-container">
<h3 class="title">登录</h3> <h3 class="title">{{ $t('login.title') }}</h3>
</div> </div>
<el-form-item prop="username"> <el-form-item prop="username">
@ -25,7 +25,7 @@
ref="username" ref="username"
class="username" class="username"
v-model="loginForm.username" v-model="loginForm.username"
placeholder="请输入用户名" :placeholder="$t('login.username')"
name="username" name="username"
type="text" type="text"
tabindex="1" tabindex="1"
@ -36,7 +36,7 @@
<el-tooltip <el-tooltip
v-model="capsTooltip" v-model="capsTooltip"
content="Caps lock is On" :content="$t('common.capsLockOn')"
placement="right" placement="right"
manual manual
> >
@ -52,7 +52,7 @@
class="password" class="password"
v-model="loginForm.password" v-model="loginForm.password"
:type="passwordType" :type="passwordType"
placeholder="请输入用户密码" :placeholder="$t('login.password')"
name="password" name="password"
tabindex="2" tabindex="2"
autocomplete="on" autocomplete="on"
@ -77,7 +77,7 @@
type="primary" type="primary"
round round
@click.native.prevent="handleLogin" @click.native.prevent="handleLogin"
> </el-button >{{ $t('login.submit') }}</el-button
> >
</el-form> </el-form>
</div> </div>
@ -101,7 +101,7 @@ export default {
} }
const validatePassword = (rule, value, callback) => { const validatePassword = (rule, value, callback) => {
if (value.length < 6) { if (value.length < 6) {
callback(new Error('密码不少于6位字符')) callback(new Error(this.$t('login.validation.passwordMin')))
} else { } else {
callback() callback()
} }
@ -117,7 +117,7 @@ export default {
], ],
password: [ password: [
// { required: true, trigger: 'blur', validator: validatePassword } // { required: true, trigger: 'blur', validator: validatePassword }
{ required: true, trigger: 'blur', message: '请输入密码' }, { required: true, trigger: 'blur', message: this.$t('login.validation.required') },
], ],
}, },
passwordType: 'password', passwordType: 'password',

File diff suppressed because it is too large Load Diff