mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-27 17:43:55 +08:00
update:字典前端增删改查
This commit is contained in:
@@ -96,4 +96,11 @@ public class RedisKeys {
|
|||||||
public static String getOtaDownloadCountKey(String uuid) {
|
public static String getOtaDownloadCountKey(String uuid) {
|
||||||
return "ota:download:count:" + uuid;
|
return "ota:download:count:" + uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字典数据的缓存key
|
||||||
|
*/
|
||||||
|
public static String getDictDataByTypeKey(String dictType) {
|
||||||
|
return "sys:dict:data:" + dictType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-4
@@ -21,9 +21,7 @@ import xiaozhi.common.redis.RedisUtils;
|
|||||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||||
import xiaozhi.common.utils.ConvertUtils;
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
import xiaozhi.modules.agent.dao.AgentDao;
|
import xiaozhi.modules.agent.dao.AgentDao;
|
||||||
import xiaozhi.modules.agent.dao.AgentTemplateDao;
|
|
||||||
import xiaozhi.modules.agent.entity.AgentEntity;
|
import xiaozhi.modules.agent.entity.AgentEntity;
|
||||||
import xiaozhi.modules.agent.service.AgentTemplateService;
|
|
||||||
import xiaozhi.modules.model.dao.ModelConfigDao;
|
import xiaozhi.modules.model.dao.ModelConfigDao;
|
||||||
import xiaozhi.modules.model.dto.ModelBasicInfoDTO;
|
import xiaozhi.modules.model.dto.ModelBasicInfoDTO;
|
||||||
import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
|
import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
|
||||||
@@ -41,8 +39,6 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
|||||||
private final ModelConfigDao modelConfigDao;
|
private final ModelConfigDao modelConfigDao;
|
||||||
private final ModelProviderService modelProviderService;
|
private final ModelProviderService modelProviderService;
|
||||||
private final RedisUtils redisUtils;
|
private final RedisUtils redisUtils;
|
||||||
private final AgentTemplateDao agentTemplateDao;
|
|
||||||
private final AgentTemplateService agentTemplateService;
|
|
||||||
private final AgentDao agentDao;
|
private final AgentDao agentDao;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+15
-6
@@ -1,10 +1,10 @@
|
|||||||
package xiaozhi.modules.sys.controller;
|
package xiaozhi.modules.sys.controller;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -25,6 +25,7 @@ import xiaozhi.common.utils.Result;
|
|||||||
import xiaozhi.common.validator.ValidatorUtils;
|
import xiaozhi.common.validator.ValidatorUtils;
|
||||||
import xiaozhi.modules.sys.dto.SysDictDataDTO;
|
import xiaozhi.modules.sys.dto.SysDictDataDTO;
|
||||||
import xiaozhi.modules.sys.service.SysDictDataService;
|
import xiaozhi.modules.sys.service.SysDictDataService;
|
||||||
|
import xiaozhi.modules.sys.vo.SysDictDataItem;
|
||||||
import xiaozhi.modules.sys.vo.SysDictDataVO;
|
import xiaozhi.modules.sys.vo.SysDictDataVO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,10 +44,10 @@ public class SysDictDataController {
|
|||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@Operation(summary = "分页查询字典数据")
|
@Operation(summary = "分页查询字典数据")
|
||||||
@RequiresPermissions("sys:role:superAdmin")
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
@Parameters({@Parameter(name = "dictTypeId", description = "字典类型ID", required = true),
|
@Parameters({ @Parameter(name = "dictTypeId", description = "字典类型ID", required = true),
|
||||||
@Parameter(name = "dictLabel", description = "数据标签"), @Parameter(name = "dictValue", description = "数据值"),
|
@Parameter(name = "dictLabel", description = "数据标签"), @Parameter(name = "dictValue", description = "数据值"),
|
||||||
@Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true),
|
@Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true),
|
||||||
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true)})
|
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) })
|
||||||
public Result<PageData<SysDictDataVO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
|
public Result<PageData<SysDictDataVO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
|
||||||
ValidatorUtils.validateEntity(params);
|
ValidatorUtils.validateEntity(params);
|
||||||
// 强制校验dictTypeId是否存在
|
// 强制校验dictTypeId是否存在
|
||||||
@@ -84,7 +85,7 @@ public class SysDictDataController {
|
|||||||
return new Result<>();
|
return new Result<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/delete")
|
@PostMapping("/delete")
|
||||||
@Operation(summary = "删除字典数据")
|
@Operation(summary = "删除字典数据")
|
||||||
@RequiresPermissions("sys:role:superAdmin")
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
@Parameter(name = "ids", description = "ID数组", required = true)
|
@Parameter(name = "ids", description = "ID数组", required = true)
|
||||||
@@ -93,4 +94,12 @@ public class SysDictDataController {
|
|||||||
return new Result<>();
|
return new Result<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/type/{dictType}")
|
||||||
|
@Operation(summary = "获取字典数据列表")
|
||||||
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
|
public Result<List<SysDictDataItem>> getDictDataByType(@PathVariable("dictType") String dictType) {
|
||||||
|
List<SysDictDataItem> list = sysDictDataService.getDictDataByType(dictType);
|
||||||
|
return new Result<List<SysDictDataItem>>().ok(list);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-6
@@ -3,7 +3,6 @@ package xiaozhi.modules.sys.controller;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -42,10 +41,10 @@ public class SysDictTypeController {
|
|||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@Operation(summary = "分页查询字典类型")
|
@Operation(summary = "分页查询字典类型")
|
||||||
@RequiresPermissions("sys:role:superAdmin")
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
@Parameters({@Parameter(name = "dictType", description = "字典类型编码"),
|
@Parameters({ @Parameter(name = "dictType", description = "字典类型编码"),
|
||||||
@Parameter(name = "dictName", description = "字典类型名称"),
|
@Parameter(name = "dictName", description = "字典类型名称"),
|
||||||
@Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true),
|
@Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true),
|
||||||
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true)})
|
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) })
|
||||||
public Result<PageData<SysDictTypeVO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
|
public Result<PageData<SysDictTypeVO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
|
||||||
ValidatorUtils.validateEntity(params);
|
ValidatorUtils.validateEntity(params);
|
||||||
PageData<SysDictTypeVO> page = sysDictTypeService.page(params);
|
PageData<SysDictTypeVO> page = sysDictTypeService.page(params);
|
||||||
@@ -82,7 +81,7 @@ public class SysDictTypeController {
|
|||||||
return new Result<>();
|
return new Result<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/delete")
|
@PostMapping("/delete")
|
||||||
@Operation(summary = "删除字典类型")
|
@Operation(summary = "删除字典类型")
|
||||||
@RequiresPermissions("sys:role:superAdmin")
|
@RequiresPermissions("sys:role:superAdmin")
|
||||||
@Parameter(name = "ids", description = "ID数组", required = true)
|
@Parameter(name = "ids", description = "ID数组", required = true)
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package xiaozhi.modules.sys.dao;
|
package xiaozhi.modules.sys.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
import xiaozhi.common.dao.BaseDao;
|
import xiaozhi.common.dao.BaseDao;
|
||||||
import xiaozhi.modules.sys.entity.SysDictDataEntity;
|
import xiaozhi.modules.sys.entity.SysDictDataEntity;
|
||||||
|
import xiaozhi.modules.sys.vo.SysDictDataItem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字典数据
|
* 字典数据
|
||||||
@@ -11,4 +14,13 @@ import xiaozhi.modules.sys.entity.SysDictDataEntity;
|
|||||||
@Mapper
|
@Mapper
|
||||||
public interface SysDictDataDao extends BaseDao<SysDictDataEntity> {
|
public interface SysDictDataDao extends BaseDao<SysDictDataEntity> {
|
||||||
|
|
||||||
|
List<SysDictDataItem> getDictDataByType(String dictType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据字典类型ID获取字典类型编码
|
||||||
|
*
|
||||||
|
* @param dictTypeId 字典类型ID
|
||||||
|
* @return 字典类型编码
|
||||||
|
*/
|
||||||
|
String getTypeByTypeId(Long dictTypeId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package xiaozhi.modules.sys.service;
|
package xiaozhi.modules.sys.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import xiaozhi.common.page.PageData;
|
import xiaozhi.common.page.PageData;
|
||||||
import xiaozhi.common.service.BaseService;
|
import xiaozhi.common.service.BaseService;
|
||||||
import xiaozhi.modules.sys.dto.SysDictDataDTO;
|
import xiaozhi.modules.sys.dto.SysDictDataDTO;
|
||||||
import xiaozhi.modules.sys.entity.SysDictDataEntity;
|
import xiaozhi.modules.sys.entity.SysDictDataEntity;
|
||||||
|
import xiaozhi.modules.sys.vo.SysDictDataItem;
|
||||||
import xiaozhi.modules.sys.vo.SysDictDataVO;
|
import xiaozhi.modules.sys.vo.SysDictDataVO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,4 +52,19 @@ public interface SysDictDataService extends BaseService<SysDictDataEntity> {
|
|||||||
*/
|
*/
|
||||||
void delete(Long[] ids);
|
void delete(Long[] ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据字典类型ID删除对应的字典数据
|
||||||
|
*
|
||||||
|
* @param dictTypeId 字典类型ID
|
||||||
|
*/
|
||||||
|
void deleteByTypeId(Long dictTypeId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据字典类型获取字典数据列表
|
||||||
|
*
|
||||||
|
* @param dictType 字典类型
|
||||||
|
* @return 返回字典数据列表
|
||||||
|
*/
|
||||||
|
List<SysDictDataItem> getDictDataByType(String dictType);
|
||||||
|
|
||||||
}
|
}
|
||||||
+56
-9
@@ -1,6 +1,5 @@
|
|||||||
package xiaozhi.modules.sys.service.impl;
|
package xiaozhi.modules.sys.service.impl;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -19,6 +18,8 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import xiaozhi.common.exception.RenException;
|
import xiaozhi.common.exception.RenException;
|
||||||
import xiaozhi.common.page.PageData;
|
import xiaozhi.common.page.PageData;
|
||||||
|
import xiaozhi.common.redis.RedisKeys;
|
||||||
|
import xiaozhi.common.redis.RedisUtils;
|
||||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||||
import xiaozhi.common.utils.ConvertUtils;
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
import xiaozhi.modules.sys.dao.SysDictDataDao;
|
import xiaozhi.modules.sys.dao.SysDictDataDao;
|
||||||
@@ -27,6 +28,7 @@ import xiaozhi.modules.sys.dto.SysDictDataDTO;
|
|||||||
import xiaozhi.modules.sys.entity.SysDictDataEntity;
|
import xiaozhi.modules.sys.entity.SysDictDataEntity;
|
||||||
import xiaozhi.modules.sys.entity.SysUserEntity;
|
import xiaozhi.modules.sys.entity.SysUserEntity;
|
||||||
import xiaozhi.modules.sys.service.SysDictDataService;
|
import xiaozhi.modules.sys.service.SysDictDataService;
|
||||||
|
import xiaozhi.modules.sys.vo.SysDictDataItem;
|
||||||
import xiaozhi.modules.sys.vo.SysDictDataVO;
|
import xiaozhi.modules.sys.vo.SysDictDataVO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,8 +37,9 @@ import xiaozhi.modules.sys.vo.SysDictDataVO;
|
|||||||
@Service
|
@Service
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysDictDataEntity>
|
public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysDictDataEntity>
|
||||||
implements SysDictDataService {
|
implements SysDictDataService {
|
||||||
private final SysUserDao sysUserDao;
|
private final SysUserDao sysUserDao;
|
||||||
|
private final RedisUtils redisUtils;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageData<SysDictDataVO> page(Map<String, Object> params) {
|
public PageData<SysDictDataVO> page(Map<String, Object> params) {
|
||||||
@@ -50,9 +53,9 @@ public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysD
|
|||||||
}
|
}
|
||||||
|
|
||||||
private QueryWrapper<SysDictDataEntity> getWrapper(Map<String, Object> params) {
|
private QueryWrapper<SysDictDataEntity> getWrapper(Map<String, Object> params) {
|
||||||
String dictTypeId = (String)params.get("dictTypeId");
|
String dictTypeId = (String) params.get("dictTypeId");
|
||||||
String dictLabel = (String)params.get("dictLabel");
|
String dictLabel = (String) params.get("dictLabel");
|
||||||
String dictValue = (String)params.get("dictValue");
|
String dictValue = (String) params.get("dictValue");
|
||||||
|
|
||||||
QueryWrapper<SysDictDataEntity> wrapper = new QueryWrapper<>();
|
QueryWrapper<SysDictDataEntity> wrapper = new QueryWrapper<>();
|
||||||
wrapper.eq("dict_type_id", Long.parseLong(dictTypeId));
|
wrapper.eq("dict_type_id", Long.parseLong(dictTypeId));
|
||||||
@@ -78,6 +81,9 @@ public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysD
|
|||||||
SysDictDataEntity entity = ConvertUtils.sourceToTarget(dto, SysDictDataEntity.class);
|
SysDictDataEntity entity = ConvertUtils.sourceToTarget(dto, SysDictDataEntity.class);
|
||||||
|
|
||||||
insert(entity);
|
insert(entity);
|
||||||
|
// 删除Redis缓存
|
||||||
|
String dictType = baseDao.getTypeByTypeId(dto.getDictTypeId());
|
||||||
|
redisUtils.delete(RedisKeys.getDictDataByTypeKey(dictType));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -89,13 +95,30 @@ public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysD
|
|||||||
SysDictDataEntity entity = ConvertUtils.sourceToTarget(dto, SysDictDataEntity.class);
|
SysDictDataEntity entity = ConvertUtils.sourceToTarget(dto, SysDictDataEntity.class);
|
||||||
|
|
||||||
updateById(entity);
|
updateById(entity);
|
||||||
|
// 删除Redis缓存
|
||||||
|
String dictType = baseDao.getTypeByTypeId(dto.getDictTypeId());
|
||||||
|
redisUtils.delete(RedisKeys.getDictDataByTypeKey(dictType));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void delete(Long[] ids) {
|
public void delete(Long[] ids) {
|
||||||
// 删除
|
for (Long id : ids) {
|
||||||
deleteBatchIds(Arrays.asList(ids));
|
SysDictDataEntity entity = baseDao.selectById(id);
|
||||||
|
// 删除Redis缓存
|
||||||
|
String dictType = baseDao.getTypeByTypeId(entity.getDictTypeId());
|
||||||
|
redisUtils.delete(RedisKeys.getDictDataByTypeKey(dictType));
|
||||||
|
// 删除
|
||||||
|
deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void deleteByTypeId(Long dictTypeId) {
|
||||||
|
LambdaQueryWrapper<SysDictDataEntity> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(SysDictDataEntity::getDictTypeId, dictTypeId);
|
||||||
|
baseDao.delete(wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -106,14 +129,14 @@ public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysD
|
|||||||
private void setUserName(List<SysDictDataVO> sysDictDataList) {
|
private void setUserName(List<SysDictDataVO> sysDictDataList) {
|
||||||
// 收集所有用户 ID
|
// 收集所有用户 ID
|
||||||
Set<Long> userIds = sysDictDataList.stream().flatMap(vo -> Stream.of(vo.getCreator(), vo.getUpdater()))
|
Set<Long> userIds = sysDictDataList.stream().flatMap(vo -> Stream.of(vo.getCreator(), vo.getUpdater()))
|
||||||
.filter(Objects::nonNull).collect(Collectors.toSet());
|
.filter(Objects::nonNull).collect(Collectors.toSet());
|
||||||
|
|
||||||
// 设置更新者和创建者名称
|
// 设置更新者和创建者名称
|
||||||
if (!userIds.isEmpty()) {
|
if (!userIds.isEmpty()) {
|
||||||
List<SysUserEntity> sysUserEntities = sysUserDao.selectBatchIds(userIds);
|
List<SysUserEntity> sysUserEntities = sysUserDao.selectBatchIds(userIds);
|
||||||
// 把List转成Map,Map<Long, String>
|
// 把List转成Map,Map<Long, String>
|
||||||
Map<Long, String> userNameMap = sysUserEntities.stream().collect(Collectors.toMap(SysUserEntity::getId,
|
Map<Long, String> userNameMap = sysUserEntities.stream().collect(Collectors.toMap(SysUserEntity::getId,
|
||||||
SysUserEntity::getUsername, (existing, replacement) -> existing));
|
SysUserEntity::getUsername, (existing, replacement) -> existing));
|
||||||
|
|
||||||
sysDictDataList.forEach(vo -> {
|
sysDictDataList.forEach(vo -> {
|
||||||
vo.setCreatorName(userNameMap.get(vo.getCreator()));
|
vo.setCreatorName(userNameMap.get(vo.getCreator()));
|
||||||
@@ -133,4 +156,28 @@ public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysD
|
|||||||
throw new RenException("字典标签重复");
|
throw new RenException("字典标签重复");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SysDictDataItem> getDictDataByType(String dictType) {
|
||||||
|
if (StringUtils.isBlank(dictType)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先从Redis获取缓存
|
||||||
|
String key = RedisKeys.getDictDataByTypeKey(dictType);
|
||||||
|
List<SysDictDataItem> cachedData = (List<SysDictDataItem>) redisUtils.get(key);
|
||||||
|
if (cachedData != null) {
|
||||||
|
return cachedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果缓存中没有,则从数据库获取
|
||||||
|
List<SysDictDataItem> data = baseDao.getDictDataByType(dictType);
|
||||||
|
|
||||||
|
// 存入Redis缓存
|
||||||
|
if (data != null) {
|
||||||
|
redisUtils.set(key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+12
-7
@@ -26,6 +26,7 @@ import xiaozhi.modules.sys.dao.SysUserDao;
|
|||||||
import xiaozhi.modules.sys.dto.SysDictTypeDTO;
|
import xiaozhi.modules.sys.dto.SysDictTypeDTO;
|
||||||
import xiaozhi.modules.sys.entity.SysDictTypeEntity;
|
import xiaozhi.modules.sys.entity.SysDictTypeEntity;
|
||||||
import xiaozhi.modules.sys.entity.SysUserEntity;
|
import xiaozhi.modules.sys.entity.SysUserEntity;
|
||||||
|
import xiaozhi.modules.sys.service.SysDictDataService;
|
||||||
import xiaozhi.modules.sys.service.SysDictTypeService;
|
import xiaozhi.modules.sys.service.SysDictTypeService;
|
||||||
import xiaozhi.modules.sys.vo.SysDictTypeVO;
|
import xiaozhi.modules.sys.vo.SysDictTypeVO;
|
||||||
|
|
||||||
@@ -35,9 +36,9 @@ import xiaozhi.modules.sys.vo.SysDictTypeVO;
|
|||||||
@Service
|
@Service
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class SysDictTypeServiceImpl extends BaseServiceImpl<SysDictTypeDao, SysDictTypeEntity>
|
public class SysDictTypeServiceImpl extends BaseServiceImpl<SysDictTypeDao, SysDictTypeEntity>
|
||||||
implements SysDictTypeService {
|
implements SysDictTypeService {
|
||||||
|
|
||||||
private final SysUserDao sysUserDao;
|
private final SysUserDao sysUserDao;
|
||||||
|
private final SysDictDataService sysDictDataService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageData<SysDictTypeVO> page(Map<String, Object> params) {
|
public PageData<SysDictTypeVO> page(Map<String, Object> params) {
|
||||||
@@ -51,8 +52,8 @@ public class SysDictTypeServiceImpl extends BaseServiceImpl<SysDictTypeDao, SysD
|
|||||||
}
|
}
|
||||||
|
|
||||||
private QueryWrapper<SysDictTypeEntity> getWrapper(Map<String, Object> params) {
|
private QueryWrapper<SysDictTypeEntity> getWrapper(Map<String, Object> params) {
|
||||||
String dictType = (String)params.get("dictType");
|
String dictType = (String) params.get("dictType");
|
||||||
String dictName = (String)params.get("dictName");
|
String dictName = (String) params.get("dictName");
|
||||||
|
|
||||||
QueryWrapper<SysDictTypeEntity> wrapper = new QueryWrapper<>();
|
QueryWrapper<SysDictTypeEntity> wrapper = new QueryWrapper<>();
|
||||||
wrapper.like(StringUtils.isNotBlank(dictType), "dict_type", dictType);
|
wrapper.like(StringUtils.isNotBlank(dictType), "dict_type", dictType);
|
||||||
@@ -96,7 +97,11 @@ public class SysDictTypeServiceImpl extends BaseServiceImpl<SysDictTypeDao, SysD
|
|||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void delete(Long[] ids) {
|
public void delete(Long[] ids) {
|
||||||
// 删除
|
// 先删除对应的字典数据
|
||||||
|
for (Long id : ids) {
|
||||||
|
sysDictDataService.deleteByTypeId(id);
|
||||||
|
}
|
||||||
|
// 再删除字典类型
|
||||||
deleteBatchIds(Arrays.asList(ids));
|
deleteBatchIds(Arrays.asList(ids));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,14 +123,14 @@ public class SysDictTypeServiceImpl extends BaseServiceImpl<SysDictTypeDao, SysD
|
|||||||
private void setUserName(List<SysDictTypeVO> sysDictTypeList) {
|
private void setUserName(List<SysDictTypeVO> sysDictTypeList) {
|
||||||
// 收集所有用户 ID
|
// 收集所有用户 ID
|
||||||
Set<Long> userIds = sysDictTypeList.stream().flatMap(vo -> Stream.of(vo.getCreator(), vo.getUpdater()))
|
Set<Long> userIds = sysDictTypeList.stream().flatMap(vo -> Stream.of(vo.getCreator(), vo.getUpdater()))
|
||||||
.filter(Objects::nonNull).collect(Collectors.toSet());
|
.filter(Objects::nonNull).collect(Collectors.toSet());
|
||||||
|
|
||||||
// 设置更新者和创建者名称
|
// 设置更新者和创建者名称
|
||||||
if (!userIds.isEmpty()) {
|
if (!userIds.isEmpty()) {
|
||||||
List<SysUserEntity> sysUserEntities = sysUserDao.selectBatchIds(userIds);
|
List<SysUserEntity> sysUserEntities = sysUserDao.selectBatchIds(userIds);
|
||||||
// 把List转成Map,Map<Long, String>
|
// 把List转成Map,Map<Long, String>
|
||||||
Map<Long, String> userNameMap = sysUserEntities.stream().collect(Collectors.toMap(SysUserEntity::getId,
|
Map<Long, String> userNameMap = sysUserEntities.stream().collect(Collectors.toMap(SysUserEntity::getId,
|
||||||
SysUserEntity::getUsername, (existing, replacement) -> existing));
|
SysUserEntity::getUsername, (existing, replacement) -> existing));
|
||||||
|
|
||||||
sysDictTypeList.forEach(vo -> {
|
sysDictTypeList.forEach(vo -> {
|
||||||
vo.setCreatorName(userNameMap.get(vo.getCreator()));
|
vo.setCreatorName(userNameMap.get(vo.getCreator()));
|
||||||
|
|||||||
+1
@@ -169,6 +169,7 @@ public class SysUserServiceImpl extends BaseServiceImpl<SysUserDao, SysUserEntit
|
|||||||
String deviceCount = deviceService.selectCountByUserId(user.getId()).toString();
|
String deviceCount = deviceService.selectCountByUserId(user.getId()).toString();
|
||||||
adminPageUserVO.setDeviceCount(deviceCount);
|
adminPageUserVO.setDeviceCount(deviceCount);
|
||||||
adminPageUserVO.setStatus(user.getStatus());
|
adminPageUserVO.setStatus(user.getStatus());
|
||||||
|
adminPageUserVO.setCreateDate(user.getCreateDate());
|
||||||
return adminPageUserVO;
|
return adminPageUserVO;
|
||||||
}).toList();
|
}).toList();
|
||||||
return new PageData<>(list, page.getTotal());
|
return new PageData<>(list, page.getTotal());
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package xiaozhi.modules.sys.vo;
|
package xiaozhi.modules.sys.vo;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -23,4 +25,7 @@ public class AdminPageUserVO {
|
|||||||
|
|
||||||
@Schema(description = "用户id")
|
@Schema(description = "用户id")
|
||||||
private String userid;
|
private String userid;
|
||||||
|
|
||||||
|
@Schema(description = "注册时间")
|
||||||
|
private Date createDate;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package xiaozhi.modules.sys.vo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典数据VO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "字典数据项")
|
||||||
|
public class SysDictDataItem implements Serializable {
|
||||||
|
|
||||||
|
@Schema(description = "字典标签")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "字典值")
|
||||||
|
private String key;
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
update `ai_model_provider` set `fields` =
|
|
||||||
'[{"key": "api_url","label": "API地址","type": "string"},{"key": "voice","label": "音色","type": "string"},{"key": "output_dir","label": "输出目录","type": "string"},{"key": "authorization","label": "授权","type": "string"},{"key": "appid","label": "应用ID","type": "string"},{"key": "access_token","label": "访问令牌","type": "string"},{"key": "cluster","label": "集群","type": "string"},{"key": "speed_ratio","label": "语速","type": "number"},{"key": "volume_ratio","label": "音量","type": "number"},{"key": "pitch_ratio","label": "音高","type": "number"}]'
|
|
||||||
where `id` = 'SYSTEM_TTS_doubao';
|
|
||||||
|
|
||||||
-- 添加阿里云ASR供应器
|
|
||||||
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
|
|
||||||
('SYSTEM_ASR_AliyunASR', 'ASR', 'aliyun', '阿里云语音识别', '[{"key":"appkey","label":"应用AppKey","type":"string"},{"key":"token","label":"临时Token","type":"string"},{"key":"access_key_id","label":"AccessKey ID","type":"string"},{"key":"access_key_secret","label":"AccessKey Secret","type":"string"},{"key":"output_dir","label":"输出目录","type":"string"}]', 5, 1, NOW(), 1, NOW());
|
|
||||||
|
|
||||||
-- 添加阿里云ASR模型配置
|
|
||||||
INSERT INTO `ai_model_config` VALUES ('ASR_AliyunASR', 'ASR', 'AliyunASR', '阿里云语音识别', 0, 1, '{\"type\": \"aliyun\", \"appkey\": \"\", \"token\": \"\", \"access_key_id\": \"\", \"access_key_secret\": \"\", \"output_dir\": \"tmp/\"}', NULL, NULL, 6, NULL, NULL, NULL, NULL);
|
|
||||||
|
|
||||||
-- 更新阿里云ASR模型配置的说明文档
|
|
||||||
UPDATE `ai_model_config` SET
|
|
||||||
`doc_link` = 'https://nls-portal.console.aliyun.com/',
|
|
||||||
`remark` = '阿里云ASR配置说明:
|
|
||||||
1. 访问 https://nls-portal.console.aliyun.com/ 开通服务
|
|
||||||
2. 访问 https://nls-portal.console.aliyun.com/applist 获取appkey
|
|
||||||
3. 访问 https://nls-portal.console.aliyun.com/overview 获取token
|
|
||||||
4. 获取access_key_id和access_key_secret
|
|
||||||
5. 填入配置文件中' WHERE `id` = 'ASR_AliyunASR';
|
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
update `ai_model_provider` set `fields` =
|
||||||
|
'[{"key": "api_url","label": "API地址","type": "string"},{"key": "voice","label": "音色","type": "string"},{"key": "output_dir","label": "输出目录","type": "string"},{"key": "authorization","label": "授权","type": "string"},{"key": "appid","label": "应用ID","type": "string"},{"key": "access_token","label": "访问令牌","type": "string"},{"key": "cluster","label": "集群","type": "string"},{"key": "speed_ratio","label": "语速","type": "number"},{"key": "volume_ratio","label": "音量","type": "number"},{"key": "pitch_ratio","label": "音高","type": "number"}]'
|
||||||
|
where `id` = 'SYSTEM_TTS_doubao';
|
||||||
|
|
||||||
|
-- 添加阿里云ASR供应器
|
||||||
|
delete from `ai_model_provider` where `id` = 'SYSTEM_ASR_AliyunASR';
|
||||||
|
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
|
||||||
|
('SYSTEM_ASR_AliyunASR', 'ASR', 'aliyun', '阿里云语音识别', '[{"key":"appkey","label":"应用AppKey","type":"string"},{"key":"token","label":"临时Token","type":"string"},{"key":"access_key_id","label":"AccessKey ID","type":"string"},{"key":"access_key_secret","label":"AccessKey Secret","type":"string"},{"key":"output_dir","label":"输出目录","type":"string"}]', 5, 1, NOW(), 1, NOW());
|
||||||
|
|
||||||
|
-- 添加阿里云ASR模型配置
|
||||||
|
delete from `ai_model_config` where `id` = 'ASR_AliyunASR';
|
||||||
|
INSERT INTO `ai_model_config` VALUES ('ASR_AliyunASR', 'ASR', 'AliyunASR', '阿里云语音识别', 0, 1, '{\"type\": \"aliyun\", \"appkey\": \"\", \"token\": \"\", \"access_key_id\": \"\", \"access_key_secret\": \"\", \"output_dir\": \"tmp/\"}', NULL, NULL, 6, NULL, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
-- 更新阿里云ASR模型配置的说明文档
|
||||||
|
UPDATE `ai_model_config` SET
|
||||||
|
`doc_link` = 'https://nls-portal.console.aliyun.com/',
|
||||||
|
`remark` = '阿里云ASR配置说明:
|
||||||
|
1. 访问 https://nls-portal.console.aliyun.com/ 开通服务
|
||||||
|
2. 访问 https://nls-portal.console.aliyun.com/applist 获取appkey
|
||||||
|
3. 访问 https://nls-portal.console.aliyun.com/overview 获取token
|
||||||
|
4. 获取access_key_id和access_key_secret
|
||||||
|
5. 填入配置文件中' WHERE `id` = 'ASR_AliyunASR';
|
||||||
|
|
||||||
|
-- 插入固件类型字典类型
|
||||||
|
delete from `sys_dict_type` where `id` = 101;
|
||||||
|
INSERT INTO `sys_dict_type` (`id`, `dict_type`, `dict_name`, `remark`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
|
||||||
|
(101, 'FIRMWARE_TYPE', '固件类型', '固件类型字典', 0, 1, NOW(), 1, NOW());
|
||||||
|
|
||||||
|
-- 插入固件类型字典数据
|
||||||
|
delete from `sys_dict_data` where `dict_type_id` = 101;
|
||||||
|
INSERT INTO `sys_dict_data` (`id`, `dict_type_id`, `dict_label`, `dict_value`, `remark`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
|
||||||
|
(101001, 101, '面包板新版接线(WiFi)', 'bread-compact-wifi', '面包板新版接线(WiFi)', 1, 1, NOW(), 1, NOW()),
|
||||||
|
(101002, 101, '面包板新版接线(WiFi)+ LCD', 'bread-compact-wifi-lcd', '面包板新版接线(WiFi)+ LCD', 2, 1, NOW(), 1, NOW()),
|
||||||
|
(101003, 101, '面包板新版接线(ML307 AT)', 'bread-compact-ml307', '面包板新版接线(ML307 AT)', 3, 1, NOW(), 1, NOW()),
|
||||||
|
(101004, 101, '面包板(WiFi) ESP32 DevKit', 'bread-compact-esp32', '面包板(WiFi) ESP32 DevKit', 4, 1, NOW(), 1, NOW()),
|
||||||
|
(101005, 101, '面包板(WiFi+ LCD) ESP32 DevKit', 'bread-compact-esp32-lcd', '面包板(WiFi+ LCD) ESP32 DevKit', 5, 1, NOW(), 1, NOW()),
|
||||||
|
(101006, 101, 'DFRobot 行空板 k10', 'df-k10', 'DFRobot 行空板 k10', 6, 1, NOW(), 1, NOW()),
|
||||||
|
(101007, 101, 'ESP32 CGC', 'esp32-cgc', 'ESP32 CGC', 7, 1, NOW(), 1, NOW()),
|
||||||
|
(101008, 101, 'ESP BOX 3', 'esp-box-3', 'ESP BOX 3', 8, 1, NOW(), 1, NOW()),
|
||||||
|
(101009, 101, 'ESP BOX', 'esp-box', 'ESP BOX', 9, 1, NOW(), 1, NOW()),
|
||||||
|
(101010, 101, 'ESP BOX Lite', 'esp-box-lite', 'ESP BOX Lite', 10, 1, NOW(), 1, NOW()),
|
||||||
|
(101011, 101, 'Kevin Box 1', 'kevin-box-1', 'Kevin Box 1', 11, 1, NOW(), 1, NOW()),
|
||||||
|
(101012, 101, 'Kevin Box 2', 'kevin-box-2', 'Kevin Box 2', 12, 1, NOW(), 1, NOW()),
|
||||||
|
(101013, 101, 'Kevin C3', 'kevin-c3', 'Kevin C3', 13, 1, NOW(), 1, NOW()),
|
||||||
|
(101014, 101, 'Kevin SP V3开发板', 'kevin-sp-v3-dev', 'Kevin SP V3开发板', 14, 1, NOW(), 1, NOW()),
|
||||||
|
(101015, 101, 'Kevin SP V4开发板', 'kevin-sp-v4-dev', 'Kevin SP V4开发板', 15, 1, NOW(), 1, NOW()),
|
||||||
|
(101016, 101, '鱼鹰科技3.13LCD开发板', 'kevin-yuying-313lcd', '鱼鹰科技3.13LCD开发板', 16, 1, NOW(), 1, NOW()),
|
||||||
|
(101017, 101, '立创·实战派ESP32-S3开发板', 'lichuang-dev', '立创·实战派ESP32-S3开发板', 17, 1, NOW(), 1, NOW()),
|
||||||
|
(101018, 101, '立创·实战派ESP32-C3开发板', 'lichuang-c3-dev', '立创·实战派ESP32-C3开发板', 18, 1, NOW(), 1, NOW()),
|
||||||
|
(101019, 101, '神奇按钮 Magiclick_2.4', 'magiclick-2p4', '神奇按钮 Magiclick_2.4', 19, 1, NOW(), 1, NOW()),
|
||||||
|
(101020, 101, '神奇按钮 Magiclick_2.5', 'magiclick-2p5', '神奇按钮 Magiclick_2.5', 20, 1, NOW(), 1, NOW()),
|
||||||
|
(101021, 101, '神奇按钮 Magiclick_C3', 'magiclick-c3', '神奇按钮 Magiclick_C3', 21, 1, NOW(), 1, NOW()),
|
||||||
|
(101022, 101, '神奇按钮 Magiclick_C3_v2', 'magiclick-c3-v2', '神奇按钮 Magiclick_C3_v2', 22, 1, NOW(), 1, NOW()),
|
||||||
|
(101023, 101, 'M5Stack CoreS3', 'm5stack-core-s3', 'M5Stack CoreS3', 23, 1, NOW(), 1, NOW()),
|
||||||
|
(101024, 101, 'AtomS3 + Echo Base', 'atoms3-echo-base', 'AtomS3 + Echo Base', 24, 1, NOW(), 1, NOW()),
|
||||||
|
(101025, 101, 'AtomS3R + Echo Base', 'atoms3r-echo-base', 'AtomS3R + Echo Base', 25, 1, NOW(), 1, NOW()),
|
||||||
|
(101026, 101, 'AtomS3R CAM/M12 + Echo Base', 'atoms3r-cam-m12-echo-base', 'AtomS3R CAM/M12 + Echo Base', 26, 1, NOW(), 1, NOW()),
|
||||||
|
(101027, 101, 'AtomMatrix + Echo Base', 'atommatrix-echo-base', 'AtomMatrix + Echo Base', 27, 1, NOW(), 1, NOW()),
|
||||||
|
(101028, 101, '虾哥 Mini C3', 'xmini-c3', '虾哥 Mini C3', 28, 1, NOW(), 1, NOW()),
|
||||||
|
(101029, 101, 'ESP32S3_KORVO2_V3开发板', 'esp32s3-korvo2-v3', 'ESP32S3_KORVO2_V3开发板', 29, 1, NOW(), 1, NOW()),
|
||||||
|
(101030, 101, 'ESP-SparkBot开发板', 'esp-sparkbot', 'ESP-SparkBot开发板', 30, 1, NOW(), 1, NOW()),
|
||||||
|
(101031, 101, 'ESP-Spot-S3', 'esp-spot-s3', 'ESP-Spot-S3', 31, 1, NOW(), 1, NOW()),
|
||||||
|
(101032, 101, 'Waveshare ESP32-S3-Touch-AMOLED-1.8', 'esp32-s3-touch-amoled-1.8', 'Waveshare ESP32-S3-Touch-AMOLED-1.8', 32, 1, NOW(), 1, NOW()),
|
||||||
|
(101033, 101, 'Waveshare ESP32-S3-Touch-LCD-1.85C', 'esp32-s3-touch-lcd-1.85c', 'Waveshare ESP32-S3-Touch-LCD-1.85C', 33, 1, NOW(), 1, NOW()),
|
||||||
|
(101034, 101, 'Waveshare ESP32-S3-Touch-LCD-1.85', 'esp32-s3-touch-lcd-1.85', 'Waveshare ESP32-S3-Touch-LCD-1.85', 34, 1, NOW(), 1, NOW()),
|
||||||
|
(101035, 101, 'Waveshare ESP32-S3-Touch-LCD-1.46', 'esp32-s3-touch-lcd-1.46', 'Waveshare ESP32-S3-Touch-LCD-1.46', 35, 1, NOW(), 1, NOW()),
|
||||||
|
(101036, 101, 'Waveshare ESP32-S3-Touch-LCD-3.5', 'esp32-s3-touch-lcd-3.5', 'Waveshare ESP32-S3-Touch-LCD-3.5', 36, 1, NOW(), 1, NOW()),
|
||||||
|
(101037, 101, '土豆子', 'tudouzi', '土豆子', 37, 1, NOW(), 1, NOW()),
|
||||||
|
(101038, 101, 'LILYGO T-Circle-S3', 'lilygo-t-circle-s3', 'LILYGO T-Circle-S3', 38, 1, NOW(), 1, NOW()),
|
||||||
|
(101039, 101, 'LILYGO T-CameraPlus-S3', 'lilygo-t-cameraplus-s3', 'LILYGO T-CameraPlus-S3', 39, 1, NOW(), 1, NOW()),
|
||||||
|
(101040, 101, 'Movecall Moji 小智AI衍生版', 'movecall-moji-esp32s3', 'Movecall Moji 小智AI衍生版', 40, 1, NOW(), 1, NOW()),
|
||||||
|
(101041, 101, 'Movecall CuiCan 璀璨·AI吊坠', 'movecall-cuican-esp32s3', 'Movecall CuiCan 璀璨·AI吊坠', 41, 1, NOW(), 1, NOW()),
|
||||||
|
(101042, 101, '正点原子DNESP32S3开发板', 'atk-dnesp32s3', '正点原子DNESP32S3开发板', 42, 1, NOW(), 1, NOW()),
|
||||||
|
(101043, 101, '正点原子DNESP32S3-BOX', 'atk-dnesp32s3-box', '正点原子DNESP32S3-BOX', 43, 1, NOW(), 1, NOW()),
|
||||||
|
(101044, 101, '嘟嘟开发板CHATX(wifi)', 'du-chatx', '嘟嘟开发板CHATX(wifi)', 44, 1, NOW(), 1, NOW()),
|
||||||
|
(101045, 101, '太极小派esp32s3', 'taiji-pi-s3', '太极小派esp32s3', 45, 1, NOW(), 1, NOW()),
|
||||||
|
(101046, 101, '无名科技星智0.85(WIFI)', 'xingzhi-cube-0.85tft-wifi', '无名科技星智0.85(WIFI)', 46, 1, NOW(), 1, NOW()),
|
||||||
|
(101047, 101, '无名科技星智0.85(ML307)', 'xingzhi-cube-0.85tft-ml307', '无名科技星智0.85(ML307)', 47, 1, NOW(), 1, NOW()),
|
||||||
|
(101048, 101, '无名科技星智0.96(WIFI)', 'xingzhi-cube-0.96oled-wifi', '无名科技星智0.96(WIFI)', 48, 1, NOW(), 1, NOW()),
|
||||||
|
(101049, 101, '无名科技星智0.96(ML307)', 'xingzhi-cube-0.96oled-ml307', '无名科技星智0.96(ML307)', 49, 1, NOW(), 1, NOW()),
|
||||||
|
(101050, 101, '无名科技星智1.54(WIFI)', 'xingzhi-cube-1.54tft-wifi', '无名科技星智1.54(WIFI)', 50, 1, NOW(), 1, NOW()),
|
||||||
|
(101051, 101, '无名科技星智1.54(ML307)', 'xingzhi-cube-1.54tft-ml307', '无名科技星智1.54(ML307)', 51, 1, NOW(), 1, NOW()),
|
||||||
|
(101052, 101, 'SenseCAP Watcher', 'sensecap-watcher', 'SenseCAP Watcher', 52, 1, NOW(), 1, NOW()),
|
||||||
|
(101053, 101, '四博智联AI陪伴盒子', 'doit-s3-aibox', '四博智联AI陪伴盒子', 53, 1, NOW(), 1, NOW()),
|
||||||
|
(101054, 101, '元控·青春', 'mixgo-nova', '元控·青春', 54, 1, NOW(), 1, NOW());
|
||||||
@@ -87,9 +87,9 @@ databaseChangeLog:
|
|||||||
encoding: utf8
|
encoding: utf8
|
||||||
path: classpath:db/changelog/202504291043.sql
|
path: classpath:db/changelog/202504291043.sql
|
||||||
- changeSet:
|
- changeSet:
|
||||||
id: 202504301340
|
id: 202504301341
|
||||||
author: Goody
|
author: Goody
|
||||||
changes:
|
changes:
|
||||||
- sqlFile:
|
- sqlFile:
|
||||||
encoding: utf8
|
encoding: utf8
|
||||||
path: classpath:db/changelog/202504301340.sql
|
path: classpath:db/changelog/202504301341.sql
|
||||||
@@ -2,5 +2,17 @@
|
|||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
|
||||||
<mapper namespace="xiaozhi.modules.sys.dao.SysDictDataDao">
|
<mapper namespace="xiaozhi.modules.sys.dao.SysDictDataDao">
|
||||||
|
<select id="getDictDataByType" resultType="xiaozhi.modules.sys.vo.SysDictDataItem">
|
||||||
|
SELECT d.dict_label AS `name`, d.dict_value AS `key`
|
||||||
|
FROM sys_dict_data d
|
||||||
|
LEFT JOIN sys_dict_type t ON d.dict_type_id = t.id
|
||||||
|
WHERE t.dict_type = #{dictType}
|
||||||
|
ORDER BY d.sort ASC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="getTypeByTypeId" resultType="java.lang.String">
|
||||||
|
SELECT dict_type
|
||||||
|
FROM sys_dict_type
|
||||||
|
WHERE id = #{dictTypeId}
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -2,11 +2,11 @@
|
|||||||
import admin from './module/admin.js'
|
import admin from './module/admin.js'
|
||||||
import agent from './module/agent.js'
|
import agent from './module/agent.js'
|
||||||
import device from './module/device.js'
|
import device from './module/device.js'
|
||||||
|
import dict from './module/dict.js'
|
||||||
import model from './module/model.js'
|
import model from './module/model.js'
|
||||||
|
import ota from './module/ota.js'
|
||||||
import timbre from "./module/timbre.js"
|
import timbre from "./module/timbre.js"
|
||||||
import user from './module/user.js'
|
import user from './module/user.js'
|
||||||
import ota from './module/ota.js'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 接口地址
|
* 接口地址
|
||||||
* 开发时自动读取使用.env.development文件
|
* 开发时自动读取使用.env.development文件
|
||||||
@@ -32,5 +32,6 @@ export default {
|
|||||||
device,
|
device,
|
||||||
model,
|
model,
|
||||||
timbre,
|
timbre,
|
||||||
ota
|
ota,
|
||||||
|
dict
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,227 @@
|
|||||||
|
import { getServiceUrl } from '../api';
|
||||||
|
import RequestService from '../httpRequest';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// 获取字典类型列表
|
||||||
|
getDictTypeList(params, callback) {
|
||||||
|
const queryParams = new URLSearchParams({
|
||||||
|
dictType: params.dictType || '',
|
||||||
|
dictName: params.dictName || '',
|
||||||
|
page: params.page || 1,
|
||||||
|
limit: params.limit || 10
|
||||||
|
}).toString();
|
||||||
|
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/admin/dict/type/page?${queryParams}`)
|
||||||
|
.method('GET')
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('获取字典类型列表失败:', err)
|
||||||
|
this.$message.error(err.msg || '获取字典类型列表失败')
|
||||||
|
RequestService.reAjaxFun(() => {
|
||||||
|
this.getDictTypeList(params, callback)
|
||||||
|
})
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取字典类型详情
|
||||||
|
getDictTypeDetail(id, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/admin/dict/type/${id}`)
|
||||||
|
.method('GET')
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('获取字典类型详情失败:', err)
|
||||||
|
this.$message.error(err.msg || '获取字典类型详情失败')
|
||||||
|
RequestService.reAjaxFun(() => {
|
||||||
|
this.getDictTypeDetail(id, callback)
|
||||||
|
})
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增字典类型
|
||||||
|
addDictType(data, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/admin/dict/type/save`)
|
||||||
|
.method('POST')
|
||||||
|
.data(data)
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('新增字典类型失败:', err)
|
||||||
|
this.$message.error(err.msg || '新增字典类型失败')
|
||||||
|
RequestService.reAjaxFun(() => {
|
||||||
|
this.addDictType(data, callback)
|
||||||
|
})
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新字典类型
|
||||||
|
updateDictType(data, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/admin/dict/type/update`)
|
||||||
|
.method('PUT')
|
||||||
|
.data(data)
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('更新字典类型失败:', err)
|
||||||
|
this.$message.error(err.msg || '更新字典类型失败')
|
||||||
|
RequestService.reAjaxFun(() => {
|
||||||
|
this.updateDictType(data, callback)
|
||||||
|
})
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除字典类型
|
||||||
|
deleteDictType(ids, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/admin/dict/type/delete`)
|
||||||
|
.method('POST')
|
||||||
|
.data(ids)
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('删除字典类型失败:', err)
|
||||||
|
this.$message.error(err.msg || '删除字典类型失败')
|
||||||
|
RequestService.reAjaxFun(() => {
|
||||||
|
this.deleteDictType(ids, callback)
|
||||||
|
})
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取字典数据列表
|
||||||
|
getDictDataList(params, callback) {
|
||||||
|
const queryParams = new URLSearchParams({
|
||||||
|
dictTypeId: params.dictTypeId,
|
||||||
|
dictLabel: params.dictLabel || '',
|
||||||
|
dictValue: params.dictValue || '',
|
||||||
|
page: params.page || 1,
|
||||||
|
limit: params.limit || 10
|
||||||
|
}).toString();
|
||||||
|
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/admin/dict/data/page?${queryParams}`)
|
||||||
|
.method('GET')
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('获取字典数据列表失败:', err)
|
||||||
|
this.$message.error(err.msg || '获取字典数据列表失败')
|
||||||
|
RequestService.reAjaxFun(() => {
|
||||||
|
this.getDictDataList(params, callback)
|
||||||
|
})
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取字典数据详情
|
||||||
|
getDictDataDetail(id, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/admin/dict/data/${id}`)
|
||||||
|
.method('GET')
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('获取字典数据详情失败:', err)
|
||||||
|
this.$message.error(err.msg || '获取字典数据详情失败')
|
||||||
|
RequestService.reAjaxFun(() => {
|
||||||
|
this.getDictDataDetail(id, callback)
|
||||||
|
})
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增字典数据
|
||||||
|
addDictData(data, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/admin/dict/data/save`)
|
||||||
|
.method('POST')
|
||||||
|
.data(data)
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('新增字典数据失败:', err)
|
||||||
|
this.$message.error(err.msg || '新增字典数据失败')
|
||||||
|
RequestService.reAjaxFun(() => {
|
||||||
|
this.addDictData(data, callback)
|
||||||
|
})
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新字典数据
|
||||||
|
updateDictData(data, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/admin/dict/data/update`)
|
||||||
|
.method('PUT')
|
||||||
|
.data(data)
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('更新字典数据失败:', err)
|
||||||
|
this.$message.error(err.msg || '更新字典数据失败')
|
||||||
|
RequestService.reAjaxFun(() => {
|
||||||
|
this.updateDictData(data, callback)
|
||||||
|
})
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除字典数据
|
||||||
|
deleteDictData(ids, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/admin/dict/data/delete`)
|
||||||
|
.method('POST')
|
||||||
|
.data(ids)
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('删除字典数据失败:', err)
|
||||||
|
this.$message.error(err.msg || '删除字典数据失败')
|
||||||
|
RequestService.reAjaxFun(() => {
|
||||||
|
this.deleteDictData(ids, callback)
|
||||||
|
})
|
||||||
|
}).send()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取字典数据列表
|
||||||
|
getDictDataByType(dictType) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/admin/dict/data/type/${dictType}`)
|
||||||
|
.method('GET')
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
if (res.data && res.data.code === 0) {
|
||||||
|
resolve(res.data)
|
||||||
|
} else {
|
||||||
|
reject(new Error(res.data?.msg || '获取字典数据列表失败'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('获取字典数据列表失败:', err)
|
||||||
|
reject(err)
|
||||||
|
}).send()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog :title="title" :visible.sync="visible" width="500px" @close="handleClose">
|
||||||
|
<el-form :model="form" :rules="rules" ref="form" label-width="100px">
|
||||||
|
<el-form-item label="字典标签" prop="dictLabel">
|
||||||
|
<el-input v-model="form.dictLabel" placeholder="请输入字典标签"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="字典值" prop="dictValue">
|
||||||
|
<el-input v-model="form.dictValue" placeholder="请输入字典值"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序" prop="sort">
|
||||||
|
<el-input-number v-model="form.sort" :min="0" :max="999" style="width: 100%;"></el-input-number>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="handleClose">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="handleSave">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'DictDataDialog',
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '新增字典数据'
|
||||||
|
},
|
||||||
|
dictData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
dictTypeId: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
id: null,
|
||||||
|
dictTypeId: null,
|
||||||
|
dictLabel: '',
|
||||||
|
dictValue: '',
|
||||||
|
sort: 0
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
dictLabel: [{ required: true, message: '请输入字典标签', trigger: 'blur' }],
|
||||||
|
dictValue: [{ required: true, message: '请输入字典值', trigger: 'blur' }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
dictData: {
|
||||||
|
handler(val) {
|
||||||
|
if (val) {
|
||||||
|
this.form = { ...val }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
dictTypeId: {
|
||||||
|
handler(val) {
|
||||||
|
if (val) {
|
||||||
|
this.form.dictTypeId = val
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleClose() {
|
||||||
|
this.$emit('update:visible', false)
|
||||||
|
this.resetForm()
|
||||||
|
},
|
||||||
|
resetForm() {
|
||||||
|
this.form = {
|
||||||
|
id: null,
|
||||||
|
dictTypeId: this.dictTypeId,
|
||||||
|
dictLabel: '',
|
||||||
|
dictValue: '',
|
||||||
|
sort: 0
|
||||||
|
}
|
||||||
|
this.$refs.form?.resetFields()
|
||||||
|
},
|
||||||
|
handleSave() {
|
||||||
|
this.$refs.form.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.$emit('save', this.form)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dialog-footer {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog :title="title" :visible.sync="visible" width="500px" @close="handleClose">
|
||||||
|
<el-form :model="form" :rules="rules" ref="form" label-width="120px">
|
||||||
|
<el-form-item label="字典类型名称" prop="dictName">
|
||||||
|
<el-input v-model="form.dictName" placeholder="请输入字典类型名称"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="字典类型编码" prop="dictType">
|
||||||
|
<el-input v-model="form.dictType" placeholder="请输入字典类型编码"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="handleClose">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="handleSave">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'DictTypeDialog',
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '新增字典类型'
|
||||||
|
},
|
||||||
|
dictTypeData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
id: null,
|
||||||
|
dictName: '',
|
||||||
|
dictType: ''
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
dictName: [{ required: true, message: '请输入字典类型名称', trigger: 'blur' }],
|
||||||
|
dictType: [{ required: true, message: '请输入字典类型编码', trigger: 'blur' }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
dictTypeData: {
|
||||||
|
handler(val) {
|
||||||
|
if (val) {
|
||||||
|
this.form = { ...val }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleClose() {
|
||||||
|
this.$emit('update:visible', false)
|
||||||
|
this.resetForm()
|
||||||
|
},
|
||||||
|
resetForm() {
|
||||||
|
this.form = {
|
||||||
|
id: null,
|
||||||
|
dictName: '',
|
||||||
|
dictType: ''
|
||||||
|
}
|
||||||
|
this.$refs.form?.resetFields()
|
||||||
|
},
|
||||||
|
handleSave() {
|
||||||
|
this.$refs.form.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.$emit('save', this.form)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dialog-footer {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -38,7 +38,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from '@/apis/api';
|
import Api from '@/apis/api';
|
||||||
import { FIRMWARE_TYPES } from '@/utils';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FirmwareDialog',
|
name: 'FirmwareDialog',
|
||||||
@@ -54,11 +53,14 @@ export default {
|
|||||||
form: {
|
form: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
firmwareTypes: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
firmwareTypes: FIRMWARE_TYPES,
|
|
||||||
uploadProgress: 0,
|
uploadProgress: 0,
|
||||||
uploadStatus: '',
|
uploadStatus: '',
|
||||||
isUploading: false,
|
isUploading: false,
|
||||||
@@ -85,7 +87,11 @@ export default {
|
|||||||
return !!this.form.id
|
return !!this.form.id
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
// 移除 getDictDataByType 调用
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
// 移除 getFirmwareTypes 方法
|
||||||
handleClose() {
|
handleClose() {
|
||||||
this.$refs.form.clearValidate();
|
this.$refs.form.clearValidate();
|
||||||
this.$emit('cancel');
|
this.$emit('cancel');
|
||||||
|
|||||||
@@ -9,9 +9,11 @@
|
|||||||
|
|
||||||
<!-- 中间导航菜单 -->
|
<!-- 中间导航菜单 -->
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<div class="equipment-management" :class="{ 'active-tab': $route.path === '/home' }" @click="goHome">
|
<div class="equipment-management"
|
||||||
|
:class="{ 'active-tab': $route.path === '/home' || $route.path === '/role-config' || $route.path === '/device-management' }"
|
||||||
|
@click="goHome">
|
||||||
<img loading="lazy" alt="" src="@/assets/header/robot.png"
|
<img loading="lazy" alt="" src="@/assets/header/robot.png"
|
||||||
:style="{ filter: $route.path === '/home' ? 'brightness(0) invert(1)' : 'None' }" />
|
:style="{ filter: $route.path === '/home' || $route.path === '/role-config' || $route.path === '/device-management' ? 'brightness(0) invert(1)' : 'None' }" />
|
||||||
智能体管理
|
智能体管理
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isSuperAdmin" class="equipment-management" :class="{ 'active-tab': $route.path === '/model-config' }"
|
<div v-if="isSuperAdmin" class="equipment-management" :class="{ 'active-tab': $route.path === '/model-config' }"
|
||||||
@@ -26,18 +28,29 @@
|
|||||||
:style="{ filter: $route.path === '/user-management' ? 'brightness(0) invert(1)' : 'None' }" />
|
:style="{ filter: $route.path === '/user-management' ? 'brightness(0) invert(1)' : 'None' }" />
|
||||||
用户管理
|
用户管理
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isSuperAdmin" class="equipment-management"
|
|
||||||
:class="{ 'active-tab': $route.path === '/params-management' }" @click="goParamManagement">
|
|
||||||
<img loading="lazy" alt="" src="@/assets/header/param_management.png"
|
|
||||||
:style="{ filter: $route.path === '/params-management' ? 'brightness(0) invert(1)' : 'None' }" />
|
|
||||||
参数管理
|
|
||||||
</div>
|
|
||||||
<div v-if="isSuperAdmin" class="equipment-management"
|
<div v-if="isSuperAdmin" class="equipment-management"
|
||||||
:class="{ 'active-tab': $route.path === '/ota-management' }" @click="goOtaManagement">
|
:class="{ 'active-tab': $route.path === '/ota-management' }" @click="goOtaManagement">
|
||||||
<img loading="lazy" alt="" src="@/assets/header/firmware_update.png"
|
<img loading="lazy" alt="" src="@/assets/header/firmware_update.png"
|
||||||
:style="{ filter: $route.path === '/ota-management' ? 'brightness(0) invert(1)' : 'None' }" />
|
:style="{ filter: $route.path === '/ota-management' ? 'brightness(0) invert(1)' : 'None' }" />
|
||||||
OTA管理
|
OTA管理
|
||||||
</div>
|
</div>
|
||||||
|
<el-dropdown v-if="isSuperAdmin" trigger="click" class="equipment-management more-dropdown"
|
||||||
|
:class="{ 'active-tab': $route.path === '/dict-management' || $route.path === '/params-management' }">
|
||||||
|
<span class="el-dropdown-link">
|
||||||
|
<img loading="lazy" alt="" src="@/assets/header/param_management.png"
|
||||||
|
:style="{ filter: $route.path === '/dict-management' || $route.path === '/params-management' ? 'brightness(0) invert(1)' : 'None' }" />
|
||||||
|
参数字典
|
||||||
|
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
|
</span>
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
<el-dropdown-item @click.native="goParamManagement">
|
||||||
|
参数管理
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click.native="goDictManagement">
|
||||||
|
字典管理
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 右侧元素 -->
|
<!-- 右侧元素 -->
|
||||||
@@ -114,6 +127,9 @@ export default {
|
|||||||
goOtaManagement() {
|
goOtaManagement() {
|
||||||
this.$router.push('/ota-management')
|
this.$router.push('/ota-management')
|
||||||
},
|
},
|
||||||
|
goDictManagement() {
|
||||||
|
this.$router.push('/dict-management')
|
||||||
|
},
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
fetchUserInfo() {
|
fetchUserInfo() {
|
||||||
userApi.getUserInfo(({ data }) => {
|
userApi.getUserInfo(({ data }) => {
|
||||||
@@ -343,4 +359,32 @@ export default {
|
|||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.equipment-management.more-dropdown {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.equipment-management.more-dropdown .el-dropdown-menu {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
min-width: 120px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dropdown-menu__item {
|
||||||
|
min-width: 60px;
|
||||||
|
padding: 8px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.equipment-management.more-dropdown .el-dropdown-menu {
|
||||||
|
position: fixed;
|
||||||
|
right: 10px;
|
||||||
|
top: 60px;
|
||||||
|
z-index: 2000;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -84,8 +84,14 @@ const routes = [
|
|||||||
title: 'OTA管理'
|
title: 'OTA管理'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/dict-management',
|
||||||
|
name: 'DictManagement',
|
||||||
|
component: function () {
|
||||||
|
return import('../views/DictManagement.vue')
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const router = new VueRouter({
|
const router = new VueRouter({
|
||||||
base: process.env.VUE_APP_PUBLIC_PATH || '/',
|
base: process.env.VUE_APP_PUBLIC_PATH || '/',
|
||||||
routes
|
routes
|
||||||
|
|||||||
@@ -136,63 +136,3 @@ export function getUUID() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 固件类型选项
|
|
||||||
*/
|
|
||||||
export const FIRMWARE_TYPES = [
|
|
||||||
{ "key": "bread-compact-wifi", "name": "面包板新版接线(WiFi)" },
|
|
||||||
{ "key": "bread-compact-wifi-lcd", "name": "面包板新版接线(WiFi)+ LCD" },
|
|
||||||
{ "key": "bread-compact-ml307", "name": "面包板新版接线(ML307 AT)" },
|
|
||||||
{ "key": "bread-compact-esp32", "name": "面包板(WiFi) ESP32 DevKit" },
|
|
||||||
{ "key": "bread-compact-esp32-lcd", "name": "面包板(WiFi+ LCD) ESP32 DevKit" },
|
|
||||||
{ "key": "df-k10", "name": "DFRobot 行空板 k10" },
|
|
||||||
{ "key": "esp32-cgc", "name": "ESP32 CGC" },
|
|
||||||
{ "key": "esp-box-3", "name": "ESP BOX 3" },
|
|
||||||
{ "key": "esp-box", "name": "ESP BOX" },
|
|
||||||
{ "key": "esp-box-lite", "name": "ESP BOX Lite" },
|
|
||||||
{ "key": "kevin-box-1", "name": "Kevin Box 1" },
|
|
||||||
{ "key": "kevin-box-2", "name": "Kevin Box 2" },
|
|
||||||
{ "key": "kevin-c3", "name": "Kevin C3" },
|
|
||||||
{ "key": "kevin-sp-v3-dev", "name": "Kevin SP V3开发板" },
|
|
||||||
{ "key": "kevin-sp-v4-dev", "name": "Kevin SP V4开发板" },
|
|
||||||
{ "key": "kevin-yuying-313lcd", "name": "鱼鹰科技3.13LCD开发板" },
|
|
||||||
{ "key": "lichuang-dev", "name": "立创·实战派ESP32-S3开发板" },
|
|
||||||
{ "key": "lichuang-c3-dev", "name": "立创·实战派ESP32-C3开发板" },
|
|
||||||
{ "key": "magiclick-2p4", "name": "神奇按钮 Magiclick_2.4" },
|
|
||||||
{ "key": "magiclick-2p5", "name": "神奇按钮 Magiclick_2.5" },
|
|
||||||
{ "key": "magiclick-c3", "name": "神奇按钮 Magiclick_C3" },
|
|
||||||
{ "key": "magiclick-c3-v2", "name": "神奇按钮 Magiclick_C3_v2" },
|
|
||||||
{ "key": "m5stack-core-s3", "name": "M5Stack CoreS3" },
|
|
||||||
{ "key": "atoms3-echo-base", "name": "AtomS3 + Echo Base" },
|
|
||||||
{ "key": "atoms3r-echo-base", "name": "AtomS3R + Echo Base" },
|
|
||||||
{ "key": "atoms3r-cam-m12-echo-base", "name": "AtomS3R CAM/M12 + Echo Base" },
|
|
||||||
{ "key": "atommatrix-echo-base", "name": "AtomMatrix + Echo Base" },
|
|
||||||
{ "key": "xmini-c3", "name": "虾哥 Mini C3" },
|
|
||||||
{ "key": "esp32s3-korvo2-v3", "name": "ESP32S3_KORVO2_V3开发板" },
|
|
||||||
{ "key": "esp-sparkbot", "name": "ESP-SparkBot开发板" },
|
|
||||||
{ "key": "esp-spot-s3", "name": "ESP-Spot-S3" },
|
|
||||||
{ "key": "esp32-s3-touch-amoled-1.8", "name": "Waveshare ESP32-S3-Touch-AMOLED-1.8" },
|
|
||||||
{ "key": "esp32-s3-touch-lcd-1.85c", "name": "Waveshare ESP32-S3-Touch-LCD-1.85C" },
|
|
||||||
{ "key": "esp32-s3-touch-lcd-1.85", "name": "Waveshare ESP32-S3-Touch-LCD-1.85" },
|
|
||||||
{ "key": "esp32-s3-touch-lcd-1.46", "name": "Waveshare ESP32-S3-Touch-LCD-1.46" },
|
|
||||||
{ "key": "esp32-s3-touch-lcd-3.5", "name": "Waveshare ESP32-S3-Touch-LCD-3.5" },
|
|
||||||
{ "key": "tudouzi", "name": "土豆子" },
|
|
||||||
{ "key": "lilygo-t-circle-s3", "name": "LILYGO T-Circle-S3" },
|
|
||||||
{ "key": "lilygo-t-cameraplus-s3", "name": "LILYGO T-CameraPlus-S3" },
|
|
||||||
{ "key": "movecall-moji-esp32s3", "name": "Movecall Moji 小智AI衍生版" },
|
|
||||||
{ "key": "movecall-cuican-esp32s3", "name": "Movecall CuiCan 璀璨·AI吊坠" },
|
|
||||||
{ "key": "atk-dnesp32s3", "name": "正点原子DNESP32S3开发板" },
|
|
||||||
{ "key": "atk-dnesp32s3-box", "name": "正点原子DNESP32S3-BOX" },
|
|
||||||
{ "key": "du-chatx", "name": "嘟嘟开发板CHATX(wifi)" },
|
|
||||||
{ "key": "taiji-pi-s3", "name": "太极小派esp32s3" },
|
|
||||||
{ "key": "xingzhi-cube-0.85tft-wifi", "name": "无名科技星智0.85(WIFI)" },
|
|
||||||
{ "key": "xingzhi-cube-0.85tft-ml307", "name": "无名科技星智0.85(ML307)" },
|
|
||||||
{ "key": "xingzhi-cube-0.96oled-wifi", "name": "无名科技星智0.96(WIFI)" },
|
|
||||||
{ "key": "xingzhi-cube-0.96oled-ml307", "name": "无名科技星智0.96(ML307)" },
|
|
||||||
{ "key": "xingzhi-cube-1.54tft-wifi", "name": "无名科技星智1.54(WIFI)" },
|
|
||||||
{ "key": "xingzhi-cube-1.54tft-ml307", "name": "无名科技星智1.54(ML307)" },
|
|
||||||
{ "key": "sensecap-watcher", "name": "SenseCAP Watcher" },
|
|
||||||
{ "key": "doit-s3-aibox", "name": "四博智联AI陪伴盒子" },
|
|
||||||
{ "key": "mixgo-nova", "name": "元控·青春" }
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,6 @@
|
|||||||
import Api from '@/apis/api';
|
import Api from '@/apis/api';
|
||||||
import AddDeviceDialog from "@/components/AddDeviceDialog.vue";
|
import AddDeviceDialog from "@/components/AddDeviceDialog.vue";
|
||||||
import HeaderBar from "@/components/HeaderBar.vue";
|
import HeaderBar from "@/components/HeaderBar.vue";
|
||||||
import { FIRMWARE_TYPES } from "@/utils";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { HeaderBar, AddDeviceDialog },
|
components: { HeaderBar, AddDeviceDialog },
|
||||||
@@ -118,6 +117,7 @@ export default {
|
|||||||
deviceList: [],
|
deviceList: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
userApi: null,
|
userApi: null,
|
||||||
|
firmwareTypes: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -163,7 +163,19 @@ export default {
|
|||||||
this.fetchBindDevices(agentId);
|
this.fetchBindDevices(agentId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
this.getFirmwareTypes()
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async getFirmwareTypes() {
|
||||||
|
try {
|
||||||
|
const res = await Api.dict.getDictDataByType('FIRMWARE_TYPE')
|
||||||
|
this.firmwareTypes = res.data
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取固件类型失败:', error)
|
||||||
|
this.$message.error(error.message || '获取固件类型失败')
|
||||||
|
}
|
||||||
|
},
|
||||||
handlePageSizeChange(val) {
|
handlePageSizeChange(val) {
|
||||||
this.pageSize = val;
|
this.pageSize = val;
|
||||||
this.currentPage = 1;
|
this.currentPage = 1;
|
||||||
@@ -317,8 +329,8 @@ export default {
|
|||||||
return "";
|
return "";
|
||||||
},
|
},
|
||||||
getFirmwareTypeName(type) {
|
getFirmwareTypeName(type) {
|
||||||
const firmwareType = FIRMWARE_TYPES.find(item => item.key === type);
|
const firmwareType = this.firmwareTypes.find(item => item.key === type)
|
||||||
return firmwareType ? firmwareType.name : type;
|
return firmwareType ? firmwareType.name : type
|
||||||
},
|
},
|
||||||
handleOtaSwitchChange(row) {
|
handleOtaSwitchChange(row) {
|
||||||
Api.device.enableOtaUpgrade(row.device_id, row.otaSwitch ? 1 : 0, ({ data }) => {
|
Api.device.enableOtaUpgrade(row.device_id, row.otaSwitch ? 1 : 0, ({ data }) => {
|
||||||
|
|||||||
@@ -0,0 +1,835 @@
|
|||||||
|
<template>
|
||||||
|
<div class="welcome">
|
||||||
|
<HeaderBar />
|
||||||
|
|
||||||
|
<div class="operation-bar">
|
||||||
|
<h2 class="page-title">字典管理</h2>
|
||||||
|
<div class="action-group">
|
||||||
|
<div class="search-group">
|
||||||
|
<el-input placeholder="请输入字典值标签查询" v-model="search" class="search-input" clearable
|
||||||
|
@keyup.enter.native="handleSearch" style="width: 240px" />
|
||||||
|
<el-button class="btn-search" @click="handleSearch">
|
||||||
|
搜索
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 主体内容 -->
|
||||||
|
<div class="main-wrapper">
|
||||||
|
<div class="content-panel">
|
||||||
|
<!-- 左侧字典类型列表 -->
|
||||||
|
<div class="dict-type-panel">
|
||||||
|
<div class="dict-type-header">
|
||||||
|
<el-button type="success" size="mini" @click="showAddDictTypeDialog">新增字典类型</el-button>
|
||||||
|
<el-button type="danger" size="mini" @click="batchDeleteDictType"
|
||||||
|
:disabled="selectedDictTypes.length === 0">
|
||||||
|
批量删除字典类型
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table ref="dictTypeTable" :data="dictTypeList" style="width: 100%" v-loading="dictTypeLoading"
|
||||||
|
element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading"
|
||||||
|
element-loading-background="rgba(255, 255, 255, 0.7)" @row-click="handleDictTypeRowClick"
|
||||||
|
@selection-change="handleDictTypeSelectionChange" :row-class-name="tableRowClassName"
|
||||||
|
class="dict-type-table">
|
||||||
|
<el-table-column type="selection" width="55" align="center"></el-table-column>
|
||||||
|
<el-table-column label="字典类型名称" prop="dictName" align="center"></el-table-column>
|
||||||
|
<el-table-column label="操作" width="100" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" size="mini" @click.stop="editDictType(scope.row)">编辑</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧字典数据列表 -->
|
||||||
|
<div class="content-area">
|
||||||
|
<el-card class="dict-data-card" shadow="never">
|
||||||
|
<el-table ref="dictDataTable" :data="dictDataList" style="width: 100%"
|
||||||
|
v-loading="dictDataLoading" element-loading-text="拼命加载中"
|
||||||
|
element-loading-spinner="el-icon-loading"
|
||||||
|
element-loading-background="rgba(255, 255, 255, 0.7)"
|
||||||
|
@selection-change="handleDictDataSelectionChange" class="data-table"
|
||||||
|
header-row-class-name="table-header">
|
||||||
|
<el-table-column type="selection" width="55" align="center"></el-table-column>
|
||||||
|
<el-table-column label="字典标签" prop="dictLabel" align="center"></el-table-column>
|
||||||
|
<el-table-column label="字典值" prop="dictValue" align="center"></el-table-column>
|
||||||
|
<el-table-column label="排序" prop="sort" align="center"></el-table-column>
|
||||||
|
<el-table-column label="操作" align="center" width="180px">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" size="mini" @click="editDictData(scope.row)"
|
||||||
|
class="edit-btn">
|
||||||
|
修改
|
||||||
|
</el-button>
|
||||||
|
<el-button type="text" size="mini" @click="deleteDictData(scope.row)"
|
||||||
|
class="delete-btn">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="table-footer">
|
||||||
|
<div class="batch-actions">
|
||||||
|
<el-button size="mini" type="primary" @click="selectAllDictData">
|
||||||
|
{{ isAllDictDataSelected ? '取消全选' : '全选' }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="success" size="mini" @click="showAddDictDataDialog" class="add-btn">
|
||||||
|
新增字典数据
|
||||||
|
</el-button>
|
||||||
|
<el-button size="mini" type="danger" icon="el-icon-delete" @click="batchDeleteDictData">
|
||||||
|
批量删除字典数据
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="custom-pagination">
|
||||||
|
<el-select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select">
|
||||||
|
<el-option v-for="item in pageSizeOptions" :key="item" :label="`${item}条/页`"
|
||||||
|
:value="item">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
|
||||||
|
<button class="pagination-btn" :disabled="currentPage === 1" @click="goFirst">
|
||||||
|
首页
|
||||||
|
</button>
|
||||||
|
<button class="pagination-btn" :disabled="currentPage === 1" @click="goPrev">
|
||||||
|
上一页
|
||||||
|
</button>
|
||||||
|
<button v-for="page in visiblePages" :key="page" class="pagination-btn"
|
||||||
|
:class="{ active: page === currentPage }" @click="goToPage(page)">
|
||||||
|
{{ page }}
|
||||||
|
</button>
|
||||||
|
<button class="pagination-btn" :disabled="currentPage === pageCount" @click="goNext">
|
||||||
|
下一页
|
||||||
|
</button>
|
||||||
|
<span class="total-text">共{{ total }}条记录</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 使用字典类型编辑弹框组件 -->
|
||||||
|
<DictTypeDialog :visible.sync="dictTypeDialogVisible" :title="dictTypeDialogTitle" :dictTypeData="dictTypeForm"
|
||||||
|
@save="saveDictType" />
|
||||||
|
|
||||||
|
<!-- 使用字典数据编辑弹框组件 -->
|
||||||
|
<DictDataDialog :visible.sync="dictDataDialogVisible" :title="dictDataDialogTitle" :dictData="dictDataForm"
|
||||||
|
:dictTypeId="selectedDictType?.id" @save="saveDictData" />
|
||||||
|
<el-footer style="flex-shrink:unset;">
|
||||||
|
<version-footer />
|
||||||
|
</el-footer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import dictApi from '@/apis/module/dict'
|
||||||
|
import DictDataDialog from '@/components/DictDataDialog.vue'
|
||||||
|
import DictTypeDialog from '@/components/DictTypeDialog.vue'
|
||||||
|
import HeaderBar from '@/components/HeaderBar.vue'
|
||||||
|
import VersionFooter from '@/components/VersionFooter.vue'
|
||||||
|
export default {
|
||||||
|
name: 'DictManagement',
|
||||||
|
components: {
|
||||||
|
HeaderBar,
|
||||||
|
DictTypeDialog,
|
||||||
|
DictDataDialog,
|
||||||
|
VersionFooter
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 字典类型相关
|
||||||
|
dictTypeList: [],
|
||||||
|
dictTypeLoading: false,
|
||||||
|
selectedDictType: null,
|
||||||
|
selectedDictTypes: [], // 恢复多选数组
|
||||||
|
dictTypeDialogVisible: false,
|
||||||
|
dictTypeDialogTitle: '新增字典类型',
|
||||||
|
dictTypeForm: {
|
||||||
|
id: null,
|
||||||
|
dictName: '',
|
||||||
|
dictType: ''
|
||||||
|
},
|
||||||
|
|
||||||
|
// 字典数据相关
|
||||||
|
dictDataList: [],
|
||||||
|
dictDataLoading: false,
|
||||||
|
selectedDictData: [],
|
||||||
|
isAllDictDataSelected: false,
|
||||||
|
dictDataDialogVisible: false,
|
||||||
|
dictDataDialogTitle: '新增字典数据',
|
||||||
|
dictDataForm: {
|
||||||
|
id: null,
|
||||||
|
dictTypeId: null,
|
||||||
|
dictLabel: '',
|
||||||
|
dictValue: '',
|
||||||
|
sort: 0
|
||||||
|
},
|
||||||
|
search: '',
|
||||||
|
// 添加分页相关数据
|
||||||
|
pageSizeOptions: [10, 20, 50, 100],
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.loadDictTypeList()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 字典类型相关方法
|
||||||
|
loadDictTypeList() {
|
||||||
|
this.dictTypeLoading = true
|
||||||
|
dictApi.getDictTypeList({
|
||||||
|
page: 1,
|
||||||
|
limit: 100,
|
||||||
|
dictName: this.search
|
||||||
|
}, ({ data }) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
this.dictTypeList = data.data.list
|
||||||
|
if (this.dictTypeList.length > 0) {
|
||||||
|
this.selectedDictType = this.dictTypeList[0]
|
||||||
|
this.loadDictDataList(this.dictTypeList[0].id)
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.dictTypeTable.setCurrentRow(this.dictTypeList[0])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.dictTypeLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleDictTypeRowClick(row) {
|
||||||
|
this.selectedDictType = row
|
||||||
|
this.loadDictDataList(row.id)
|
||||||
|
this.$refs.dictTypeTable.setCurrentRow(row)
|
||||||
|
},
|
||||||
|
handleDictTypeSelectionChange(val) {
|
||||||
|
this.selectedDictTypes = val
|
||||||
|
},
|
||||||
|
tableRowClassName({ row }) {
|
||||||
|
return row === this.selectedDictType ? 'current-row' : ''
|
||||||
|
},
|
||||||
|
showAddDictTypeDialog() {
|
||||||
|
this.dictTypeDialogTitle = '新增字典类型'
|
||||||
|
this.dictTypeForm = {
|
||||||
|
id: null,
|
||||||
|
dictName: '',
|
||||||
|
dictType: ''
|
||||||
|
}
|
||||||
|
this.dictTypeDialogVisible = true
|
||||||
|
},
|
||||||
|
editDictType(row) {
|
||||||
|
this.dictTypeDialogTitle = '编辑字典类型'
|
||||||
|
this.dictTypeForm = { ...row }
|
||||||
|
this.dictTypeDialogVisible = true
|
||||||
|
},
|
||||||
|
saveDictType(formData) {
|
||||||
|
const api = formData.id ? dictApi.updateDictType : dictApi.addDictType
|
||||||
|
api(formData, ({ data }) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
this.$message.success('保存成功')
|
||||||
|
this.dictTypeDialogVisible = false
|
||||||
|
this.loadDictTypeList()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
batchDeleteDictType() {
|
||||||
|
if (this.selectedDictTypes.length === 0) {
|
||||||
|
this.$message.warning('请选择要删除的字典类型')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$confirm('确定要删除选中的字典类型吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
const ids = this.selectedDictTypes.map(item => item.id)
|
||||||
|
dictApi.deleteDictType(ids, ({ data }) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
this.$message.success('删除成功')
|
||||||
|
this.loadDictTypeList()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 字典数据相关方法
|
||||||
|
loadDictDataList(dictTypeId) {
|
||||||
|
if (!dictTypeId) return
|
||||||
|
this.dictDataLoading = true
|
||||||
|
dictApi.getDictDataList({
|
||||||
|
dictTypeId,
|
||||||
|
page: this.currentPage,
|
||||||
|
limit: this.pageSize,
|
||||||
|
dictLabel: this.search,
|
||||||
|
dictValue: ''
|
||||||
|
}, ({ data }) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
this.dictDataList = data.data.list
|
||||||
|
this.total = data.data.total
|
||||||
|
} else {
|
||||||
|
this.$message.error(data.msg || '获取字典数据失败')
|
||||||
|
}
|
||||||
|
this.dictDataLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleDictDataSelectionChange(val) {
|
||||||
|
this.selectedDictData = val
|
||||||
|
this.isAllDictDataSelected = val.length === this.dictDataList.length
|
||||||
|
},
|
||||||
|
selectAllDictData() {
|
||||||
|
if (this.isAllDictDataSelected) {
|
||||||
|
this.$refs.dictDataTable.clearSelection()
|
||||||
|
} else {
|
||||||
|
this.$refs.dictDataTable.toggleAllSelection()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showAddDictDataDialog() {
|
||||||
|
if (!this.selectedDictType) {
|
||||||
|
this.$message.warning('请先选择字典类型')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.dictDataDialogTitle = '新增字典数据'
|
||||||
|
this.dictDataForm = {
|
||||||
|
id: null,
|
||||||
|
dictTypeId: this.selectedDictType.id,
|
||||||
|
dictLabel: '',
|
||||||
|
dictValue: '',
|
||||||
|
sort: 0
|
||||||
|
}
|
||||||
|
this.dictDataDialogVisible = true
|
||||||
|
},
|
||||||
|
editDictData(row) {
|
||||||
|
this.dictDataDialogTitle = '编辑字典数据'
|
||||||
|
this.dictDataForm = { ...row }
|
||||||
|
this.dictDataDialogVisible = true
|
||||||
|
},
|
||||||
|
saveDictData(formData) {
|
||||||
|
const api = formData.id ? dictApi.updateDictData : dictApi.addDictData
|
||||||
|
api(formData, ({ data }) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
this.$message.success('保存成功')
|
||||||
|
this.dictDataDialogVisible = false
|
||||||
|
this.loadDictDataList(formData.dictTypeId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
deleteDictData(row) {
|
||||||
|
this.$confirm('确定要删除该字典数据吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
dictApi.deleteDictData([row.id], ({ data }) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
this.$message.success('删除成功')
|
||||||
|
this.loadDictDataList(row.dictTypeId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
batchDeleteDictData() {
|
||||||
|
if (this.selectedDictData.length === 0) {
|
||||||
|
this.$message.warning('请选择要删除的字典数据')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$confirm('确定要删除选中的字典数据吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
const ids = this.selectedDictData.map(item => item.id)
|
||||||
|
dictApi.deleteDictData(ids, ({ data }) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
this.$message.success('删除成功')
|
||||||
|
this.loadDictDataList(this.selectedDictType.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleSearch() {
|
||||||
|
if (!this.selectedDictType) {
|
||||||
|
this.$message.warning('请先选择字典类型')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.currentPage = 1
|
||||||
|
this.loadDictDataList(this.selectedDictType.id)
|
||||||
|
},
|
||||||
|
// 添加分页相关方法
|
||||||
|
handlePageSizeChange(val) {
|
||||||
|
this.pageSize = val;
|
||||||
|
this.currentPage = 1;
|
||||||
|
this.loadDictDataList(this.selectedDictType?.id);
|
||||||
|
},
|
||||||
|
goFirst() {
|
||||||
|
this.currentPage = 1;
|
||||||
|
this.loadDictDataList(this.selectedDictType?.id);
|
||||||
|
},
|
||||||
|
goPrev() {
|
||||||
|
if (this.currentPage > 1) {
|
||||||
|
this.currentPage--;
|
||||||
|
this.loadDictDataList(this.selectedDictType?.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
goNext() {
|
||||||
|
if (this.currentPage < this.pageCount) {
|
||||||
|
this.currentPage++;
|
||||||
|
this.loadDictDataList(this.selectedDictType?.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
goToPage(page) {
|
||||||
|
this.currentPage = page;
|
||||||
|
this.loadDictDataList(this.selectedDictType?.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
pageCount() {
|
||||||
|
return Math.ceil(this.total / this.pageSize);
|
||||||
|
},
|
||||||
|
visiblePages() {
|
||||||
|
const pages = [];
|
||||||
|
const maxVisible = 3;
|
||||||
|
let start = Math.max(1, this.currentPage - 1);
|
||||||
|
let end = Math.min(this.pageCount, start + maxVisible - 1);
|
||||||
|
|
||||||
|
if (end - start + 1 < maxVisible) {
|
||||||
|
start = Math.max(1, end - maxVisible + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = start; i <= end; i++) {
|
||||||
|
pages.push(i);
|
||||||
|
}
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.welcome {
|
||||||
|
min-width: 900px;
|
||||||
|
min-height: 506px;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
flex-direction: column;
|
||||||
|
background-size: cover;
|
||||||
|
background: linear-gradient(to bottom right, #dce8ff, #e4eeff, #e6cbfd) center;
|
||||||
|
-webkit-background-size: cover;
|
||||||
|
-o-background-size: cover;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-wrapper {
|
||||||
|
margin: 5px 22px;
|
||||||
|
border-radius: 15px;
|
||||||
|
min-height: calc(100vh - 24vh);
|
||||||
|
height: auto;
|
||||||
|
max-height: 80vh;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
position: relative;
|
||||||
|
background: rgba(237, 242, 255, 0.5);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation-bar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 24px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
width: 240px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-search {
|
||||||
|
background: linear-gradient(135deg, #6b8cff, #a966ff);
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-search:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.search-input .el-input__inner) {
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #DCDFE6;
|
||||||
|
background-color: white;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.search-input .el-input__inner:focus) {
|
||||||
|
border-color: #6b8cff;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-panel {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 15px;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dict-type-panel {
|
||||||
|
width: 300px;
|
||||||
|
background: white;
|
||||||
|
border-right: 1px solid #ebeef5;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dict-type-header {
|
||||||
|
padding: 16px;
|
||||||
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dict-type-table {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-area {
|
||||||
|
flex: 1;
|
||||||
|
padding: 24px;
|
||||||
|
height: 100%;
|
||||||
|
min-width: 600px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: white;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dict-data-card {
|
||||||
|
background: white;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table {
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: transparent !important;
|
||||||
|
--table-max-height: calc(100vh - 40vh);
|
||||||
|
max-height: var(--table-max-height);
|
||||||
|
|
||||||
|
:deep(.el-table__body-wrapper) {
|
||||||
|
max-height: calc(var(--table-max-height) - 40px);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table__body) {
|
||||||
|
tr:last-child td {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table) {
|
||||||
|
&::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 0;
|
||||||
|
width: 100%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
min-height: 60px;
|
||||||
|
background: white;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
padding-left: 26px;
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
min-width: 72px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 7px 12px 7px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
line-height: 1;
|
||||||
|
font-weight: 500;
|
||||||
|
border: none;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button--primary {
|
||||||
|
background: #5f70f3;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button--success {
|
||||||
|
background: #5bc98c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button--danger {
|
||||||
|
background: #fd5b63;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-pagination {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.el-select {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn:first-child,
|
||||||
|
.pagination-btn:nth-child(2),
|
||||||
|
.pagination-btn:nth-child(3),
|
||||||
|
.pagination-btn:nth-last-child(2) {
|
||||||
|
min-width: 60px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
background: #dee7ff;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #d7dce6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn:not(:first-child):not(:nth-child(2)):not(:nth-child(3)):not(:nth-last-child(2)) {
|
||||||
|
min-width: 28px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background: transparent;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(245, 247, 250, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn.active {
|
||||||
|
background: #5f70f3 !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
border-color: #5f70f3 !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #6d7cf5 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.total-text {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-size-select {
|
||||||
|
width: 100px;
|
||||||
|
margin-right: 10px;
|
||||||
|
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
background: #dee7ff;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__suffix) {
|
||||||
|
right: 6px;
|
||||||
|
width: 15px;
|
||||||
|
height: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
top: 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__suffix-inner) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-icon-arrow-up:before) {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
border-left: 6px solid transparent;
|
||||||
|
border-right: 6px solid transparent;
|
||||||
|
border-top: 9px solid #606266;
|
||||||
|
position: relative;
|
||||||
|
transform: rotate(0deg);
|
||||||
|
transition: transform 0.3s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-btn,
|
||||||
|
.delete-btn {
|
||||||
|
margin: 0 8px;
|
||||||
|
color: #7079aa !important;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 7px 12px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 1;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #5a64b5 !important;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.dict-type-header .el-button) {
|
||||||
|
min-width: 72px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 7px 12px 7px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
line-height: 1;
|
||||||
|
font-weight: 500;
|
||||||
|
border: none;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.el-button--success {
|
||||||
|
background: #5bc98c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.el-button--danger {
|
||||||
|
background: #fd5b63;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table .cell) {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-loading-mask) {
|
||||||
|
background-color: rgba(255, 255, 255, 0.6) !important;
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-loading-spinner .circular) {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-loading-spinner .path) {
|
||||||
|
stroke: #6b8cff;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-loading-text) {
|
||||||
|
color: #6b8cff !important;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.dict-type-table .el-table__row) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.dict-type-table .el-table__row.current-row) {
|
||||||
|
background-color: #5778ff !important;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.dict-type-table .el-table__row.current-row .el-button--text) {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.dict-type-table .el-table__row:hover) {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.dict-type-table .el-table__row.current-row:hover) {
|
||||||
|
background-color: #5778ff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.dict-type-table .el-table__row td) {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table thead) {
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-card__body) {
|
||||||
|
padding: 15px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -96,8 +96,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑固件对话框 -->
|
<!-- 新增/编辑固件对话框 -->
|
||||||
<firmware-dialog :title="dialogTitle" :visible.sync="dialogVisible" :form="firmwareForm" @submit="handleSubmit"
|
<firmware-dialog :title="dialogTitle" :visible.sync="dialogVisible" :form="firmwareForm"
|
||||||
@cancel="dialogVisible = false" />
|
:firmware-types="firmwareTypes" @submit="handleSubmit" @cancel="dialogVisible = false" />
|
||||||
<el-footer>
|
<el-footer>
|
||||||
<version-footer />
|
<version-footer />
|
||||||
</el-footer>
|
</el-footer>
|
||||||
@@ -109,7 +109,6 @@ import Api from "@/apis/api";
|
|||||||
import FirmwareDialog from "@/components/FirmwareDialog.vue";
|
import FirmwareDialog from "@/components/FirmwareDialog.vue";
|
||||||
import HeaderBar from "@/components/HeaderBar.vue";
|
import HeaderBar from "@/components/HeaderBar.vue";
|
||||||
import VersionFooter from "@/components/VersionFooter.vue";
|
import VersionFooter from "@/components/VersionFooter.vue";
|
||||||
import { FIRMWARE_TYPES } from "@/utils";
|
|
||||||
import { formatDate, formatFileSize } from "@/utils/format";
|
import { formatDate, formatFileSize } from "@/utils/format";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -136,10 +135,12 @@ export default {
|
|||||||
remark: "",
|
remark: "",
|
||||||
firmwarePath: ""
|
firmwarePath: ""
|
||||||
},
|
},
|
||||||
|
firmwareTypes: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.fetchFirmwareList();
|
this.fetchFirmwareList();
|
||||||
|
this.getFirmwareTypes();
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
@@ -387,9 +388,18 @@ export default {
|
|||||||
},
|
},
|
||||||
formatDate,
|
formatDate,
|
||||||
formatFileSize,
|
formatFileSize,
|
||||||
|
async getFirmwareTypes() {
|
||||||
|
try {
|
||||||
|
const res = await Api.dict.getDictDataByType('FIRMWARE_TYPE')
|
||||||
|
this.firmwareTypes = res.data
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取固件类型失败:', error)
|
||||||
|
this.$message.error(error.message || '获取固件类型失败')
|
||||||
|
}
|
||||||
|
},
|
||||||
getFirmwareTypeName(type) {
|
getFirmwareTypeName(type) {
|
||||||
const firmwareType = FIRMWARE_TYPES.find(item => item.key === type);
|
const firmwareType = this.firmwareTypes.find(item => item.key === type)
|
||||||
return firmwareType ? firmwareType.name : type;
|
return firmwareType ? firmwareType.name : type
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,6 +26,11 @@
|
|||||||
<el-table-column label="用户Id" prop="userid" align="center"></el-table-column>
|
<el-table-column label="用户Id" prop="userid" align="center"></el-table-column>
|
||||||
<el-table-column label="手机号码" prop="mobile" align="center"></el-table-column>
|
<el-table-column label="手机号码" prop="mobile" align="center"></el-table-column>
|
||||||
<el-table-column label="设备数量" prop="deviceCount" align="center"></el-table-column>
|
<el-table-column label="设备数量" prop="deviceCount" align="center"></el-table-column>
|
||||||
|
<el-table-column label="注册时间" prop="createDate" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
{{ formatDate(scope.row.createDate) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="状态" prop="status" align="center">
|
<el-table-column label="状态" prop="status" align="center">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tag v-if="scope.row.status === 1" type="success">正常</el-tag>
|
<el-tag v-if="scope.row.status === 1" type="success">正常</el-tag>
|
||||||
@@ -336,7 +341,12 @@ export default {
|
|||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
// 用户取消操作
|
// 用户取消操作
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
formatDate(dateString) {
|
||||||
|
if (!dateString) return '';
|
||||||
|
const date = new Date(dateString);
|
||||||
|
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="welcome">
|
<div class="welcome">
|
||||||
<HeaderBar/>
|
<HeaderBar />
|
||||||
|
|
||||||
<div class="operation-bar">
|
<div class="operation-bar">
|
||||||
<h2 class="page-title">角色配置</h2>
|
<h2 class="page-title">角色配置</h2>
|
||||||
@@ -26,93 +26,49 @@
|
|||||||
<div class="form-grid">
|
<div class="form-grid">
|
||||||
<div class="form-column">
|
<div class="form-column">
|
||||||
<el-form-item label="助手昵称:">
|
<el-form-item label="助手昵称:">
|
||||||
<el-input v-model="form.agentName" class="form-input"/>
|
<el-input v-model="form.agentName" class="form-input" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="角色模版:">
|
<el-form-item label="角色模版:">
|
||||||
<div class="template-container">
|
<div class="template-container">
|
||||||
<div
|
<div v-for="(template, index) in templates" :key="`template-${index}`" class="template-item"
|
||||||
v-for="(template, index) in templates"
|
:class="{ 'template-loading': loadingTemplate }" @click="selectTemplate(template)">
|
||||||
:key="`template-${index}`"
|
|
||||||
class="template-item"
|
|
||||||
:class="{ 'template-loading': loadingTemplate }"
|
|
||||||
@click="selectTemplate(template)"
|
|
||||||
>
|
|
||||||
{{ template.agentName }}
|
{{ template.agentName }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="角色介绍:">
|
<el-form-item label="角色介绍:">
|
||||||
<el-input
|
<el-input type="textarea" rows="12" resize="none" placeholder="请输入内容" v-model="form.systemPrompt"
|
||||||
type="textarea"
|
maxlength="2000" show-word-limit class="form-textarea" />
|
||||||
rows="5"
|
|
||||||
resize="none"
|
|
||||||
placeholder="请输入内容"
|
|
||||||
v-model="form.systemPrompt"
|
|
||||||
maxlength="2000"
|
|
||||||
show-word-limit
|
|
||||||
class="form-textarea"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="语言编码:">
|
<el-form-item label="语言编码:" style="display: none;">
|
||||||
<el-input
|
<el-input v-model="form.langCode" placeholder="请输入语言编码,如:zh_CN" maxlength="10" show-word-limit
|
||||||
v-model="form.langCode"
|
class="form-input" />
|
||||||
placeholder="请输入语言编码,如:zh_CN"
|
|
||||||
maxlength="10"
|
|
||||||
show-word-limit
|
|
||||||
class="form-input"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="交互语种:">
|
<el-form-item label="交互语种:" style="display: none;">
|
||||||
<el-input
|
<el-input v-model="form.language" placeholder="请输入交互语种,如:中文" maxlength="10" show-word-limit
|
||||||
v-model="form.language"
|
class="form-input" />
|
||||||
placeholder="请输入交互语种,如:中文"
|
|
||||||
maxlength="10"
|
|
||||||
show-word-limit
|
|
||||||
class="form-input"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<div class="action-bar">
|
<div class="action-bar">
|
||||||
<el-button type="primary" class="save-btn" @click="saveConfig">保存配置</el-button>
|
<el-button type="primary" class="save-btn" @click="saveConfig">保存配置</el-button>
|
||||||
<el-button class="reset-btn" @click="resetConfig">重置</el-button>
|
<el-button class="reset-btn" @click="resetConfig">重置</el-button>
|
||||||
<div class="hint-text">
|
<div class="hint-text">
|
||||||
<img loading="lazy" src="@/assets/home/red-info.png" alt="">
|
<img loading="lazy" src="@/assets/home/red-info.png" alt="">
|
||||||
<span>保存配置后,需要重启设备,新的配置才会生效。</span>
|
<span>保存配置后,需要重启设备,新的配置才会生效。</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-column">
|
<div class="form-column">
|
||||||
<el-form-item
|
<el-form-item v-for="(model, index) in models" :key="`model-${index}`" :label="model.label"
|
||||||
v-for="(model, index) in models"
|
class="model-item">
|
||||||
:key="`model-${index}`"
|
<el-select v-model="form.model[model.key]" filterable placeholder="请选择" class="form-select">
|
||||||
:label="model.label"
|
<el-option v-for="(item, optionIndex) in modelOptions[model.type]"
|
||||||
class="model-item"
|
:key="`option-${index}-${optionIndex}`" :label="item.label" :value="item.value" />
|
||||||
>
|
|
||||||
<el-select
|
|
||||||
v-model="form.model[model.key]"
|
|
||||||
filterable
|
|
||||||
placeholder="请选择"
|
|
||||||
class="form-select"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="(item, optionIndex) in modelOptions[model.type]"
|
|
||||||
:key="`option-${index}-${optionIndex}`"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="角色音色:">
|
<el-form-item label="角色音色:">
|
||||||
<el-select
|
<el-select v-model="form.ttsVoiceId" placeholder="请选择" class="form-select">
|
||||||
v-model="form.ttsVoiceId"
|
<el-option v-for="(item, index) in voiceOptions" :key="`voice-${index}`" :label="item.label"
|
||||||
placeholder="请选择"
|
:value="item.value" />
|
||||||
class="form-select"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="(item, index) in voiceOptions"
|
|
||||||
:key="`voice-${index}`"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
@@ -133,7 +89,7 @@ import HeaderBar from "@/components/HeaderBar.vue";
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'RoleConfigPage',
|
name: 'RoleConfigPage',
|
||||||
components: {HeaderBar},
|
components: { HeaderBar },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
form: {
|
form: {
|
||||||
@@ -154,12 +110,12 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
models: [
|
models: [
|
||||||
{label: '语音活动检测(VAD)', key: 'vadModelId', type: 'VAD'},
|
{ label: '语音活动检测(VAD)', key: 'vadModelId', type: 'VAD' },
|
||||||
{label: '语音识别(ASR)', key: 'asrModelId', type: 'ASR'},
|
{ label: '语音识别(ASR)', key: 'asrModelId', type: 'ASR' },
|
||||||
{label: '大语言模型(LLM)', key: 'llmModelId', type: 'LLM'},
|
{ label: '大语言模型(LLM)', key: 'llmModelId', type: 'LLM' },
|
||||||
{label: '意图识别(Intent)', key: 'intentModelId', type: 'Intent'},
|
{ label: '意图识别(Intent)', key: 'intentModelId', type: 'Intent' },
|
||||||
{label: '记忆(Memory)', key: 'memModelId', type: 'Memory'},
|
{ label: '记忆(Memory)', key: 'memModelId', type: 'Memory' },
|
||||||
{label: '语音合成(TTS)', key: 'ttsModelId', type: 'TTS'},
|
{ label: '语音合成(TTS)', key: 'ttsModelId', type: 'TTS' },
|
||||||
],
|
],
|
||||||
modelOptions: {},
|
modelOptions: {},
|
||||||
templates: [],
|
templates: [],
|
||||||
@@ -187,7 +143,7 @@ export default {
|
|||||||
language: this.form.language,
|
language: this.form.language,
|
||||||
sort: this.form.sort
|
sort: this.form.sort
|
||||||
};
|
};
|
||||||
Api.agent.updateAgentConfig(this.$route.query.agentId, configData, ({data}) => {
|
Api.agent.updateAgentConfig(this.$route.query.agentId, configData, ({ data }) => {
|
||||||
if (data.code === 0) {
|
if (data.code === 0) {
|
||||||
this.$message.success({
|
this.$message.success({
|
||||||
message: '配置保存成功',
|
message: '配置保存成功',
|
||||||
@@ -232,7 +188,7 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
fetchTemplates() {
|
fetchTemplates() {
|
||||||
Api.agent.getAgentTemplate(({data}) => {
|
Api.agent.getAgentTemplate(({ data }) => {
|
||||||
if (data.code === 0) {
|
if (data.code === 0) {
|
||||||
this.templates = data.data;
|
this.templates = data.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -277,7 +233,7 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
fetchAgentConfig(agentId) {
|
fetchAgentConfig(agentId) {
|
||||||
Api.agent.getDeviceConfig(agentId, ({data}) => {
|
Api.agent.getDeviceConfig(agentId, ({ data }) => {
|
||||||
if (data.code === 0) {
|
if (data.code === 0) {
|
||||||
this.form = {
|
this.form = {
|
||||||
...this.form,
|
...this.form,
|
||||||
@@ -298,7 +254,7 @@ export default {
|
|||||||
},
|
},
|
||||||
fetchModelOptions() {
|
fetchModelOptions() {
|
||||||
this.models.forEach(model => {
|
this.models.forEach(model => {
|
||||||
Api.model.getModelNames(model.type, '', ({data}) => {
|
Api.model.getModelNames(model.type, '', ({ data }) => {
|
||||||
if (data.code === 0) {
|
if (data.code === 0) {
|
||||||
this.$set(this.modelOptions, model.type, data.data.map(item => ({
|
this.$set(this.modelOptions, model.type, data.data.map(item => ({
|
||||||
value: item.id,
|
value: item.id,
|
||||||
@@ -315,7 +271,7 @@ export default {
|
|||||||
this.voiceOptions = [];
|
this.voiceOptions = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Api.model.getModelVoices(modelId, '', ({data}) => {
|
Api.model.getModelVoices(modelId, '', ({ data }) => {
|
||||||
if (data.code === 0 && data.data) {
|
if (data.code === 0 && data.data) {
|
||||||
this.voiceOptions = data.data.map(voice => ({
|
this.voiceOptions = data.data.map(voice => ({
|
||||||
value: voice.id,
|
value: voice.id,
|
||||||
@@ -567,7 +523,6 @@ export default {
|
|||||||
background: none;
|
background: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
bottom: -10%;
|
|
||||||
right: 3%;
|
right: 3%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -597,5 +552,4 @@ export default {
|
|||||||
color: #409EFF;
|
color: #409EFF;
|
||||||
border-color: #409EFF;
|
border-color: #409EFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user