mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-29 10:33:56 +08:00
Merge branch 'main' into update-style
This commit is contained in:
+6
-7
@@ -56,7 +56,7 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
|||||||
new QueryWrapper<ModelConfigEntity>()
|
new QueryWrapper<ModelConfigEntity>()
|
||||||
.eq("model_type", modelType)
|
.eq("model_type", modelType)
|
||||||
.eq("is_enabled", 1)
|
.eq("is_enabled", 1)
|
||||||
.like(StringUtils.isNotBlank(modelName), "model_name", "%" + modelName + "%")
|
.like(StringUtils.isNotBlank(modelName), "model_name", modelName)
|
||||||
.select("id", "model_name"));
|
.select("id", "model_name"));
|
||||||
return ConvertUtils.sourceToTarget(entities, ModelBasicInfoDTO.class);
|
return ConvertUtils.sourceToTarget(entities, ModelBasicInfoDTO.class);
|
||||||
}
|
}
|
||||||
@@ -67,14 +67,14 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
|||||||
new QueryWrapper<ModelConfigEntity>()
|
new QueryWrapper<ModelConfigEntity>()
|
||||||
.eq("model_type", "llm")
|
.eq("model_type", "llm")
|
||||||
.eq("is_enabled", 1)
|
.eq("is_enabled", 1)
|
||||||
.like(StringUtils.isNotBlank(modelName), "model_name", "%" + modelName + "%")
|
.like(StringUtils.isNotBlank(modelName), "model_name", modelName)
|
||||||
.select("id", "model_name", "config_json"));
|
.select("id", "model_name", "config_json"));
|
||||||
|
|
||||||
return entities.stream().map(item -> {
|
return entities.stream().map(item -> {
|
||||||
LlmModelBasicInfoDTO dto = new LlmModelBasicInfoDTO();
|
LlmModelBasicInfoDTO dto = new LlmModelBasicInfoDTO();
|
||||||
dto.setId(item.getId());
|
dto.setId(item.getId());
|
||||||
dto.setModelName(item.getModelName());
|
dto.setModelName(item.getModelName());
|
||||||
String type = item.getConfigJson().get("type").toString();
|
String type = item.getConfigJson().getOrDefault("type", "").toString();
|
||||||
dto.setType(type);
|
dto.setType(type);
|
||||||
return dto;
|
return dto;
|
||||||
}).toList();
|
}).toList();
|
||||||
@@ -91,14 +91,13 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
|||||||
Page<ModelConfigEntity> pageInfo = new Page<>(curPage, pageSize);
|
Page<ModelConfigEntity> pageInfo = new Page<>(curPage, pageSize);
|
||||||
|
|
||||||
// 添加排序规则:先按is_enabled降序,再按sort升序
|
// 添加排序规则:先按is_enabled降序,再按sort升序
|
||||||
pageInfo.addOrder(OrderItem.desc("is_enabled"));
|
pageInfo.addOrder(OrderItem.desc("is_enabled"), OrderItem.asc("sort"));
|
||||||
pageInfo.addOrder(OrderItem.asc("sort"));
|
|
||||||
|
|
||||||
IPage<ModelConfigEntity> modelConfigEntityIPage = modelConfigDao.selectPage(
|
IPage<ModelConfigEntity> modelConfigEntityIPage = modelConfigDao.selectPage(
|
||||||
pageInfo,
|
pageInfo,
|
||||||
new QueryWrapper<ModelConfigEntity>()
|
new QueryWrapper<ModelConfigEntity>()
|
||||||
.eq("model_type", modelType)
|
.eq("model_type", modelType)
|
||||||
.like(StringUtils.isNotBlank(modelName), "model_name", "%" + modelName + "%"));
|
.like(StringUtils.isNotBlank(modelName), "model_name", modelName));
|
||||||
|
|
||||||
return getPageData(modelConfigEntityIPage, ModelConfigDTO.class);
|
return getPageData(modelConfigEntityIPage, ModelConfigDTO.class);
|
||||||
}
|
}
|
||||||
@@ -489,7 +488,7 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
|||||||
List<ModelConfigEntity> intentConfigs = modelConfigDao.selectList(
|
List<ModelConfigEntity> intentConfigs = modelConfigDao.selectList(
|
||||||
new QueryWrapper<ModelConfigEntity>()
|
new QueryWrapper<ModelConfigEntity>()
|
||||||
.eq("model_type", "Intent")
|
.eq("model_type", "Intent")
|
||||||
.like("config_json", "%" + modelId + "%"));
|
.like("config_json", modelId));
|
||||||
if (!intentConfigs.isEmpty()) {
|
if (!intentConfigs.isEmpty()) {
|
||||||
throw new RenException(ErrorCode.LLM_REFERENCED_BY_INTENT);
|
throw new RenException(ErrorCode.LLM_REFERENCED_BY_INTENT);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-8
@@ -124,12 +124,6 @@ public class ModelProviderServiceImpl extends BaseServiceImpl<ModelProviderDao,
|
|||||||
return getPageData(modelProviderDao.selectPage(pageParam, wrapper), ModelProviderDTO.class);
|
return getPageData(modelProviderDao.selectPage(pageParam, wrapper), ModelProviderDTO.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
String jsonString = "\"[]\"";
|
|
||||||
JSONArray jsonArray = new JSONArray(jsonString);
|
|
||||||
System.out.println("字符串转 JSONArray: " + jsonArray.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ModelProviderDTO add(ModelProviderDTO modelProviderDTO) {
|
public ModelProviderDTO add(ModelProviderDTO modelProviderDTO) {
|
||||||
UserDetail user = SecurityUser.getUser();
|
UserDetail user = SecurityUser.getUser();
|
||||||
@@ -153,8 +147,7 @@ public class ModelProviderServiceImpl extends BaseServiceImpl<ModelProviderDao,
|
|||||||
UserDetail user = SecurityUser.getUser();
|
UserDetail user = SecurityUser.getUser();
|
||||||
modelProviderDTO.setUpdater(user.getId());
|
modelProviderDTO.setUpdater(user.getId());
|
||||||
modelProviderDTO.setUpdateDate(new Date());
|
modelProviderDTO.setUpdateDate(new Date());
|
||||||
if (modelProviderDao
|
if (modelProviderDao.updateById(ConvertUtils.sourceToTarget(modelProviderDTO, ModelProviderEntity.class)) == 0) {
|
||||||
.updateById(ConvertUtils.sourceToTarget(modelProviderDTO, ModelProviderEntity.class)) == 0) {
|
|
||||||
throw new RenException(ErrorCode.UPDATE_DATA_FAILED);
|
throw new RenException(ErrorCode.UPDATE_DATA_FAILED);
|
||||||
}
|
}
|
||||||
return ConvertUtils.sourceToTarget(modelProviderDTO, ModelProviderDTO.class);
|
return ConvertUtils.sourceToTarget(modelProviderDTO, ModelProviderDTO.class);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
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;
|
import xiaozhi.modules.sys.vo.SysDictDataItem;
|
||||||
@@ -23,4 +24,9 @@ public interface SysDictDataDao extends BaseDao<SysDictDataEntity> {
|
|||||||
* @return 字典类型编码
|
* @return 字典类型编码
|
||||||
*/
|
*/
|
||||||
String getTypeByTypeId(Long dictTypeId);
|
String getTypeByTypeId(Long dictTypeId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据字典数据ID集合获取字典类型编码集合
|
||||||
|
*/
|
||||||
|
List<String> getDictTypesByIdList(@Param("dictDataIdList") List<Long> dictDataIdList);
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-11
@@ -1,9 +1,6 @@
|
|||||||
package xiaozhi.modules.sys.service.impl;
|
package xiaozhi.modules.sys.service.impl;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@@ -23,6 +20,7 @@ import xiaozhi.common.redis.RedisKeys;
|
|||||||
import xiaozhi.common.redis.RedisUtils;
|
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.common.utils.ToolUtil;
|
||||||
import xiaozhi.modules.sys.dao.SysDictDataDao;
|
import xiaozhi.modules.sys.dao.SysDictDataDao;
|
||||||
import xiaozhi.modules.sys.dao.SysUserDao;
|
import xiaozhi.modules.sys.dao.SysUserDao;
|
||||||
import xiaozhi.modules.sys.dto.SysDictDataDTO;
|
import xiaozhi.modules.sys.dto.SysDictDataDTO;
|
||||||
@@ -104,13 +102,19 @@ public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, 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) {
|
List<Long> idList = Arrays.asList(ids);
|
||||||
SysDictDataEntity entity = baseDao.selectById(id);
|
if (ToolUtil.isNotEmpty(idList)) {
|
||||||
// 删除Redis缓存
|
//批量删除redis字典
|
||||||
String dictType = baseDao.getTypeByTypeId(entity.getDictTypeId());
|
List<String> redisKeyList = new ArrayList<>();
|
||||||
redisUtils.delete(RedisKeys.getDictDataByTypeKey(dictType));
|
//批量获取字典类型
|
||||||
// 删除
|
List<String> dictTypeList = Optional.ofNullable(baseDao.getDictTypesByIdList(idList)).orElseGet(ArrayList::new);
|
||||||
deleteById(id);
|
dictTypeList.forEach(dictType -> redisKeyList.add(RedisKeys.getDictDataByTypeKey(dictType)));
|
||||||
|
if (ToolUtil.isNotEmpty(redisKeyList)) {
|
||||||
|
//清除缓存
|
||||||
|
redisUtils.delete(redisKeyList);
|
||||||
|
}
|
||||||
|
//批量删除字典数据
|
||||||
|
deleteBatchIds(Arrays.asList(ids));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-9
@@ -1,10 +1,6 @@
|
|||||||
package xiaozhi.modules.timbre.service.impl;
|
package xiaozhi.modules.timbre.service.impl;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -128,10 +124,7 @@ public class TimbreServiceImpl extends BaseServiceImpl<TimbreDao, TimbreEntity>
|
|||||||
if (StringUtils.isNotBlank(voiceName)) {
|
if (StringUtils.isNotBlank(voiceName)) {
|
||||||
queryWrapper.like("name", voiceName);
|
queryWrapper.like("name", voiceName);
|
||||||
}
|
}
|
||||||
List<TimbreEntity> timbreEntities = timbreDao.selectList(queryWrapper);
|
List<TimbreEntity> timbreEntities = Optional.ofNullable(timbreDao.selectList(queryWrapper)).orElseGet(ArrayList::new);
|
||||||
if (timbreEntities == null) {
|
|
||||||
timbreEntities = new ArrayList<>();
|
|
||||||
}
|
|
||||||
List<VoiceDTO> voiceDTOs = timbreEntities.stream()
|
List<VoiceDTO> voiceDTOs = timbreEntities.stream()
|
||||||
.map(entity -> {
|
.map(entity -> {
|
||||||
VoiceDTO dto = new VoiceDTO(entity.getId(), entity.getName());
|
VoiceDTO dto = new VoiceDTO(entity.getId(), entity.getName());
|
||||||
|
|||||||
+19
-10
@@ -4,12 +4,8 @@ import java.net.URI;
|
|||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
import java.util.stream.Collectors;
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -30,8 +26,11 @@ import xiaozhi.common.page.PageData;
|
|||||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||||
import xiaozhi.common.utils.ConvertUtils;
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
import xiaozhi.common.utils.DateUtils;
|
import xiaozhi.common.utils.DateUtils;
|
||||||
|
import xiaozhi.common.utils.ToolUtil;
|
||||||
import xiaozhi.modules.model.entity.ModelConfigEntity;
|
import xiaozhi.modules.model.entity.ModelConfigEntity;
|
||||||
import xiaozhi.modules.model.service.ModelConfigService;
|
import xiaozhi.modules.model.service.ModelConfigService;
|
||||||
|
import xiaozhi.modules.sys.dao.SysUserDao;
|
||||||
|
import xiaozhi.modules.sys.entity.SysUserEntity;
|
||||||
import xiaozhi.modules.sys.service.SysUserService;
|
import xiaozhi.modules.sys.service.SysUserService;
|
||||||
import xiaozhi.modules.voiceclone.dao.VoiceCloneDao;
|
import xiaozhi.modules.voiceclone.dao.VoiceCloneDao;
|
||||||
import xiaozhi.modules.voiceclone.dto.VoiceCloneDTO;
|
import xiaozhi.modules.voiceclone.dto.VoiceCloneDTO;
|
||||||
@@ -47,6 +46,7 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
|
|||||||
|
|
||||||
private final ModelConfigService modelConfigService;
|
private final ModelConfigService modelConfigService;
|
||||||
private final SysUserService sysUserService;
|
private final SysUserService sysUserService;
|
||||||
|
private final SysUserDao sysUserDao;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -104,9 +104,11 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 批量保存
|
||||||
|
List<VoiceCloneEntity> batchInsertList = new ArrayList<>();
|
||||||
// 遍历选择的音色ID,为每个音色ID创建一条记录
|
// 遍历选择的音色ID,为每个音色ID创建一条记录
|
||||||
int index = 0;
|
int index = 0;
|
||||||
String namePrefix = DateUtils.format(new java.util.Date(), "MMddHHmm");
|
String namePrefix = DateUtils.format(new Date(), "MMddHHmm");
|
||||||
for (String voiceId : dto.getVoiceIds()) {
|
for (String voiceId : dto.getVoiceIds()) {
|
||||||
index++;
|
index++;
|
||||||
VoiceCloneEntity entity = new VoiceCloneEntity();
|
VoiceCloneEntity entity = new VoiceCloneEntity();
|
||||||
@@ -115,8 +117,10 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
|
|||||||
entity.setName(namePrefix + "_" + index);
|
entity.setName(namePrefix + "_" + index);
|
||||||
entity.setUserId(dto.getUserId());
|
entity.setUserId(dto.getUserId());
|
||||||
entity.setTrainStatus(0); // 默认训练中
|
entity.setTrainStatus(0); // 默认训练中
|
||||||
|
batchInsertList.add(entity);
|
||||||
baseDao.insert(entity);
|
}
|
||||||
|
if (ToolUtil.isNotEmpty(batchInsertList)) {
|
||||||
|
insertBatch(batchInsertList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,6 +191,11 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
|
|||||||
|
|
||||||
List<VoiceCloneResponseDTO> dtoList = new ArrayList<>(entityList.size());
|
List<VoiceCloneResponseDTO> dtoList = new ArrayList<>(entityList.size());
|
||||||
|
|
||||||
|
// 获取用户名称ID集合
|
||||||
|
Set<Long> userIdList = entityList.stream().map(VoiceCloneEntity::getUserId).collect(Collectors.toSet());
|
||||||
|
List<SysUserEntity> userList = sysUserDao.selectList(new QueryWrapper<SysUserEntity>().in("id", userIdList));
|
||||||
|
Map<Long, String> userMap = userList.stream().collect(Collectors.toMap(SysUserEntity::getId, SysUserEntity::getUsername));
|
||||||
|
|
||||||
// 转换每个实体为DTO
|
// 转换每个实体为DTO
|
||||||
for (VoiceCloneEntity entity : entityList) {
|
for (VoiceCloneEntity entity : entityList) {
|
||||||
VoiceCloneResponseDTO dto = ConvertUtils.sourceToTarget(entity, VoiceCloneResponseDTO.class);
|
VoiceCloneResponseDTO dto = ConvertUtils.sourceToTarget(entity, VoiceCloneResponseDTO.class);
|
||||||
@@ -198,7 +207,7 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
|
|||||||
|
|
||||||
// 设置用户名称
|
// 设置用户名称
|
||||||
if (entity.getUserId() != null) {
|
if (entity.getUserId() != null) {
|
||||||
dto.setUserName(sysUserService.getByUserId(entity.getUserId()).getUsername());
|
dto.setUserName(userMap.get(entity.getUserId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保trainStatus字段被正确设置,前端需要这个字段来判断是否为克隆音频
|
// 确保trainStatus字段被正确设置,前端需要这个字段来判断是否为克隆音频
|
||||||
|
|||||||
@@ -15,4 +15,23 @@
|
|||||||
FROM sys_dict_type
|
FROM sys_dict_type
|
||||||
WHERE id = #{dictTypeId}
|
WHERE id = #{dictTypeId}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="getDictTypesByIdList" resultType="java.lang.String">
|
||||||
|
SELECT
|
||||||
|
dict_type
|
||||||
|
FROM
|
||||||
|
sys_dict_type
|
||||||
|
WHERE
|
||||||
|
id IN (
|
||||||
|
SELECT
|
||||||
|
dict_type_id
|
||||||
|
FROM
|
||||||
|
sys_dict_data
|
||||||
|
WHERE
|
||||||
|
id IN
|
||||||
|
<foreach collection="dictDataIdList" item="id" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
)
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -193,18 +193,55 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ==================== 聊天消息流(弹幕样式) ==================== */
|
/* ==================== 聊天消息流(弹幕样式) ==================== */
|
||||||
.chat-stream {
|
.chat-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 100px;
|
bottom: 150px;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-stream {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
z-index: 100;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-ipt {
|
||||||
|
width: 100%;
|
||||||
|
display: none;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-ipt input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 24px;
|
||||||
|
background: rgba(35, 39, 42, 0.85);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
color: white;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-ipt input::placeholder {
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-ipt input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: rgba(88, 101, 242, 0.6);
|
||||||
|
box-shadow: 0 4px 16px rgba(88, 101, 242, 0.3);
|
||||||
|
background: rgba(45, 49, 52, 0.9);
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-message {
|
.chat-message {
|
||||||
@@ -955,10 +992,10 @@ body {
|
|||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-stream {
|
.chat-container {
|
||||||
width: 250px;
|
width: 230px;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
bottom: 80px;
|
bottom: 105px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-bubble {
|
.message-bubble {
|
||||||
|
|||||||
@@ -40,15 +40,10 @@ class App {
|
|||||||
// 关闭加载loading
|
// 关闭加载loading
|
||||||
this.setModelLoadingStatus(false);
|
this.setModelLoadingStatus(false);
|
||||||
|
|
||||||
log('应用初始化完成', 'success');
|
// 绑定页面卸载事件
|
||||||
}
|
this.bindUnload();
|
||||||
|
|
||||||
// 设置model加载状态
|
log('应用初始化完成', 'success');
|
||||||
setModelLoadingStatus(isLoading) {
|
|
||||||
const modelLoading = document.getElementById('modelLoading');
|
|
||||||
if (modelLoading) {
|
|
||||||
modelLoading.style.display = isLoading ? 'flex' : 'none';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化Live2D
|
// 初始化Live2D
|
||||||
@@ -81,6 +76,24 @@ class App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置model加载状态
|
||||||
|
setModelLoadingStatus(isLoading) {
|
||||||
|
const modelLoading = document.getElementById('modelLoading');
|
||||||
|
if (modelLoading) {
|
||||||
|
modelLoading.style.display = isLoading ? 'flex' : 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定页面卸载事件
|
||||||
|
bindUnload() {
|
||||||
|
window.addEventListener('beforeunload', () => {
|
||||||
|
// 销毁定时器
|
||||||
|
if (this.uiController && this.uiController.wsTimer) {
|
||||||
|
clearInterval(this.uiController.wsTimer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建并启动应用
|
// 创建并启动应用
|
||||||
@@ -89,17 +102,10 @@ const app = new App();
|
|||||||
// 将应用实例暴露到全局,供其他模块访问
|
// 将应用实例暴露到全局,供其他模块访问
|
||||||
window.chatApp = app;
|
window.chatApp = app;
|
||||||
|
|
||||||
// DOM加载完成后初始化
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
if (document.readyState === 'loading') {
|
// 初始化应用
|
||||||
document.addEventListener('DOMContentLoaded', () => app.init());
|
|
||||||
} else {
|
|
||||||
app.init();
|
app.init();
|
||||||
window.addEventListener('beforeunload', () => {
|
});
|
||||||
// 销毁定时器
|
|
||||||
if (app.uiController && app.uiController.wsTimer) {
|
|
||||||
clearInterval(app.uiController.wsTimer);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export default app;
|
export default app;
|
||||||
|
|||||||
@@ -58,12 +58,16 @@ export function saveConfig() {
|
|||||||
|
|
||||||
// 获取配置值
|
// 获取配置值
|
||||||
export function getConfig() {
|
export function getConfig() {
|
||||||
const deviceMac = document.getElementById('deviceMac').value.trim();
|
// 从DOM获取值
|
||||||
|
const deviceMac = document.getElementById('deviceMac')?.value.trim() || '';
|
||||||
|
const deviceName = document.getElementById('deviceName')?.value.trim() || '';
|
||||||
|
const clientId = document.getElementById('clientId')?.value.trim() || '';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
deviceId: deviceMac, // 使用MAC地址作为deviceId
|
deviceId: deviceMac, // 使用MAC地址作为deviceId
|
||||||
deviceName: document.getElementById('deviceName').value.trim(),
|
deviceName,
|
||||||
deviceMac: deviceMac,
|
deviceMac,
|
||||||
clientId: document.getElementById('clientId').value.trim()
|
clientId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -307,14 +307,7 @@ export class AudioRecorder {
|
|||||||
|
|
||||||
// 发送监听开始消息
|
// 发送监听开始消息
|
||||||
if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
|
if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
|
||||||
const listenMessage = {
|
log(`发送录音开始消息`, 'info');
|
||||||
type: 'listen',
|
|
||||||
mode: 'manual',
|
|
||||||
state: 'start'
|
|
||||||
};
|
|
||||||
|
|
||||||
log(`发送录音开始消息: ${JSON.stringify(listenMessage)}`, 'info');
|
|
||||||
this.websocket.send(JSON.stringify(listenMessage));
|
|
||||||
} else {
|
} else {
|
||||||
log('WebSocket未连接,无法发送开始消息', 'error');
|
log('WebSocket未连接,无法发送开始消息', 'error');
|
||||||
return false;
|
return false;
|
||||||
@@ -403,14 +396,6 @@ export class AudioRecorder {
|
|||||||
if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
|
if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
|
||||||
const emptyOpusFrame = new Uint8Array(0);
|
const emptyOpusFrame = new Uint8Array(0);
|
||||||
this.websocket.send(emptyOpusFrame);
|
this.websocket.send(emptyOpusFrame);
|
||||||
|
|
||||||
const stopMessage = {
|
|
||||||
type: 'listen',
|
|
||||||
mode: 'manual',
|
|
||||||
state: 'stop'
|
|
||||||
};
|
|
||||||
|
|
||||||
this.websocket.send(JSON.stringify(stopMessage));
|
|
||||||
log('已发送录音停止信号', 'info');
|
log('已发送录音停止信号', 'info');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,18 +62,6 @@ function validateConfig(config) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断wsUrl路径是否存在错误
|
|
||||||
function validateWsUrl(wsUrl) {
|
|
||||||
if (wsUrl === '') return false;
|
|
||||||
// 检查URL格式
|
|
||||||
if (!wsUrl.startsWith('ws://') && !wsUrl.startsWith('wss://')) {
|
|
||||||
log('URL格式错误,必须以ws://或wss://开头', 'error');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// OTA发送请求,验证状态,并返回响应数据
|
// OTA发送请求,验证状态,并返回响应数据
|
||||||
async function sendOTA(otaUrl, config) {
|
async function sendOTA(otaUrl, config) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -421,7 +421,6 @@ export class WebSocketHandler {
|
|||||||
|
|
||||||
const listenMessage = {
|
const listenMessage = {
|
||||||
type: 'listen',
|
type: 'listen',
|
||||||
mode: 'manual',
|
|
||||||
state: 'detect',
|
state: 'detect',
|
||||||
text: text
|
text: text
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -133,6 +133,21 @@ class UIController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 消息输入框事件
|
||||||
|
const chatIpt = document.getElementById('chatIpt');
|
||||||
|
if (chatIpt) {
|
||||||
|
const wsHandler = getWebSocketHandler();
|
||||||
|
chatIpt.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
if (e.target.value) {
|
||||||
|
wsHandler.sendTextMessage(e.target.value);
|
||||||
|
e.target.value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 关闭按钮
|
// 关闭按钮
|
||||||
const closeButtons = document.querySelectorAll('.close-btn');
|
const closeButtons = document.querySelectorAll('.close-btn');
|
||||||
closeButtons.forEach(btn => {
|
closeButtons.forEach(btn => {
|
||||||
@@ -247,7 +262,7 @@ class UIController {
|
|||||||
const recordBtn = document.getElementById('recordBtn');
|
const recordBtn = document.getElementById('recordBtn');
|
||||||
if (recordBtn) {
|
if (recordBtn) {
|
||||||
if (isRecording) {
|
if (isRecording) {
|
||||||
recordBtn.querySelector('.btn-text').textContent = `录音中 ${seconds.toFixed(1)}秒`;
|
recordBtn.querySelector('.btn-text').textContent = `录音中`;
|
||||||
recordBtn.classList.add('recording');
|
recordBtn.classList.add('recording');
|
||||||
} else {
|
} else {
|
||||||
recordBtn.querySelector('.btn-text').textContent = '录音';
|
recordBtn.querySelector('.btn-text').textContent = '录音';
|
||||||
@@ -379,6 +394,11 @@ class UIController {
|
|||||||
// 显示连接中消息
|
// 显示连接中消息
|
||||||
this.addChatMessage('正在连接服务器...', false);
|
this.addChatMessage('正在连接服务器...', false);
|
||||||
|
|
||||||
|
const chatIpt = document.getElementById('chatIpt');
|
||||||
|
if (chatIpt) {
|
||||||
|
chatIpt.style.display = 'flex';
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 获取配置信息
|
// 获取配置信息
|
||||||
const config = getConfig();
|
const config = getConfig();
|
||||||
|
|||||||
@@ -1,16 +1,3 @@
|
|||||||
// DOM元素
|
|
||||||
const connectButton = document.getElementById('connectButton');
|
|
||||||
const serverUrlInput = document.getElementById('serverUrl');
|
|
||||||
const connectionStatus = document.getElementById('connectionStatus');
|
|
||||||
const messageInput = document.getElementById('messageInput');
|
|
||||||
const sendTextButton = document.getElementById('sendTextButton');
|
|
||||||
const recordButton = document.getElementById('recordButton');
|
|
||||||
const stopButton = document.getElementById('stopButton');
|
|
||||||
// 会话记录
|
|
||||||
const conversationDiv = document.getElementById('conversation');
|
|
||||||
const logContainer = document.getElementById('logContainer');
|
|
||||||
let visualizerCanvas = document.getElementById('audioVisualizer');
|
|
||||||
|
|
||||||
// ota 是否连接成功,修改成对应的样式
|
// ota 是否连接成功,修改成对应的样式
|
||||||
export function otaStatusStyle(flan) {
|
export function otaStatusStyle(flan) {
|
||||||
const otaStatusElement = document.getElementById('otaStatus');
|
const otaStatusElement = document.getElementById('otaStatus');
|
||||||
@@ -25,11 +12,6 @@ export function otaStatusStyle(flan) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ota 是否连接成功,修改成对应的样式
|
|
||||||
export function getLogContainer(flan) {
|
|
||||||
return logContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新Opus库状态显示
|
// 更新Opus库状态显示
|
||||||
export function updateScriptStatus(message, type) {
|
export function updateScriptStatus(message, type) {
|
||||||
const statusElement = document.getElementById('scriptStatus');
|
const statusElement = document.getElementById('scriptStatus');
|
||||||
@@ -41,12 +23,3 @@ export function updateScriptStatus(message, type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加消息到会话记录
|
|
||||||
export function addMessage(text, isUser = false) {
|
|
||||||
const messageDiv = document.createElement('div');
|
|
||||||
messageDiv.className = `message ${isUser ? 'user' : 'server'}`;
|
|
||||||
messageDiv.textContent = text;
|
|
||||||
conversationDiv.appendChild(messageDiv);
|
|
||||||
conversationDiv.scrollTop = conversationDiv.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -68,8 +68,13 @@
|
|||||||
<canvas id="live2d-stage"></canvas>
|
<canvas id="live2d-stage"></canvas>
|
||||||
|
|
||||||
<!-- 聊天消息流(弹幕样式) -->
|
<!-- 聊天消息流(弹幕样式) -->
|
||||||
<div class="chat-stream" id="chatStream">
|
<div class="chat-container">
|
||||||
<!-- 聊天消息将动态插入这里 -->
|
<div class="chat-stream" id="chatStream">
|
||||||
|
<!-- 聊天消息将动态插入这里 -->
|
||||||
|
</div>
|
||||||
|
<div class="chat-ipt" id="chatIpt">
|
||||||
|
<input type="text" id="messageInput" autocomplete="off" placeholder="输入消息,按Enter发送">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 底部控制栏 -->
|
<!-- 底部控制栏 -->
|
||||||
|
|||||||
Reference in New Issue
Block a user