Commit 6aab2c93 authored by 赵灿灿's avatar 赵灿灿

增加查询专家信息

parent f1f8a6eb
Pipeline #21459 passed with stages
in 4 minutes and 24 seconds
......@@ -767,4 +767,114 @@ public class LangChainController {
chatService.deleteChat(sessionId);
return Result.successData("删除成功");
}
//调用智能体
@GetMapping("/sseTest")
public ResponseEntity<SseEmitter> sseTest(@RequestParam String chatMessage,@RequestParam String dialogId,
@RequestParam String selectedExpert ,@RequestParam String selectedOrg) {
String condition="";
// String regionName= chatService.getAuthRegionName();
// if("内部专家".equals(selectedExpert)) {
// if ("组织内".equals(selectedOrg) && StringUtils.isNotEmpty(regionName)) {
// condition = ",只能查找" + regionName;
// } else if ("组织外".equals(selectedOrg) && StringUtils.isNotEmpty(regionName)) {
// condition = ",排除" + regionName;
// }
// }
// String urlAddr = "http://10.32.41.35:40517/scene_gateway/agent/6809d0895428476bb6789ad70c525c97";
String urlAddr ="http://10.32.41.35:40517/scene_gateway/agent/468fd1bfa3e543fc8526a82a9093097a"; //chatService.getUrl(selectedExpert);
//String urlAddr = "http://10.32.41.35:40517/scene_gateway/agent/83f77143b09c461993dd9a7db403eb94";
SseEmitter emitter = new SseEmitter(0L);
QuestionRequest questionRequest=new QuestionRequest();
questionRequest.setKeyword(chatMessage+condition);
questionRequest.setRequestId(getRequestId());
questionRequest.setDialogId(dialogId);
// Conversations conversations=chatService.saveConversations(dialogId,chatMessage,selectedExpert);
// Messages messagesQusetion=new Messages();
// messagesQusetion.setRequestId(dialogId);
// messagesQusetion.setContent(chatMessage);
// messagesQusetion.setRole("user");
// messagesQusetion.setModelName("qwen2.5-72b");
// messagesQusetion=chatService.insertQuestionMessage(conversations,messagesQusetion);
// Messages messagesContent=new Messages();
// messagesContent.setRole("assistant");
// messagesContent.setModelName("qwen2.5-72b");
// messagesContent.setParentMsgId(messagesQusetion.getId());
// messagesContent.setSort(messagesQusetion.getSort()+1);
// chatService.insertMessage(conversations,messagesContent);
String params = JsonUtils.objectToJson(questionRequest);
StringBuffer content =new StringBuffer();
StringBuffer lineCotent =new StringBuffer();
new Thread(() -> {
HttpURLConnection connection = null;
try {
URL url = new URL(urlAddr);
// 建立链接
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("AuthToken", "4009fe23e6b648539792330c14f5ed8e");
// connection.setRequestProperty("AuthToken", "fc40db5b7abe47dabfe1899e61fde2d7");
// 允许输入和输出
connection.setDoInput(true);
connection.setDoOutput(true);
// 设置超时为0,表示无限制
connection.setConnectTimeout(0);
connection.setReadTimeout(0);
try (OutputStream os = connection.getOutputStream()) {
os.write(params.getBytes(StandardCharsets.UTF_8));
os.flush();
}
// 检查响应码
int responseCode = connection.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
emitter.completeWithError(new RuntimeException("SSE 连接失败: " + responseCode));
return;
}
// 持续读取 SSE 数据流
try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
logger.info(line);
if (!line.startsWith("data:CALLBACK#")) {
if (line.startsWith("data:")) {
String data = line.substring(5).trim();
if("stop".equals(data))
{
emitter.send(SseEmitter.event().data("stop"),MediaType.parseMediaType("application/json; charset=UTF-8"));
}else
{
lineCotent.append(data);
String sendData=data.replace("attachment#[]#attachment","").replace("source#[]#source","")
.replace("#","").replace("*","");
emitter.send(SseEmitter.event().data(sendData), MediaType.parseMediaType("application/json; charset=UTF-8"));
if(!sendData.startsWith("SUGGEST["))
content.append(sendData);
}
}
}
}
}
emitter.complete(); // 流结束
} catch (Exception e) {
emitter.completeWithError(e);
logger.info(e.getMessage());
} finally {
// messagesContent.setContent(content.toString());
// chatService.updateMessage(messagesContent);
if (connection != null) {
connection.disconnect();
}
logger.info(lineCotent.toString());
}
}).start();
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_EVENT_STREAM_VALUE)
.body(emitter);
}
}
package com.infoepoch.pms.dispatchassistant.domain.expertInformation;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.math.BigDecimal;
import java.util.Date;
/**
* generated by code-generator
* 专家信息表
*/
public class ExpertInformation {
/**
* 专家ID
*/
private Integer id;
/**
* 专家姓名
*/
private String name;
/**
* 专家性别
*/
private String gender;
/**
* 专家邮箱
*/
private String mailbox;
/**
* 专家电话
*/
private String telephone;
/**
* 专家OA用户名
*/
private String userName;
/**
* 入职时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date employmentDate;
/**
* 专家部门
*/
private String department;
/**
* 专家科室
*/
private String group;
/**
* 职位
*/
private String position;
/**
* 业务范围
*/
private String scopeBusiness;
/**
* 调用状态
*/
private String state;
/**
* 擅长领域
*/
private String specializesFields;
/**
* 专家简介
*/
private String expertIntroduction;
/**
* 所属OA组织编码
*/
private String organizationalCode;
/**
* 记录时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date recordTime;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
/**
* 专家照片
*/
private String photo;
/**
* 专家科室ID
*/
private Integer groupId;
/**
* 专家部门ID
*/
private Integer departmentId;
/**
* 专家用户ID
*/
private Integer userId;
/**
* 专家满意度均分
*/
private BigDecimal satisfactionScore;
/**
* 专家职称
*/
private String level;
/**
* 专家是否启用(1是启用,0是禁用,2是冻结)
*/
private String disabled;
/**
* 所属地市
*/
private String regionCode;
/**
* 排序
*/
private Integer sort;
/**
* 私有无参构造
*/
private ExpertInformation() {
}
/**
* 仓储还原
*/
public ExpertInformation(Integer id, String name, String gender, String mailbox, String telephone, String userName, Date employmentDate, String department, String group, String position, String scopeBusiness, String state, String specializesFields, String expertIntroduction, String organizationalCode, Date recordTime, Date updateTime, String photo, Integer groupId, Integer departmentId, Integer userId, BigDecimal satisfactionScore, String level, String disabled, String regionCode, Integer sort) {
this.id = id;
this.name = name;
this.gender = gender;
this.mailbox = mailbox;
this.telephone = telephone;
this.userName = userName;
this.employmentDate = employmentDate;
this.department = department;
this.group = group;
this.position = position;
this.scopeBusiness = scopeBusiness;
this.state = state;
this.specializesFields = specializesFields;
this.expertIntroduction = expertIntroduction;
this.organizationalCode = organizationalCode;
this.recordTime = recordTime;
this.updateTime = updateTime;
this.photo = photo;
this.groupId = groupId;
this.departmentId = departmentId;
this.userId = userId;
this.satisfactionScore = satisfactionScore;
this.level = level;
this.disabled = disabled;
this.regionCode = regionCode;
this.sort = sort;
}
/**
* 校验
*/
public void verify() {
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public String getGender() {
return gender;
}
public String getMailbox() {
return mailbox;
}
public String getTelephone() {
return telephone;
}
public String getUserName() {
return userName;
}
public Date getEmploymentDate() {
return employmentDate;
}
public String getDepartment() {
return department;
}
public String getGroup() {
return group;
}
public String getPosition() {
return position;
}
public String getScopeBusiness() {
return scopeBusiness;
}
public String getState() {
return state;
}
public String getSpecializesFields() {
return specializesFields;
}
public String getExpertIntroduction() {
return expertIntroduction;
}
public String getOrganizationalCode() {
return organizationalCode;
}
public Date getRecordTime() {
return recordTime;
}
public Date getUpdateTime() {
return updateTime;
}
public String getPhoto() {
return photo;
}
public Integer getGroupId() {
return groupId;
}
public Integer getDepartmentId() {
return departmentId;
}
public Integer getUserId() {
return userId;
}
public BigDecimal getSatisfactionScore() {
return satisfactionScore;
}
public String getLevel() {
return level;
}
public String getDisabled() {
return disabled;
}
public String getRegionCode() {
return regionCode;
}
public Integer getSort() {
return sort;
}
}
package com.infoepoch.pms.dispatchassistant.domain.expertInformation;
import com.infoepoch.pms.dispatchassistant.common.component.AbstractCriteria;
import java.util.Date;
/**
* generated by code-generator
* 专家信息表查询条件类
*/
public class ExpertInformationCriteria extends AbstractCriteria {
//region 样例
//public boolean byId() {
// return this.andMap.containsKey("Id");
//}
//
//private String id;
//
//public String getId() {
// if (byId())
// return id;
// return null;
//}
//
//public void setId(String value) {
// this.id = value;
// this.andMap.put("Id", value);
//}
//endregion
//region 专家姓名
public boolean byName() {
return this.andMap.containsKey("Name");
}
private String name;
public String getName() {
if (byName())
return name;
return null;
}
public void setName(String value) {
this.name = value;
this.andMap.put("Name", value);
}
//endregion
//region 专家姓名模糊查询
public boolean byNameContain() {
return this.andMap.containsKey("NameContain");
}
private String nameContain;
public String getNameContain() {
if (byNameContain())
return nameContain;
return null;
}
public void setNameContain(String value) {
this.nameContain = value;
this.andMap.put("NameContain", value);
}
//endregion
//region 专家性别
public boolean byGender() {
return this.andMap.containsKey("Gender");
}
private String gender;
public String getGender() {
if (byGender())
return gender;
return null;
}
public void setGender(String value) {
this.gender = value;
this.andMap.put("Gender", value);
}
//endregion
//region 专家性别模糊查询
public boolean byGenderContain() {
return this.andMap.containsKey("GenderContain");
}
private String genderContain;
public String getGenderContain() {
if (byGenderContain())
return genderContain;
return null;
}
public void setGenderContain(String value) {
this.genderContain = value;
this.andMap.put("GenderContain", value);
}
//endregion
//region 专家邮箱
public boolean byMailbox() {
return this.andMap.containsKey("Mailbox");
}
private String mailbox;
public String getMailbox() {
if (byMailbox())
return mailbox;
return null;
}
public void setMailbox(String value) {
this.mailbox = value;
this.andMap.put("Mailbox", value);
}
//endregion
//region 专家邮箱模糊查询
public boolean byMailboxContain() {
return this.andMap.containsKey("MailboxContain");
}
private String mailboxContain;
public String getMailboxContain() {
if (byMailboxContain())
return mailboxContain;
return null;
}
public void setMailboxContain(String value) {
this.mailboxContain = value;
this.andMap.put("MailboxContain", value);
}
//endregion
//region 专家电话
public boolean byTelephone() {
return this.andMap.containsKey("Telephone");
}
private String telephone;
public String getTelephone() {
if (byTelephone())
return telephone;
return null;
}
public void setTelephone(String value) {
this.telephone = value;
this.andMap.put("Telephone", value);
}
//endregion
//region 专家电话模糊查询
public boolean byTelephoneContain() {
return this.andMap.containsKey("TelephoneContain");
}
private String telephoneContain;
public String getTelephoneContain() {
if (byTelephoneContain())
return telephoneContain;
return null;
}
public void setTelephoneContain(String value) {
this.telephoneContain = value;
this.andMap.put("TelephoneContain", value);
}
//endregion
//region 专家OA用户名
public boolean byUserName() {
return this.andMap.containsKey("UserName");
}
private String userName;
public String getUserName() {
if (byUserName())
return userName;
return null;
}
public void setUserName(String value) {
this.userName = value;
this.andMap.put("UserName", value);
}
//endregion
//region 专家OA用户名模糊查询
public boolean byUserNameContain() {
return this.andMap.containsKey("UserNameContain");
}
private String userNameContain;
public String getUserNameContain() {
if (byUserNameContain())
return userNameContain;
return null;
}
public void setUserNameContain(String value) {
this.userNameContain = value;
this.andMap.put("UserNameContain", value);
}
//endregion
//region 入职时间
public boolean byEmploymentDate() {
return this.andMap.containsKey("EmploymentDate");
}
private Date employmentDate;
public Date getEmploymentDate() {
if (byEmploymentDate())
return employmentDate;
return null;
}
public void setEmploymentDate(Date value) {
this.employmentDate = value;
this.andMap.put("EmploymentDate", value);
}
//endregion
//region 专家部门
public boolean byDepartment() {
return this.andMap.containsKey("Department");
}
private String department;
public String getDepartment() {
if (byDepartment())
return department;
return null;
}
public void setDepartment(String value) {
this.department = value;
this.andMap.put("Department", value);
}
//endregion
//region 专家部门模糊查询
public boolean byDepartmentContain() {
return this.andMap.containsKey("DepartmentContain");
}
private String departmentContain;
public String getDepartmentContain() {
if (byDepartmentContain())
return departmentContain;
return null;
}
public void setDepartmentContain(String value) {
this.departmentContain = value;
this.andMap.put("DepartmentContain", value);
}
//endregion
//region 专家科室
public boolean byGroup() {
return this.andMap.containsKey("Group");
}
private String group;
public String getGroup() {
if (byGroup())
return group;
return null;
}
public void setGroup(String value) {
this.group = value;
this.andMap.put("Group", value);
}
//endregion
//region 专家科室模糊查询
public boolean byGroupContain() {
return this.andMap.containsKey("GroupContain");
}
private String groupContain;
public String getGroupContain() {
if (byGroupContain())
return groupContain;
return null;
}
public void setGroupContain(String value) {
this.groupContain = value;
this.andMap.put("GroupContain", value);
}
//endregion
//region 职位
public boolean byPosition() {
return this.andMap.containsKey("Position");
}
private String position;
public String getPosition() {
if (byPosition())
return position;
return null;
}
public void setPosition(String value) {
this.position = value;
this.andMap.put("Position", value);
}
//endregion
//region 职位模糊查询
public boolean byPositionContain() {
return this.andMap.containsKey("PositionContain");
}
private String positionContain;
public String getPositionContain() {
if (byPositionContain())
return positionContain;
return null;
}
public void setPositionContain(String value) {
this.positionContain = value;
this.andMap.put("PositionContain", value);
}
//endregion
//region 业务范围
public boolean byScopeBusiness() {
return this.andMap.containsKey("ScopeBusiness");
}
private String scopeBusiness;
public String getScopeBusiness() {
if (byScopeBusiness())
return scopeBusiness;
return null;
}
public void setScopeBusiness(String value) {
this.scopeBusiness = value;
this.andMap.put("ScopeBusiness", value);
}
//endregion
//region 业务范围模糊查询
public boolean byScopeBusinessContain() {
return this.andMap.containsKey("ScopeBusinessContain");
}
private String scopeBusinessContain;
public String getScopeBusinessContain() {
if (byScopeBusinessContain())
return scopeBusinessContain;
return null;
}
public void setScopeBusinessContain(String value) {
this.scopeBusinessContain = value;
this.andMap.put("ScopeBusinessContain", value);
}
//endregion
//region 调用状态
public boolean byState() {
return this.andMap.containsKey("State");
}
private String state;
public String getState() {
if (byState())
return state;
return null;
}
public void setState(String value) {
this.state = value;
this.andMap.put("State", value);
}
//endregion
//region 调用状态模糊查询
public boolean byStateContain() {
return this.andMap.containsKey("StateContain");
}
private String stateContain;
public String getStateContain() {
if (byStateContain())
return stateContain;
return null;
}
public void setStateContain(String value) {
this.stateContain = value;
this.andMap.put("StateContain", value);
}
//endregion
//region 擅长领域
public boolean bySpecializesFields() {
return this.andMap.containsKey("SpecializesFields");
}
private String specializesFields;
public String getSpecializesFields() {
if (bySpecializesFields())
return specializesFields;
return null;
}
public void setSpecializesFields(String value) {
this.specializesFields = value;
this.andMap.put("SpecializesFields", value);
}
//endregion
//region 擅长领域模糊查询
public boolean bySpecializesFieldsContain() {
return this.andMap.containsKey("SpecializesFieldsContain");
}
private String specializesFieldsContain;
public String getSpecializesFieldsContain() {
if (bySpecializesFieldsContain())
return specializesFieldsContain;
return null;
}
public void setSpecializesFieldsContain(String value) {
this.specializesFieldsContain = value;
this.andMap.put("SpecializesFieldsContain", value);
}
//endregion
//region 专家简介
public boolean byExpertIntroduction() {
return this.andMap.containsKey("ExpertIntroduction");
}
private String expertIntroduction;
public String getExpertIntroduction() {
if (byExpertIntroduction())
return expertIntroduction;
return null;
}
public void setExpertIntroduction(String value) {
this.expertIntroduction = value;
this.andMap.put("ExpertIntroduction", value);
}
//endregion
//region 专家简介模糊查询
public boolean byExpertIntroductionContain() {
return this.andMap.containsKey("ExpertIntroductionContain");
}
private String expertIntroductionContain;
public String getExpertIntroductionContain() {
if (byExpertIntroductionContain())
return expertIntroductionContain;
return null;
}
public void setExpertIntroductionContain(String value) {
this.expertIntroductionContain = value;
this.andMap.put("ExpertIntroductionContain", value);
}
//endregion
//region 所属OA组织编码
public boolean byOrganizationalCode() {
return this.andMap.containsKey("OrganizationalCode");
}
private String organizationalCode;
public String getOrganizationalCode() {
if (byOrganizationalCode())
return organizationalCode;
return null;
}
public void setOrganizationalCode(String value) {
this.organizationalCode = value;
this.andMap.put("OrganizationalCode", value);
}
//endregion
//region 所属OA组织编码模糊查询
public boolean byOrganizationalCodeContain() {
return this.andMap.containsKey("OrganizationalCodeContain");
}
private String organizationalCodeContain;
public String getOrganizationalCodeContain() {
if (byOrganizationalCodeContain())
return organizationalCodeContain;
return null;
}
public void setOrganizationalCodeContain(String value) {
this.organizationalCodeContain = value;
this.andMap.put("OrganizationalCodeContain", value);
}
//endregion
//region 专家职称
public boolean byLevel() {
return this.andMap.containsKey("Level");
}
private String level;
public String getLevel() {
if (byLevel())
return level;
return null;
}
public void setLevel(String value) {
this.level = value;
this.andMap.put("Level", value);
}
//endregion
//region 专家职称模糊查询
public boolean byLevelContain() {
return this.andMap.containsKey("LevelContain");
}
private String levelContain;
public String getLevelContain() {
if (byLevelContain())
return levelContain;
return null;
}
public void setLevelContain(String value) {
this.levelContain = value;
this.andMap.put("LevelContain", value);
}
//endregion
//region 所属地市
public boolean byRegionCode() {
return this.andMap.containsKey("RegionCode");
}
private String regionCode;
public String getRegionCode() {
if (byRegionCode())
return regionCode;
return null;
}
public void setRegionCode(String value) {
this.regionCode = value;
this.andMap.put("RegionCode", value);
}
//endregion
//region 所属地市模糊查询
public boolean byRegionCodeContain() {
return this.andMap.containsKey("RegionCodeContain");
}
private String regionCodeContain;
public String getRegionCodeContain() {
if (byRegionCodeContain())
return regionCodeContain;
return null;
}
public void setRegionCodeContain(String value) {
this.regionCodeContain = value;
this.andMap.put("RegionCodeContain", value);
}
//endregion
}
\ No newline at end of file
package com.infoepoch.pms.dispatchassistant.domain.expertInformation;
import com.infoepoch.pms.dispatchassistant.common.exception.ValidationException;
import com.infoepoch.pms.dispatchassistant.common.utils.StringUtils;
import com.infoepoch.pms.dispatchassistant.infractructure.langchain.ConversationsRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* generated by code-generator
* 专家信息表Service
**/
@Service
public class ExpertInformationService {
@Autowired
private IExpertInformationRepository repository;
private static final Logger LogHelper = LoggerFactory.getLogger(ConversationsRepository.class);
/**
* 根据条件查询单个对象
**/
public ExpertInformation querySingle( ExpertInformationCriteria criteria) {
criteria.removeMapNullOrEmpty();
ExpertInformation entity;
try {
entity = repository.selectOneByCriteria(criteria);
return entity;
} catch (Exception e) {
LogHelper.info(e.getMessage());
throw new ValidationException("根据条件查询专家信息表 数据 失败。");
}
}
/**
* 根据id查询
**/
public ExpertInformation queryById(String id) {
if (StringUtils.isBlank(id)){
throw new ValidationException("唯一标识不能为空");
};
ExpertInformation entity;
try {
entity = repository.selectById(id);
return entity;
} catch (Exception e) {
LogHelper.info(e.getMessage());
throw new ValidationException("根据id查询 专家信息表 数据 失败。");
}
}
/**
* 根据条件查询列表(可分页)
**/
public Map<String, Object> queryList( ExpertInformationCriteria criteria) {
try {
criteria.removeMapNullOrEmpty();
if (criteria.byPage()) {
List<ExpertInformation> entityList = repository.selectCriteriaByPage(criteria, criteria.getPageSize(), criteria.getPageSize());
int totalCount = repository.selectCountByCriteria(criteria);
Map<String, Object> map = new HashMap<>();
map.put("totalCount", totalCount);
map.put("entityList", entityList);
return map;
} else {
if (!criteria.hasCriteria())
throw new ValidationException("请输入查询条件。");
return null;
}
} catch (Exception e) {
LogHelper.info(e.getMessage());
throw new ValidationException("根据条件查询 专家信息表 列表 数据 失败。");
}
}
/**
* 保存
**/
public boolean save( ExpertInformation entity) {
try {
entity.verify();
boolean insert = repository.insert(entity);
return insert;
} catch (Exception e) {
LogHelper.info(e.getMessage());
throw new ValidationException("保存 专家信息表 数据 失败。");
}
}
/**
* 修改
**/
public boolean modify( ExpertInformation entity) {
try {
entity.verify();
boolean update = repository.update(entity);
return update;
} catch (Exception e) {
LogHelper.info(e.getMessage());
throw new ValidationException("修改 专家信息表 数据 失败。");
}
}
}
package com.infoepoch.pms.dispatchassistant.domain.expertInformation;
import java.util.List;
/**
* generated by code-generator
* 专家信息表仓储接口
*/
public interface IExpertInformationRepository {
/**
* 表序列id
*/
Long sequenceId();
/**
* 新增
* @param: [entity]
*/
boolean insert(ExpertInformation entity);
/**
* 更新
* @param: [entity]
*/
boolean update(ExpertInformation entity);
/**
* 批量新增
* @param: [entitys]
*/
int[] batchInsert(List<ExpertInformation> entitys);
/**
* 批量更新
* @param: [entitys]
*/
int[] batchUpdate(List<ExpertInformation> entitys);
/**
* 删除
* @param: [id]
*/
boolean delete(String id);
// region select
/**
* 根据Id查询
* @param: [id]
*/
ExpertInformation selectById(String id);
/**
* 根据查询条件查询单个对象
* @param: [criteria]
*/
ExpertInformation selectOneByCriteria(ExpertInformationCriteria criteria);
/**
* 根据查询条件查询对象集合
* @param: [criteria]
*/
List<ExpertInformation> selectByCriteria(ExpertInformationCriteria criteria);
/**
* 根据查询条件分页查询对象结合
* @param: [criteria, pageNum, pageSize]
*/
List<ExpertInformation> selectCriteriaByPage(ExpertInformationCriteria criteria, int pageNum, int pageSize);
/**
* 根据条件查询对象总记录数
* @param: [criteria]
*/
Integer selectCountByCriteria(ExpertInformationCriteria criteria);
// endregion
}
package com.infoepoch.pms.dispatchassistant.infractructure.expertInformation;
import com.infoepoch.pms.dispatchassistant.common.utils.OracleUtils;
import com.infoepoch.pms.dispatchassistant.domain.expertInformation.ExpertInformation;
import com.infoepoch.pms.dispatchassistant.domain.expertInformation.ExpertInformationCriteria;
import com.infoepoch.pms.dispatchassistant.domain.expertInformation.IExpertInformationRepository;
import com.infoepoch.pms.dispatchassistant.infractructure.langchain.ConversationsRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.PreparedStatement;
import java.sql.Timestamp;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* generated by code-generator
* 专家信息表仓储实现
*/
@Repository
public class ExpertInformationRepository implements IExpertInformationRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
private static final Logger LogHelper = LoggerFactory.getLogger(ConversationsRepository.class);
/**
* 查询表序列id
*/
@Override
public Long sequenceId() {
StringBuffer buffer = new StringBuffer("SELECT SEQ_T_EXPERT_INFORMATION.NEXTVAL FROM DUAL ");
return jdbcTemplate.queryForObject(buffer.toString(), Long.class);
}
/**
* 新增
*/
@Override
public boolean insert(ExpertInformation entity) {
String sql = "INSERT INTO T_EXPERT_INFORMATION(EI_ID, EI_NAME, EI_GENDER, EI_MAILBOX, EI_TELEPHONE, EI_USER_NAME, EI_EMPLOYMENT_DATE, EI_DEPARTMENT, EI_GROUP, EI_POSITION, EI_SCOPE_BUSINESS, EI_STATE, EI_SPECIALIZES_FIELDS, EI_EXPERT_INTRODUCTION, EI_ORGANIZATIONAL_CODE, EI_RECORD_TIME, EI_UPDATE_TIME, EI_PHOTO, EI_GROUP_ID, EI_DEPARTMENT_ID, EI_USER_ID, EI_SATISFACTION_SCORE, EI_LEVEL, EI_DISABLED, EI_REGION_CODE, EI_SORT) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
int result=0;
try {
result = jdbcTemplate.update(sql, entity.getId(), entity.getName(), entity.getGender(), entity.getMailbox(), entity.getTelephone(), entity.getUserName(), entity.getEmploymentDate(), entity.getDepartment(), entity.getGroup(), entity.getPosition(), entity.getScopeBusiness(), entity.getState(), entity.getSpecializesFields(), entity.getExpertIntroduction(), entity.getOrganizationalCode(), entity.getRecordTime(), entity.getUpdateTime(), entity.getPhoto(), entity.getGroupId(), entity.getDepartmentId(), entity.getUserId(), entity.getSatisfactionScore(), entity.getLevel(), entity.getDisabled(), entity.getRegionCode(), entity.getSort());
} catch (Exception e) {
LogHelper.info(e.getMessage());
//throw new ServiceException("新增 专家信息表 数据 失败。");
}
return result > 0;
}
/**
* 更新
*/
@Override
public boolean update(ExpertInformation entity) {
String sql = "UPDATE T_EXPERT_INFORMATION SET EI_NAME = ?, EI_GENDER = ?, EI_MAILBOX = ?, EI_TELEPHONE = ?, EI_USER_NAME = ?, EI_EMPLOYMENT_DATE = ?, EI_DEPARTMENT = ?, EI_GROUP = ?, EI_POSITION = ?, EI_SCOPE_BUSINESS = ?, EI_STATE = ?, EI_SPECIALIZES_FIELDS = ?, EI_EXPERT_INTRODUCTION = ?, EI_ORGANIZATIONAL_CODE = ?, EI_RECORD_TIME = ?, EI_UPDATE_TIME = ?, EI_PHOTO = ?, EI_GROUP_ID = ?, EI_DEPARTMENT_ID = ?, EI_USER_ID = ?, EI_SATISFACTION_SCORE = ?, EI_LEVEL = ?, EI_DISABLED = ?, EI_REGION_CODE = ?, EI_SORT = ? WHERE EI_ID = ?";
int result=0;
try {
result = jdbcTemplate.update(sql, entity.getName(), entity.getGender(), entity.getMailbox(), entity.getTelephone(), entity.getUserName(), entity.getEmploymentDate(), entity.getDepartment(), entity.getGroup(), entity.getPosition(), entity.getScopeBusiness(), entity.getState(), entity.getSpecializesFields(), entity.getExpertIntroduction(), entity.getOrganizationalCode(), entity.getRecordTime(), entity.getUpdateTime(), entity.getPhoto(), entity.getGroupId(), entity.getDepartmentId(), entity.getUserId(), entity.getSatisfactionScore(), entity.getLevel(), entity.getDisabled(), entity.getRegionCode(), entity.getSort(), entity.getId());
} catch (Exception e) {
LogHelper.info(e.getMessage());
//throw new ServiceException("更新 专家信息表 数据 失败。");
}
return result >= 0;
}
/**
* 批量新增
*/
public int[] batchInsert(List<ExpertInformation> list) {
String sql = "INSERT INTO T_EXPERT_INFORMATION(EI_ID, EI_NAME, EI_GENDER, EI_MAILBOX, EI_TELEPHONE, EI_USER_NAME, EI_EMPLOYMENT_DATE, EI_DEPARTMENT, EI_GROUP, EI_POSITION, EI_SCOPE_BUSINESS, EI_STATE, EI_SPECIALIZES_FIELDS, EI_EXPERT_INTRODUCTION, EI_ORGANIZATIONAL_CODE, EI_RECORD_TIME, EI_UPDATE_TIME, EI_PHOTO, EI_GROUP_ID, EI_DEPARTMENT_ID, EI_USER_ID, EI_SATISFACTION_SCORE, EI_LEVEL, EI_DISABLED, EI_REGION_CODE, EI_SORT) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
int[] result = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
int j = 0;
ExpertInformation item = list.get(i);
ps.setObject(++j, item.getId());
ps.setString(++j, item.getName());
ps.setString(++j, item.getGender());
ps.setString(++j, item.getMailbox());
ps.setString(++j, item.getTelephone());
ps.setString(++j, item.getUserName());
ps.setTimestamp(++j, item.getEmploymentDate() != null ? new Timestamp(item.getEmploymentDate().getTime()) : null);
ps.setString(++j, item.getDepartment());
ps.setString(++j, item.getGroup());
ps.setString(++j, item.getPosition());
ps.setString(++j, item.getScopeBusiness());
ps.setString(++j, item.getState());
ps.setString(++j, item.getSpecializesFields());
ps.setString(++j, item.getExpertIntroduction());
ps.setString(++j, item.getOrganizationalCode());
ps.setTimestamp(++j, item.getRecordTime() != null ? new Timestamp(item.getRecordTime().getTime()) : null);
ps.setTimestamp(++j, item.getUpdateTime() != null ? new Timestamp(item.getUpdateTime().getTime()) : null);
ps.setString(++j, item.getPhoto());
ps.setObject(++j, item.getGroupId());
ps.setObject(++j, item.getDepartmentId());
ps.setObject(++j, item.getUserId());
ps.setBigDecimal(++j, item.getSatisfactionScore());
ps.setString(++j, item.getLevel());
ps.setString(++j, item.getDisabled());
ps.setString(++j, item.getRegionCode());
ps.setObject(++j, item.getSort());
}
@Override
public int getBatchSize() {
return list.size();
}
});
return result;
}
/**
* 批量更新
*/
public int[] batchUpdate(List<ExpertInformation> list) {
String sql = "UPDATE T_EXPERT_INFORMATION SET EI_NAME = ?, EI_GENDER = ?, EI_MAILBOX = ?, EI_TELEPHONE = ?, EI_USER_NAME = ?, EI_EMPLOYMENT_DATE = ?, EI_DEPARTMENT = ?, EI_GROUP = ?, EI_POSITION = ?, EI_SCOPE_BUSINESS = ?, EI_STATE = ?, EI_SPECIALIZES_FIELDS = ?, EI_EXPERT_INTRODUCTION = ?, EI_ORGANIZATIONAL_CODE = ?, EI_RECORD_TIME = ?, EI_UPDATE_TIME = ?, EI_PHOTO = ?, EI_GROUP_ID = ?, EI_DEPARTMENT_ID = ?, EI_USER_ID = ?, EI_SATISFACTION_SCORE = ?, EI_LEVEL = ?, EI_DISABLED = ?, EI_REGION_CODE = ?, EI_SORT = ? WHERE EI_ID = ? ";
int[] result = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
int j = 0;
ExpertInformation item = list.get(i);
ps.setString(++j, item.getName());
ps.setString(++j, item.getGender());
ps.setString(++j, item.getMailbox());
ps.setString(++j, item.getTelephone());
ps.setString(++j, item.getUserName());
ps.setTimestamp(++j, item.getEmploymentDate() != null ? new Timestamp(item.getEmploymentDate().getTime()) : null);
ps.setString(++j, item.getDepartment());
ps.setString(++j, item.getGroup());
ps.setString(++j, item.getPosition());
ps.setString(++j, item.getScopeBusiness());
ps.setString(++j, item.getState());
ps.setString(++j, item.getSpecializesFields());
ps.setString(++j, item.getExpertIntroduction());
ps.setString(++j, item.getOrganizationalCode());
ps.setTimestamp(++j, item.getRecordTime() != null ? new Timestamp(item.getRecordTime().getTime()) : null);
ps.setTimestamp(++j, item.getUpdateTime() != null ? new Timestamp(item.getUpdateTime().getTime()) : null);
ps.setString(++j, item.getPhoto());
ps.setObject(++j, item.getGroupId());
ps.setObject(++j, item.getDepartmentId());
ps.setObject(++j, item.getUserId());
ps.setBigDecimal(++j, item.getSatisfactionScore());
ps.setString(++j, item.getLevel());
ps.setString(++j, item.getDisabled());
ps.setString(++j, item.getRegionCode());
ps.setObject(++j, item.getSort());
ps.setObject(++j, item.getId());
}
@Override
public int getBatchSize() {
return list.size();
}
});
return result;
}
/**
* 删除
*/
@Override
public boolean delete(String id) {
String sql = "DELETE FROM T_EXPERT_INFORMATION WHERE EI_ID = ?";
int result=0;
try {
result = jdbcTemplate.update(sql, id);
} catch (Exception e) {
LogHelper.info(e.getMessage());
// throw new ServiceException("删除 专家信息表 数据 失败。");
}
return result > 0;
}
/**
* 根据Id查询
*/
@Override
public ExpertInformation selectById(String id) {
String sql = "SELECT * FROM T_EXPERT_INFORMATION WHERE EI_ID = ? ";
try {
return jdbcTemplate.queryForObject(sql, new ExpertInformationRowMapper(), id);
} catch (Exception e) {
LogHelper.info(e.getMessage());
return null;
}
}
/**
* 根据查询条件查询单个对象
*/
@Override
public ExpertInformation selectOneByCriteria(ExpertInformationCriteria criteria) {
StringBuffer buffer = new StringBuffer("SELECT * FROM T_EXPERT_INFORMATION ");
List<Object> list = OracleUtils.combinationSql(buffer, createCriteriaSql(criteria), 1, 1);
try {
return jdbcTemplate.queryForObject(buffer.toString(), list.toArray(), new ExpertInformationRowMapper());
} catch (Exception e) {
LogHelper.info(e.getMessage());
return null;
}
}
/**
* 根据查询条件查询对象集合
*/
@Override
public List<ExpertInformation> selectByCriteria(ExpertInformationCriteria criteria) {
StringBuffer buffer = new StringBuffer("SELECT * FROM T_EXPERT_INFORMATION ");
List<Object> list = OracleUtils.combinationSql(buffer, createCriteriaSql(criteria));
try {
return jdbcTemplate.query(buffer.toString(), list.toArray(), new ExpertInformationRowMapper());
} catch (Exception e) {
LogHelper.info(e.getMessage());
return null;
}
}
/**
* 根据查询条件分页查询对象结合
*/
@Override
public List<ExpertInformation> selectCriteriaByPage(ExpertInformationCriteria criteria, int pageNum, int pageSize) {
StringBuffer buffer = new StringBuffer("SELECT * FROM T_EXPERT_INFORMATION ");
List<Object> list = OracleUtils.combinationSql(buffer, createCriteriaSql(criteria), pageNum, pageSize);
try {
return jdbcTemplate.query(buffer.toString(), list.toArray(), new ExpertInformationRowMapper());
} catch (Exception e) {
LogHelper.info(e.getMessage());
return null;
}
}
/**
* 根据条件查询对象总记录数
*/
@Override
public Integer selectCountByCriteria(ExpertInformationCriteria criteria) {
StringBuffer buffer = new StringBuffer("SELECT COUNT(*) FROM T_EXPERT_INFORMATION ");
List<Object> list = OracleUtils.combinationSql(buffer, createCriteriaSql(criteria));
int count = jdbcTemplate.queryForObject(buffer.toString(), list.toArray(), int.class);
return count;
}
/**
* RowMapper
*/
private class ExpertInformationRowMapper implements RowMapper<ExpertInformation> {
@Override
public ExpertInformation mapRow(ResultSet rs, int i) throws SQLException {
return new ExpertInformation(
rs.getObject("EI_ID") != null ? rs.getInt("EI_ID") : null,
rs.getString("EI_NAME"),
rs.getString("EI_GENDER"),
rs.getString("EI_MAILBOX"),
rs.getString("EI_TELEPHONE"),
rs.getString("EI_USER_NAME"),
rs.getTimestamp("EI_EMPLOYMENT_DATE"),
rs.getString("EI_DEPARTMENT"),
rs.getString("EI_GROUP"),
rs.getString("EI_POSITION"),
rs.getString("EI_SCOPE_BUSINESS"),
rs.getString("EI_STATE"),
rs.getString("EI_SPECIALIZES_FIELDS"),
rs.getString("EI_EXPERT_INTRODUCTION"),
rs.getString("EI_ORGANIZATIONAL_CODE"),
rs.getTimestamp("EI_RECORD_TIME"),
rs.getTimestamp("EI_UPDATE_TIME"),
rs.getString("EI_PHOTO"),
rs.getObject("EI_GROUP_ID") != null ? rs.getInt("EI_GROUP_ID") : null,
rs.getObject("EI_DEPARTMENT_ID") != null ? rs.getInt("EI_DEPARTMENT_ID") : null,
rs.getObject("EI_USER_ID") != null ? rs.getInt("EI_USER_ID") : null,
rs.getBigDecimal("EI_SATISFACTION_SCORE"),
rs.getString("EI_LEVEL"),
rs.getString("EI_DISABLED"),
rs.getString("EI_REGION_CODE"),
rs.getObject("EI_SORT") != null ? rs.getInt("EI_SORT") : null
);
}
}
/**
* 创建查询条件
*/
private Map<String, Object> createCriteriaSql(ExpertInformationCriteria criteria) {
Map<String, Object> andMap = new LinkedHashMap<>();
if (criteria == null)
return andMap;
//if (criteria.byId())
// andMap.put(criteria.getId() == null ? " T_Id IS NULL " : " T_Id = ? ", criteria.getId());
//专家姓名
if (criteria.byName())
andMap.put(criteria.getName() == null ? " EI_NAME IS NULL " : " EI_NAME = ? ", criteria.getName());
//专家姓名(LIKE)
if (criteria.byNameContain())
andMap.put(" EI_NAME LIKE ? ", "%" + criteria.getNameContain() + "%");
//专家性别
if (criteria.byGender())
andMap.put(criteria.getGender() == null ? " EI_GENDER IS NULL " : " EI_GENDER = ? ", criteria.getGender());
//专家性别(LIKE)
if (criteria.byGenderContain())
andMap.put(" EI_GENDER LIKE ? ", "%" + criteria.getGenderContain() + "%");
//专家邮箱
if (criteria.byMailbox())
andMap.put(criteria.getMailbox() == null ? " EI_MAILBOX IS NULL " : " EI_MAILBOX = ? ", criteria.getMailbox());
//专家邮箱(LIKE)
if (criteria.byMailboxContain())
andMap.put(" EI_MAILBOX LIKE ? ", "%" + criteria.getMailboxContain() + "%");
//专家电话
if (criteria.byTelephone())
andMap.put(criteria.getTelephone() == null ? " EI_TELEPHONE IS NULL " : " EI_TELEPHONE = ? ", criteria.getTelephone());
//专家电话(LIKE)
if (criteria.byTelephoneContain())
andMap.put(" EI_TELEPHONE LIKE ? ", "%" + criteria.getTelephoneContain() + "%");
//专家OA用户名
if (criteria.byUserName())
andMap.put(criteria.getUserName() == null ? " EI_USER_NAME IS NULL " : " EI_USER_NAME = ? ", criteria.getUserName());
//专家OA用户名(LIKE)
if (criteria.byUserNameContain())
andMap.put(" EI_USER_NAME LIKE ? ", "%" + criteria.getUserNameContain() + "%");
//入职时间
if (criteria.byEmploymentDate())
andMap.put(criteria.getEmploymentDate() == null ? " EI_EMPLOYMENT_DATE IS NULL " : " EI_EMPLOYMENT_DATE = ? ", criteria.getEmploymentDate());
//专家部门
if (criteria.byDepartment())
andMap.put(criteria.getDepartment() == null ? " EI_DEPARTMENT IS NULL " : " EI_DEPARTMENT = ? ", criteria.getDepartment());
//专家部门(LIKE)
if (criteria.byDepartmentContain())
andMap.put(" EI_DEPARTMENT LIKE ? ", "%" + criteria.getDepartmentContain() + "%");
//专家科室
if (criteria.byGroup())
andMap.put(criteria.getGroup() == null ? " EI_GROUP IS NULL " : " EI_GROUP = ? ", criteria.getGroup());
//专家科室(LIKE)
if (criteria.byGroupContain())
andMap.put(" EI_GROUP LIKE ? ", "%" + criteria.getGroupContain() + "%");
//职位
if (criteria.byPosition())
andMap.put(criteria.getPosition() == null ? " EI_POSITION IS NULL " : " EI_POSITION = ? ", criteria.getPosition());
//职位(LIKE)
if (criteria.byPositionContain())
andMap.put(" EI_POSITION LIKE ? ", "%" + criteria.getPositionContain() + "%");
//业务范围
if (criteria.byScopeBusiness())
andMap.put(criteria.getScopeBusiness() == null ? " EI_SCOPE_BUSINESS IS NULL " : " EI_SCOPE_BUSINESS = ? ", criteria.getScopeBusiness());
//业务范围(LIKE)
if (criteria.byScopeBusinessContain())
andMap.put(" EI_SCOPE_BUSINESS LIKE ? ", "%" + criteria.getScopeBusinessContain() + "%");
//调用状态
if (criteria.byState())
andMap.put(criteria.getState() == null ? " EI_STATE IS NULL " : " EI_STATE = ? ", criteria.getState());
//调用状态(LIKE)
if (criteria.byStateContain())
andMap.put(" EI_STATE LIKE ? ", "%" + criteria.getStateContain() + "%");
//擅长领域
if (criteria.bySpecializesFields())
andMap.put(criteria.getSpecializesFields() == null ? " EI_SPECIALIZES_FIELDS IS NULL " : " EI_SPECIALIZES_FIELDS = ? ", criteria.getSpecializesFields());
//擅长领域(LIKE)
if (criteria.bySpecializesFieldsContain())
andMap.put(" EI_SPECIALIZES_FIELDS LIKE ? ", "%" + criteria.getSpecializesFieldsContain() + "%");
//专家简介
if (criteria.byExpertIntroduction())
andMap.put(criteria.getExpertIntroduction() == null ? " EI_EXPERT_INTRODUCTION IS NULL " : " EI_EXPERT_INTRODUCTION = ? ", criteria.getExpertIntroduction());
//专家简介(LIKE)
if (criteria.byExpertIntroductionContain())
andMap.put(" EI_EXPERT_INTRODUCTION LIKE ? ", "%" + criteria.getExpertIntroductionContain() + "%");
//所属OA组织编码
if (criteria.byOrganizationalCode())
andMap.put(criteria.getOrganizationalCode() == null ? " EI_ORGANIZATIONAL_CODE IS NULL " : " EI_ORGANIZATIONAL_CODE = ? ", criteria.getOrganizationalCode());
//所属OA组织编码(LIKE)
if (criteria.byOrganizationalCodeContain())
andMap.put(" EI_ORGANIZATIONAL_CODE LIKE ? ", "%" + criteria.getOrganizationalCodeContain() + "%");
//专家职称
if (criteria.byLevel())
andMap.put(criteria.getLevel() == null ? " EI_LEVEL IS NULL " : " EI_LEVEL = ? ", criteria.getLevel());
//专家职称(LIKE)
if (criteria.byLevelContain())
andMap.put(" EI_LEVEL LIKE ? ", "%" + criteria.getLevelContain() + "%");
//所属地市
if (criteria.byRegionCode())
andMap.put(criteria.getRegionCode() == null ? " EI_REGION_CODE IS NULL " : " EI_REGION_CODE = ? ", criteria.getRegionCode());
//所属地市(LIKE)
if (criteria.byRegionCodeContain())
andMap.put(" EI_REGION_CODE LIKE ? ", "%" + criteria.getRegionCodeContain() + "%");
return andMap;
}
}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>智能问答</title>
<link rel="stylesheet" href="style/ai-chat.css">
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" class="main-container" :class="{'dark-theme': isDarkTheme}" v-cloak>
<!-- 左侧历史对话列表 -->
<div class="sidebar">
<div class="sidebar-header">
<h1>智能问答</h1>
<!-- 添加主题切换按钮 -->
<div id="theme-toggle" class="theme-toggle" title="切换主题" @click="toggleTheme">
<svg id="light-icon" viewBox="0 0 24 24" :style="{display: isDarkTheme ? 'block' : 'none'}">
<path d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41L5.99 4.58zm12.37 12.37c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0 .39-.39.39-1.03 0-1.41l-1.06-1.06zm1.06-10.96c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06zM7.05 18.36c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06z"></path>
</svg>
<svg id="dark-icon" viewBox="0 0 24 24" :style="{display: isDarkTheme ? 'none' : 'block'}">
<path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"></path>
</svg>
</div>
<button id="new-chat-btn" class="new-chat-btn" @click="newChat">
<span class="plus-icon">+</span> 新对话
</button>
</div>
<div class="history-list">
<div v-for="(section, sectionIndex) in historySections" :key="sectionIndex" class="history-section">
<div class="time-label">{{ section.label }}</div>
<div v-for="(item, itemIndex) in section.items"
:key="item.id"
class="history-item"
:class="{ active: item.active }"
:data-id="item.id" @click="loadChatHistory(item)">
<div class="history-title" >{{ item.title }}</div>
<button class="delete-btn" @click.stop="deleteChat(item.id)">
<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 6h18"></path>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"></path>
<path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
</svg>
</button>
</div>
</div>
</div>
</div>
<!-- 右侧聊天区域 -->
<div class="chat-area">
<!-- 消息历史区域 -->
<div id="scrollContainer" class="scrollContainer">
<div class="fist-loading" v-if="fistLoading">
<p>快速查找</p>
<div class="expert-guides-container">
<div class="expert-guides-scroll" :style="scrollStyle">
<div v-for="(item, index) in extendedGuides" :key="index" class="expert-guide-item" @click="questionClick(item)">
<p>{{ item }}</p>
</div>
</div>
</div>
</div>
<div id="chat-messages" class="chat-messages" v-if="!fistLoading">
<div v-for="(message, index) in messages"
:key="index"
class="message"
:class="message.role + '-message'">
<div class="avatar">{{ message.role === 'user' ? '我' : 'AI' }}</div>
<div class="content">
<p v-html="message.content"></p><!--v-html可能会有xss攻击,但是数据来源于大模型,是否需要清洗数据然后再显示?-->
<div v-if="message.typing" class="typing-indicator"></div>
</div>
</div>
<div class="question">
<div class="question_left"></div>
<div v-if="questions.length" class="question_right">
<p>您可以继续问我: </p>
<div class="questionContent" v-for="(q, i) in questions" :key="i" @click="questionClick(q)">
<p>{{ q }}</p>
</div>
</div>
</div>
</div>
</div>
<!-- 输入区域 -->
<div class="input-area">
<textarea id="user-input" v-model="userInput" placeholder="请输入您的问题 shift+enter换行" rows="3"></textarea>
<div class="input-area-content">
<div style="display: flex;align-items: center;justify-content: flex-start;">
<div class="custom-select" v-click-outside="closeExpertDropdown" style="display: none">
<div class="selected-option" @click.stop="selectExpert('内部专家')"
:class="{ 'active': selectedExpert === '内部专家' }">
<span>内部专家</span>
<!-- <svg class="dropdown-icon" :class="{ 'rotated': showExpertDropdown }" viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none">-->
<!-- <polyline points="6 9 12 15 18 9"></polyline>-->
<!-- </svg>-->
</div>
<!-- <div class="dropdown-menu" v-show="showExpertDropdown">-->
<!-- <div class="dropdown-item" @click.stop="selectExpert('内部专家')" :class="{ 'active': selectedExpert === '内部专家' }">-->
<!-- <span>内部专家</span>-->
<!-- </div>-->
<!--&lt;!&ndash; <div class="dropdown-item" @click.stop="selectExpert('外部专家')" :class="{ 'active': selectedExpert === '外部专家' }">&ndash;&gt;-->
<!--&lt;!&ndash; <span>外部专家</span>&ndash;&gt;-->
<!--&lt;!&ndash; </div>&ndash;&gt;-->
<!-- </div>-->
</div>
<div class="custom-select" v-click-outside="closeExpertDropdown" style="display: none">
<div class="selected-option" @click.stop="selectExpert('外部专家')"
:class="{ 'active': selectedExpert === '外部专家' }">
<span> 外部专家 </span>
</div>
</div>
<div class="custom-select" v-click-outside="closeOrgDropdown" v-show="showOrgSelection" >
<div class="selected-option" @click.stop="toggleOrgDropdown" style="background-color: rgba(230, 220, 250, 0.5);color: #6633ff;">
<span>{{ selectedOrg }}</span>
<svg class="dropdown-icon" :class="{ 'rotated': showOrgDropdown }" viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</div>
<div class="dropdown-menu" v-show="showOrgDropdown">
<div class="dropdown-item" @click.stop="selectOrg('全部组织')" :class="{ 'active': selectedOrg === '全部组织' }">
<span>全部</span>
</div>
<div class="dropdown-item" @click.stop="selectOrg('组织内')" :class="{ 'active': selectedOrg === '组织内' }">
<span>组织内</span>
</div>
<div class="dropdown-item" @click.stop="selectOrg('组织外')" :class="{ 'active': selectedOrg === '组织外' }">
<span>组织外</span>
</div>
</div>
</div>
</div>
<div>
<button id="send-btn" class="send-btn" @click="sendMessage" v-show="!isResponding">
<svg class="send-icon" viewBox="0 0 24 24" width="18" height="18" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
<line x1="12" y1="19" x2="12" y2="5"></line>
<polyline points="5 12 12 5 19 12"></polyline>
</svg>
</button>
<button id="stop-btn" class="stop-btn" @click="stopResponse" v-show="isResponding">
<svg viewBox="0 0 24 24" width="18" height="18" stroke="currentColor" stroke-width="2" fill="none"
stroke-linecap="round" stroke-linejoin="round" class="stop-icon">
<rect x="6" y="6" width="12" height="12" rx="2" ry="2"></rect>
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="../../libs/require.js/require.min.js"></script>
<script type="text/javascript" src="../../scripts/require-config.js"></script>
<!-- 引入Vue相关JS -->
<script type="text/javascript" src="js/ai-chat-vue.js"></script>
</body>
</html>
\ No newline at end of file
/**
* AI聊天页面Vue应用
*/
require(['jquery', 'vue', 'utils','marked','markdown', 'global'], function ($, Vue, utils, marked,markdown) {
// 添加点击外部关闭指令
Vue.directive('click-outside', {
bind: function (el, binding, vnode) {
el.clickOutsideEvent = function (event) {
if (!(el == event.target || el.contains(event.target))) {
vnode.context[binding.expression](event);
}
};
document.body.addEventListener('click', el.clickOutsideEvent);
},
unbind: function (el) {
document.body.removeEventListener('click', el.clickOutsideEvent);
}
});
const app = new Vue({
el: '#app',
data: {
// 用户相关数据
currentLoginUser: {},
fistLoading: true,
// 聊天相关数据
chatHistory: [],
sessionId: "",
currentEventSource: null,
// UI相关数据
theme: localStorage.getItem('ai-chat-theme') || 'light',
isResponding: false,
userInput: "",
// 专家选择相关数据
selectedExpert: '内部专家',
showExpertDropdown: false,
// 组织内网选择相关数据
selectedOrg:'全部组织',
showOrgDropdown: false,
questions:[],
guides:[
'引上光缆在维护下架里面是否能算距离。',
'保险是否有雇主险要求。',
'线路迁改列投资是否有统一规范的原则?',
'短距离的管道的维护和整治费用,如何计取?'
],
scrollOffset: 0,
scrollInterval: null,
transitioning: true,
// 历史对话分类
historySections: [
],
// 消息列表
messages: [
// { role: 'ai', content: '您好!我是您的专家推荐助手,有什么可以帮助您的吗?' }
],
showOrgSelection: false
},
computed: {
isDarkTheme() {
return this.theme === 'dark';
},
extendedGuides() {
// 克隆前3项到末尾
return [...this.guides, ...this.guides.slice(0, 3)];
},
scrollStyle() {
return {
transform: `translateY(-${this.scrollOffset}px)`,
transition: this.transitioning ? 'transform 0.5s ease' : 'none',
};
}
},
created: function () {
this.currentUser();
this.getSessionId();
this.getConversationHistory();
},
mounted: function () {
// 监听输入框的键盘事件
this.$nextTick(() => {
const textarea = document.getElementById('user-input');
if (textarea) {
textarea.addEventListener('keypress', this.handleKeyPress);
}
// 启动自动滚动
this.startAutoScroll();
// 添加鼠标悬停事件监听
this.$watch('fistLoading', (newVal) => {
if (newVal) {
// 等待DOM更新
this.$nextTick(() => {
const guideItems = document.querySelectorAll('.expert-guide-item');
guideItems.forEach(item => {
item.addEventListener('mouseenter', this.pauseAutoScroll);
item.addEventListener('mouseleave', this.resumeAutoScroll);
});
});
}
}, { immediate: true });
});
},
beforeDestroy: function() {
// 清除自动滚动定时器
this.stopAutoScroll();
// 移除事件监听器
const guideItems = document.querySelectorAll('.expert-guide-item');
guideItems.forEach(item => {
item.removeEventListener('mouseenter', this.pauseAutoScroll);
item.removeEventListener('mouseleave', this.resumeAutoScroll);
});
},
methods: {
// 自动滚动相关方法
startAutoScroll: function() {
const itemHeight = 80;
const visibleItems = 3;
const totalItems = this.guides.length;
this.scrollInterval = setInterval(() => {
this.transitioning = true;
this.scrollOffset += itemHeight;
// 判断是否滚动到克隆项部分(即第 totalItems 行之后)
if (this.scrollOffset >= itemHeight * (totalItems)) {
// 短暂延迟后瞬间回到原始位置
setTimeout(() => {
this.transitioning = false;
this.scrollOffset = 0;
}, 500); // 等待动画结束
}
}, 2000);
},
stopAutoScroll: function() {
if (this.scrollInterval) {
clearInterval(this.scrollInterval);
this.scrollInterval = null;
}
},
pauseAutoScroll: function() {
this.stopAutoScroll();
},
resumeAutoScroll: function() {
if (!this.scrollInterval && this.fistLoading) {
this.startAutoScroll();
}
},
// 用户相关方法
currentUser: function () {
const that = this;
$.ajax({
url: "../../auth/current-user",
type: "get",
dataType: "json",
contentType: "application/json;charset=UTF-8",
async: false,
success: function (data) {
that.currentLoginUser = data.data;
}
});
},
// 主题相关方法
toggleTheme() {
this.theme = this.theme === 'light' ? 'dark' : 'light';
localStorage.setItem('ai-chat-theme', this.theme);
document.body.classList.toggle('dark-theme', this.theme === 'dark');
},
// 会话相关方法
async getSessionId() {
try {
//const response = await fetch('../../api/langchain/getSessionId');
const response = await fetch('../../api/langchain/getDialogId');
const dataJson = await response.json();
this.sessionId = dataJson.data;
} catch (error) {
console.error('会话ID获取失败', error);
}
},
async getConversationHistory() {
try {
//const response = await fetch('../../api/langchain/getSessionId');
// const response = await fetch('../../api/langchain/conversationHistory');
// const dataJson = await response.json();
// this.historySections = dataJson.data;
} catch (error) {
console.error('会话获取失败', error);
}
},
// 聊天相关方法
handleKeyPress(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
this.sendMessage();
}
},
questionClick:function(message){
this.userInput = message;
this.sendMessage();
},
sendMessage() {
// if (!this.selectedExpert) {
// this.showToast('请先选择专家类型(内部/外部专家)');
// return;
// }
const message = this.userInput.trim();
if (!message) return;
if (this.fistLoading) {
this.fistLoading = false;
// 停止自动滚动
this.stopAutoScroll();
}
this.questions= [];
this.stopResponse();
// 添加用户消息
this.addMessage('user', message);
this.userInput = '';
// 显示终止按钮,隐藏发送按钮
this.isResponding = true;
// 调用API获取响应
this.connectSSE(message);
},
stopResponse() {
if (this.currentEventSource) {
this.currentEventSource.close();
this.currentEventSource = null;
// 切换回发送按钮
this.isResponding = false;
// 在最后一条AI消息后添加"(已终止)"标记
const aiMessages = this.messages.filter(msg => msg.role === 'ai');
if (aiMessages.length > 0) {
const lastAIMessage = aiMessages[aiMessages.length - 1];
lastAIMessage.typing = false;
if (!lastAIMessage.content.includes('(已手动结束回答)')) {
lastAIMessage.content += ' (已手动结束回答)';
}
}
}
},
addMessage(role, content) {
this.messages.push({ role, content });
this.chatHistory.push({ role, content });
// 自动滚动到底部
this.$nextTick(() => {
const messagesDiv = document.getElementById('scrollContainer');
if (messagesDiv) {
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
});
},
connectSSE(chatMessage) {
// 在消息列表中添加一个带"正在输入"指示器的AI消息
const aiMessageIndex = this.messages.length;
this.messages.push({ role: 'ai', content: '', typing: true });
// 自动滚动到底部
this.$nextTick(() => {
const messagesDiv = document.getElementById('scrollContainer');
if (messagesDiv) {
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
});
// 连接SSE
this.currentEventSource = new EventSource('../../api/langchain/sseTest?chatMessage='+chatMessage+"&dialogId="+this.sessionId
+"&selectedExpert="+this.selectedExpert+"&selectedOrg="+this.selectedOrg);//sseIntelligent
let responseText = '';
var md = new markdown({
html: true, // 允许解析 HTML 标签
linkify: true, // 自动识别链接
typographer: true, // 启用排版优化
breaks: true, // 将单个换行符视为换行
});
this.currentEventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
const content = data.dataToSend[1].data;
if (content !== "stop") {
const match = content.match(/SUGGEST\[(.*?)\]SUGGEST/);
if (match) {
const suggestionsJson = `[${match[1]}]`;
const suggestions = JSON.parse(suggestionsJson);
if (Array.isArray(suggestions)) {
this.questions = suggestions;
}
}else{//提示词不展示
responseText += content;
}
this.messages[aiMessageIndex].content = marked.parse(responseText);//md.render(responseText);
} else {
// 移除输入指示器
this.messages[aiMessageIndex].typing = false;
this.chatHistory.push({ role: 'ai', content: responseText });
// 切换回发送按钮
this.isResponding = false;
// 关闭事件源
this.currentEventSource.close();
this.currentEventSource = null;
}
// 自动滚动到底部
this.$nextTick(() => {
const messagesDiv = document.getElementById('scrollContainer');
if (messagesDiv) {
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
});
};
this.currentEventSource.onerror = () => {
console.error('SSE连接异常');
// 移除输入指示器,显示错误信息
this.messages[aiMessageIndex].typing = false;
this.messages[aiMessageIndex].content = responseText || '抱歉,连接出现问题,请稍后再试。';
// 切换回发送按钮
this.isResponding = false;
// 关闭事件源
this.currentEventSource.close();
this.currentEventSource = null;
};
},
clearChat() {
// 清空消息,保留欢迎消息
this.messages = [
{ role: 'ai', content: '您好!我是您的AI助手,有什么可以帮助您的吗?' }
];
// 清空历史记录
this.chatHistory = [];
},
newChat() {
// 终止当前响应(如果有)
if (this.currentEventSource) {
this.currentEventSource.close();
this.currentEventSource = null;
this.isResponding = false;
}
this.questions = [];
// 获取新的会话ID并清空对话
this.getSessionId().then(() => {
this.clearChat();
// 这里可以添加将新对话添加到历史列表的逻辑
// this.addChatToHistory('新对话', new Date());
});
this.fistLoading=true;
// this.selectedExpert = '内部专家';
// this.showOrgSelection = this.selectedExpert === '内部专家';
// this.selectedOrg = '全部组织';
// 重置滚动位置并重新启动自动滚动
this.scrollOffset = 0;
this.startAutoScroll();
},
loadChatHistory(chat) {
var that=this
var chatId=chat.id;
// 关闭所有菜单
this.historySections.forEach(section => {
section.items.forEach(item => {
item.showMenu = false;
});
});
//this.addMessage('user', "123456");
// 设置活跃状态
this.historySections.forEach(section => {
section.items.forEach(item => {
item.active = (item.id === chatId);
});
});
//
this.sessionId = chatId;
// this.clearChat();
this.questions= [];
this.messages=[];
// 如果正在显示初始推荐,停止自动滚动
if (this.fistLoading) {
this.stopAutoScroll();
}
this.fistLoading = false;
// this.selectedExpert = '内部专家';
// this.showOrgSelection = this.selectedExpert === '内部专家';
// this.selectedOrg = '全部组织';
//这里添加加载对应对话历史的逻辑
//实际实现时可以调用API获取历史记录
$.ajax({
url: "../../api/langchain/conversationMessages?sessionId="+chatId,
type: "get",
dataType: "json",
contentType: "application/json;charset=UTF-8",
async: false,
success: function (data) {
var conversationMessages = data.data;
if (conversationMessages && conversationMessages.length > 0){
that.fistLoading = false;
conversationMessages.forEach(section => {
that.addMessage(section.role, section.content);
});
}
}
});
},
// 删除对话
deleteChat(chatId) {
// 关闭菜单
this.historySections.forEach(section => {
const item = section.items.find(item => item.id === chatId);
if (item) {
item.showMenu = false;
}
});
// 从历史记录中删除
this.historySections.forEach(section => {
section.items = section.items.filter(item => item.id !== chatId);
});
// 如果删除的是当前活跃的对话,则清空聊天记录
const isActiveChat = this.historySections.some(section =>
section.items.some(item => item.id === chatId && item.active)
);
if (isActiveChat) {
this.clearChat();
}
$.ajax({
url: "../../api/langchain/deleteChat",
type: "post",
dataType: "json",
contentType: "application/json;charset=UTF-8",
data: chatId,
async: false,
success: function (data) {
}
});
},
// 专家选择相关方法
toggleExpertDropdown() {
this.showExpertDropdown = !this.showExpertDropdown;
},
closeExpertDropdown() {
this.showExpertDropdown = false;
},
selectExpert(expert) {
// this.selectedExpert = expert;
if (this.selectedExpert === expert) {
this.selectedExpert = '';
} else {
this.selectedExpert = expert;
}
this.showOrgSelection = expert === '内部专家';
this.selectedOrg = '全部组织';
// 立即关闭下拉框
this.showExpertDropdown = false;
},
//组织内外选择相关方法
toggleOrgDropdown() {
this.showOrgDropdown = !this.showOrgDropdown;
},
closeOrgDropdown() {
this.showOrgDropdown = false;
},
selectOrg(org) {
this.selectedOrg = org;
// 立即关闭下拉框
this.showOrgDropdown = false;
},
showToast(message) {
// 使用你项目中已有的提示组件,或创建一个简单的提示
const toast = document.createElement('div');
toast.className = 'expert-toast';
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => {
toast.remove();
}, 3000);
}
}
});
});
\ No newline at end of file
/* 全局样式重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
}
:root {
/* 浅色主题变量 */
--bg-color: #f5f7fa;
--sidebar-bg: #fff;
--sidebar-border: #e8e8e8;
--primary-color: #1890ff;
--primary-hover: #40a9ff;
--text-color: #333;
--text-secondary: #8c8c8c;
--message-bg: #fff;
--message-user-bg: #f5f7fa;
--message-border: #e8e8e8;
--input-area-bg: #fff;
--hover-bg: #f0f2f5;
--active-bg: #e6f7ff;
--avatar-bg: #1890ff;
--avatar-user-bg: #722ed1;
--scrollbar-track: #f1f1f1;
--scrollbar-thumb: #c1c1c1;
--scrollbar-hover: #a8a8a8;
--box-shadow: rgba(0, 0, 0, 0.08);
--question-text: #676c90;
}
.dark-theme {
/* 深色主题变量 */
--bg-color: #1f1f1f;
--sidebar-bg: #2d2d2d;
--sidebar-border: #3a3a3a;
--primary-color: #177ddc;
--primary-hover: #3c9ae8;
--text-color: #e0e0e0;
--text-secondary: #aaaaaa;
--message-bg: #2d2d2d;
--message-user-bg: #363636;
--message-border: #404040;
--input-area-bg: #363636;
--hover-bg: #404040;
--active-bg: #1a365d;
--avatar-bg: #177ddc;
--avatar-user-bg: #722ed1;
--scrollbar-track: #2d2d2d;
--scrollbar-thumb: #555555;
--scrollbar-hover: #777777;
--box-shadow: rgba(0, 0, 0, 0.2);
--question-text: #dbdef8;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
line-height: 1.6;
height: 100vh;
overflow: hidden;
}
/* 主容器样式 */
.main-container {
display: flex;
height: 100vh;
width: 100%;
overflow: hidden;
}
/* 左侧边栏样式 */
.sidebar {
width: 280px;
background-color: var(--sidebar-bg);
border-right: 1px solid var(--sidebar-border);
display: flex;
flex-direction: column;
overflow: hidden;
}
.sidebar-header {
padding: 16px;
border-bottom: 1px solid var(--sidebar-border);
position: relative;
}
.sidebar-header h1 {
font-size: 1.2rem;
font-weight: 600;
color: var(--primary-color);
margin-bottom: 16px;
}
.new-chat-btn {
width: 100%;
padding: 10px;
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-weight: 500;
transition: background-color 0.3s;
}
.new-chat-btn:hover {
background-color: var(--primary-hover);
}
.plus-icon {
margin-right: 8px;
font-size: 16px;
font-weight: bold;
}
/* 主题切换按钮 */
.theme-toggle {
position: absolute;
right: 16px;
top: 16px;
cursor: pointer;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: background-color 0.2s;
}
.theme-toggle:hover {
background-color: var(--hover-bg);
}
.theme-toggle svg {
width: 18px;
height: 18px;
fill: var(--text-secondary);
}
/* 历史对话列表样式 */
.history-list {
flex: 1;
overflow-y: auto;
padding: 10px;
scrollbar-width: thin;
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
max-height: calc(100vh - 90px);
}
.history-section {
margin-bottom: 16px;
}
.time-label {
font-size: 0.8rem;
color: var(--text-secondary);
padding: 5px 10px;
margin-bottom: 5px;
}
.history-item {
padding: 10px;
border-radius: 6px;
cursor: pointer;
transition: background-color 0.2s;
display: flex;
justify-content: space-between;
align-items: center;
}
.history-item:hover {
background-color: var(--hover-bg);
}
.history-item.active {
background-color: var(--active-bg);
border-left: 3px solid var(--primary-color);
}
.history-title {
font-size: 0.9rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 180px;
color: var(--text-color);
}
.delete-btn {
background: none;
border: none;
cursor: pointer;
padding: 5px;
opacity: 0.5;
transition: opacity 0.2s;
color: red;
}
.delete-btn:hover {
opacity: 1;
}
.delete-btn svg {
display: block;
}
.history-time {
font-size: 0.75rem;
color: var(--text-secondary);
}
/* 右侧聊天区域 */
.fist-loading{
width: 900px;
margin: 8% auto 10px;
}
.fist-loading>p{
color: var(--primary-color);
font-weight: bold;
font-size: 20px;
width: 500px;
margin:0 auto;
}
.expert-guides-container {
width: 500px;
height: 240px; /* 高度为每条推荐项高度的3倍 */
margin: 0 auto;
overflow: hidden;
position: relative;
}
.expert-guides-scroll {
width: 100%;
position: absolute;
transition: transform 0.5s ease;
}
.expert-guide-item {
width: 500px;
height: 70px;
line-height: 70px;
border-radius: 16px;
padding: 0 16px;
background-color: var(--sidebar-bg);
border: 1px solid var(--box-shadow);
/*box-shadow: 0 1px 8px var(--box-shadow);*/
/*box-sizing: border-box;*/
align-items: center;
margin: 10px auto 0;
font-size: 16px;
cursor: pointer;
color: var(--question-text);
}
.expert-guide-item:hover{
background-color: #e5eaf6;
color: #475ada;
}
.chat-area {
flex: 1;
display: flex;
flex-direction: column;
position: relative;
height: 100vh;
overflow-y: auto; /* 使整个聊天区域可滚动,滚动条显示在最右边 */
overflow-x: hidden;
background-color: var(--bg-color);
}
.scrollContainer{
width: 100%;
overflow-y: auto; /* 让内容继续流动 */
height: calc(100vh - 150px); /* 确保占据足够高度 */
overflow-x: hidden;
padding-top: 20px;
}
/* 聊天消息区域 */
.chat-messages {
width: 900px;
margin: 0 auto 10px;
}
/* 输入区域 */
.input-area {
width: 900px;
max-width: 95%;
margin: 0 auto 30px auto;
padding: 8px 10px;
display: flex;
background-color: var(--input-area-bg);
border-radius: 15px;
box-shadow: 0 1px 8px var(--box-shadow);
position: sticky;
bottom: 30px;
flex-shrink: 0; /* 防止被压缩 */
flex-direction: column;
justify-content: flex-start;
}
.input-area-content{
display: flex;
justify-content: space-between;
padding-left: 10px;
}
/* 单条消息样式 */
.message {
display: flex;
margin-bottom: 20px;
align-items: flex-start;
}
/* 用户消息 */
.question{
display: flex;
justify-content: flex-start;
}
.question_left{
width: 40px;
margin: 0 10px;
}
.question_right{
padding:0 6px;
}
.question_right>p{
font-size: 14px;
color: var(--question-text);
}
.questionContent{
font-size: 13px;
color: var(--question-text);
background-color: var(--input-area-bg);
border-radius: 6px;
box-shadow: 0 16px 20px 0 rgba(174, 167, 223, .06);
line-height: 20px;
max-width: 800px;
overflow: hidden;
padding: 8px 16px;
text-overflow: ellipsis;
white-space: nowrap;
width: -moz-fit-content;
width: fit-content;
margin: 10px 0;
cursor: pointer;
}
.questionContent:hover{
background-color: #e5eaf6;
color: #475ada;
}
.user-message {
flex-direction: row-reverse;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: var(--avatar-bg);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
flex-shrink: 0;
margin: 0 10px;
}
.user-message .avatar {
background-color: var(--avatar-user-bg);
}
.message .content {
max-width: 87%;
padding: 12px 16px;
border-radius: 12px;
box-shadow: 0 1px 2px var(--box-shadow);
}
.ai-message .content {
background-color: var(--message-bg);
border-top-left-radius: 0;
}
.user-message .content {
background-color: var(--message-user-bg);
border: 1px solid var(--message-border);
color: var(--text-color);
border-top-right-radius: 0;
}
.content p {
white-space: pre-wrap;
word-break: break-word;
color: var(--text-color);
}
#user-input {
flex: 1;
padding: 8px 10px;
border: none;
border-radius: 0;
resize: none;
outline: none;
height: 100px;
max-height: 150px;
background-color: transparent;
transition: all 0.3s;
margin-right: 8px;
line-height: 20px;
box-shadow: none;
overflow-y: auto;
color: var(--text-color);
}
#user-input:focus {
box-shadow: none;
}
#user-input::placeholder {
color: var(--text-secondary);
}
.send-btn, .stop-btn {
width: 36px;
height: 36px;
min-height: 36px;
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
padding: 0;
}
.send-btn:hover, .stop-btn:hover {
background-color: var(--primary-hover);
}
.stop-btn {
background-color: #ff4d4f;
}
.stop-btn:hover {
background-color: #ff7875;
}
.stop-icon, .send-icon {
width: 18px;
height: 18px;
stroke: white;
}
/* 打字指示器样式 */
.typing-indicator {
display: inline-block;
position: relative;
min-height: 20px;
font-size: 25px;
font-weight: bold;
}
.typing-indicator::after {
content: "···";
animation: typing 1.5s infinite;
}
@keyframes typing {
0%, 100% { content: "."; }
33% { content: ".."; }
66% { content: "..."; }
}
@media(max-width: 1190px){
.chat-messages{
width: 800px;
}
}
@media(max-width: 1090px){
.chat-messages{
width: 700px;
}
}
@media(max-width: 990px){
.chat-messages{
width: 600px;
}
}
/* 响应式设计 */
@media (max-width: 768px) {
.sidebar {
position: absolute;
left: -280px;
height: 100vh;
z-index: 10;
transition: left 0.3s;
}
.sidebar.visible {
left: 0;
}
.chat-messages,
.input-area {
width: 100%;
max-width: 100%;
}
.message .content {
max-width: 85%;
}
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: var(--scrollbar-track);
}
::-webkit-scrollbar-thumb {
background: var(--scrollbar-thumb);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--scrollbar-hover);
}
/* 动画效果 */
.message {
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 自定义选择器样式 */
.custom-select {
position: relative;
width: auto;
min-width: 70px;
cursor: pointer;
user-select: none;
margin-right: 10px;
margin-left: 0;
}
.selected-option {
display: flex;
align-items: center;
justify-content: center;
padding: 2px 5px;
border-radius: 8px;
/*background-color: rgba(230, 220, 250, 0.5);*/
background-color: #f5f6fa;
/*color: #6633ff;*/
color: #4c4c4c;
font-size: 14px;
transition: all 0.2s;
}
.selected-option span {
margin-right: 2px;
text-align: center;
display: inline-block;
width: 100%;
}
.dark-theme .selected-option {
background-color: rgba(130, 100, 200, 0.2);
color: #9980ff;
}
.selected-option:hover {
background-color: rgba(210, 200, 250, 0.7);
}
.dark-theme .selected-option:hover {
background-color: rgba(150, 120, 220, 0.3);
}
.dropdown-icon {
transition: transform 0.2s;
margin-left: 2px;
flex-shrink: 0;
stroke: #6633ff;
}
.dark-theme .dropdown-icon {
stroke: #9980ff;
}
.dropdown-icon.rotated {
transform: rotate(180deg);
}
.dropdown-menu {
position: absolute;
bottom: 100%;
left: 0;
width: 100%;
margin-bottom: 5px;
background-color: var(--message-bg);
border-radius: 8px;
box-shadow: 0 2px 10px var(--box-shadow);
z-index: 100;
overflow: hidden;
}
.dropdown-item {
padding: 6px 8px;
transition: background-color 0.2s;
font-size: 13px;
color: var(--text-color);
text-align: center;
}
.dropdown-item:hover {
background-color: var(--hover-bg);
}
.dropdown-item.active {
background-color: var(--active-bg);
font-weight: 500;
}
/* 输入区域内容样式 */
.input-area-content {
display: flex;
align-items: center;
}
.selected-option.active {
/* 选中样式 */
/*background-color: #f5f6fa;*/
background-color: rgba(230, 220, 250, 0.5);
/*color: #272933;*/
color: #6633ff;
}
.expert-toast {
position: fixed;
left: 50%;
top: 65%; /* 垂直居中 */
background-color: rgba(230, 220, 250, 0.5);
color: #6633ff;
padding: 10px 20px;
border-radius: 4px;
z-index: 1000;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment