Browse Source

在线问答功能

master
maotiantian 2 weeks ago
parent
commit
4277c6658b
  1. 104
      chenhai-admin/src/main/java/com/chenhai/web/controller/muhu/MuhuExperienceShareController.java
  2. 128
      chenhai-admin/src/main/java/com/chenhai/web/controller/system/SysAnswerAgainController.java
  3. 101
      chenhai-admin/src/main/java/com/chenhai/web/controller/system/SysAnswersController.java
  4. 104
      chenhai-admin/src/main/java/com/chenhai/web/controller/system/SysMuhuUserController.java
  5. 32
      chenhai-admin/src/main/java/com/chenhai/web/controller/system/SysQuestionsController.java
  6. 433
      chenhai-system/src/main/java/com/chenhai/muhu/domain/MuhuExperienceShare.java
  7. 61
      chenhai-system/src/main/java/com/chenhai/muhu/mapper/MuhuExperienceShareMapper.java
  8. 61
      chenhai-system/src/main/java/com/chenhai/muhu/service/IMuhuExperienceShareService.java
  9. 93
      chenhai-system/src/main/java/com/chenhai/muhu/service/impl/MuhuExperienceShareServiceImpl.java
  10. 155
      chenhai-system/src/main/java/com/chenhai/system/domain/SysAnswerAgain.java
  11. 169
      chenhai-system/src/main/java/com/chenhai/system/domain/SysAnswers.java
  12. 222
      chenhai-system/src/main/java/com/chenhai/system/domain/SysMuhuUser.java
  13. 40
      chenhai-system/src/main/java/com/chenhai/system/domain/SysQuestions.java
  14. 61
      chenhai-system/src/main/java/com/chenhai/system/mapper/SysAnswerAgainMapper.java
  15. 51
      chenhai-system/src/main/java/com/chenhai/system/mapper/SysAnswersMapper.java
  16. 61
      chenhai-system/src/main/java/com/chenhai/system/mapper/SysMuhuUserMapper.java
  17. 61
      chenhai-system/src/main/java/com/chenhai/system/service/ISysAnswerAgainService.java
  18. 57
      chenhai-system/src/main/java/com/chenhai/system/service/ISysAnswersService.java
  19. 61
      chenhai-system/src/main/java/com/chenhai/system/service/ISysMuhuUserService.java
  20. 121
      chenhai-system/src/main/java/com/chenhai/system/service/impl/SysAnswerAgainServiceImpl.java
  21. 370
      chenhai-system/src/main/java/com/chenhai/system/service/impl/SysAnswersServiceImpl.java
  22. 95
      chenhai-system/src/main/java/com/chenhai/system/service/impl/SysMuhuUserServiceImpl.java
  23. 36
      chenhai-system/src/main/java/com/chenhai/system/service/impl/SysQuestionsServiceImpl.java
  24. 181
      chenhai-system/src/main/resources/mapper/muhu/MuhuExperienceShareMapper.xml
  25. 94
      chenhai-system/src/main/resources/mapper/system/SysAnswerAgainMapper.xml
  26. 96
      chenhai-system/src/main/resources/mapper/system/SysAnswersMapper.xml
  27. 117
      chenhai-system/src/main/resources/mapper/system/SysMuhuUserMapper.xml
  28. 22
      chenhai-system/src/main/resources/mapper/system/SysQuestionsMapper.xml
  29. 44
      chenhai-ui/src/api/muhu/share.js
  30. 44
      chenhai-ui/src/api/system/again.js
  31. 61
      chenhai-ui/src/api/system/answers.js
  32. 44
      chenhai-ui/src/api/system/muhuuser.js
  33. 66
      chenhai-ui/src/router/index.js
  34. 440
      chenhai-ui/src/views/muhu/share/index.vue
  35. 275
      chenhai-ui/src/views/system/again/index.vue
  36. 619
      chenhai-ui/src/views/system/answers/index.vue
  37. 323
      chenhai-ui/src/views/system/muhuuser/index.vue
  38. 260
      chenhai-ui/src/views/system/questions/index.vue

104
chenhai-admin/src/main/java/com/chenhai/web/controller/muhu/MuhuExperienceShareController.java

@ -0,0 +1,104 @@
package com.chenhai.web.controller.muhu;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.chenhai.common.annotation.Log;
import com.chenhai.common.core.controller.BaseController;
import com.chenhai.common.core.domain.AjaxResult;
import com.chenhai.common.enums.BusinessType;
import com.chenhai.muhu.domain.MuhuExperienceShare;
import com.chenhai.muhu.service.IMuhuExperienceShareService;
import com.chenhai.common.utils.poi.ExcelUtil;
import com.chenhai.common.core.page.TableDataInfo;
/**
* 经验分享Controller
*
* @author ruoyi
* @date 2026-01-30
*/
@RestController
@RequestMapping("/muhu/share")
public class MuhuExperienceShareController extends BaseController
{
@Autowired
private IMuhuExperienceShareService muhuExperienceShareService;
/**
* 查询经验分享列表
*/
@PreAuthorize("@ss.hasPermi('muhu:share:list')")
@GetMapping("/list")
public TableDataInfo list(MuhuExperienceShare muhuExperienceShare)
{
startPage();
List<MuhuExperienceShare> list = muhuExperienceShareService.selectMuhuExperienceShareList(muhuExperienceShare);
return getDataTable(list);
}
/**
* 导出经验分享列表
*/
@PreAuthorize("@ss.hasPermi('muhu:share:export')")
@Log(title = "经验分享", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, MuhuExperienceShare muhuExperienceShare)
{
List<MuhuExperienceShare> list = muhuExperienceShareService.selectMuhuExperienceShareList(muhuExperienceShare);
ExcelUtil<MuhuExperienceShare> util = new ExcelUtil<MuhuExperienceShare>(MuhuExperienceShare.class);
util.exportExcel(response, list, "经验分享数据");
}
/**
* 获取经验分享详细信息
*/
@PreAuthorize("@ss.hasPermi('muhu:share:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(muhuExperienceShareService.selectMuhuExperienceShareById(id));
}
/**
* 新增经验分享
*/
@PreAuthorize("@ss.hasPermi('muhu:share:add')")
@Log(title = "经验分享", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody MuhuExperienceShare muhuExperienceShare)
{
return toAjax(muhuExperienceShareService.insertMuhuExperienceShare(muhuExperienceShare));
}
/**
* 修改经验分享
*/
@PreAuthorize("@ss.hasPermi('muhu:share:edit')")
@Log(title = "经验分享", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody MuhuExperienceShare muhuExperienceShare)
{
return toAjax(muhuExperienceShareService.updateMuhuExperienceShare(muhuExperienceShare));
}
/**
* 删除经验分享
*/
@PreAuthorize("@ss.hasPermi('muhu:share:remove')")
@Log(title = "经验分享", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(muhuExperienceShareService.deleteMuhuExperienceShareByIds(ids));
}
}

128
chenhai-admin/src/main/java/com/chenhai/web/controller/system/SysAnswerAgainController.java

@ -0,0 +1,128 @@
package com.chenhai.web.controller.system;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.chenhai.common.annotation.Log;
import com.chenhai.common.core.controller.BaseController;
import com.chenhai.common.core.domain.AjaxResult;
import com.chenhai.common.enums.BusinessType;
import com.chenhai.common.utils.SecurityUtils;
import com.chenhai.system.domain.SysAnswerAgain;
import com.chenhai.system.service.ISysAnswerAgainService;
import com.chenhai.common.utils.poi.ExcelUtil;
import com.chenhai.common.core.page.TableDataInfo;
/**
* 回复Controller
*
* @author ruoyi
* @date 2026-02-02
*/
@RestController
@RequestMapping("/system/again")
public class SysAnswerAgainController extends BaseController
{
@Autowired
private ISysAnswerAgainService sysAnswerAgainService;
/**
* 查询回复列表
*/
@PreAuthorize("@ss.hasPermi('system:again:list') or @ss.hasRole('muhu')")
@GetMapping("/list")
public TableDataInfo list(SysAnswerAgain sysAnswerAgain)
{
startPage();
List<SysAnswerAgain> list = sysAnswerAgainService.selectSysAnswerAgainList(sysAnswerAgain);
return getDataTable(list);
}
/**
* 导出回复列表
*/
@PreAuthorize("@ss.hasPermi('system:again:export') or @ss.hasRole('muhu')")
@Log(title = "回复", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, SysAnswerAgain sysAnswerAgain)
{
List<SysAnswerAgain> list = sysAnswerAgainService.selectSysAnswerAgainList(sysAnswerAgain);
ExcelUtil<SysAnswerAgain> util = new ExcelUtil<SysAnswerAgain>(SysAnswerAgain.class);
util.exportExcel(response, list, "回复数据");
}
/**
* 获取回复详细信息
*/
@PreAuthorize("@ss.hasPermi('system:again:query') or @ss.hasRole('muhu')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(sysAnswerAgainService.selectSysAnswerAgainById(id));
}
/**
* 新增回复
*/
@PreAuthorize("@ss.hasPermi('system:again:add') or @ss.hasRole('muhu')")
@Log(title = "回复", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody SysAnswerAgain sysAnswerAgain)
{
// 可以在这里验证 answerId 是否为空
if (sysAnswerAgain.getAnswerId() == null) {
return error("对应的回复ID不能为空");
}
return toAjax(sysAnswerAgainService.insertSysAnswerAgain(sysAnswerAgain));
}
/**
* 修改回复
*/
@PreAuthorize("@ss.hasPermi('system:again:edit') or @ss.hasRole('muhu')")
@Log(title = "回复", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody SysAnswerAgain sysAnswerAgain)
{
// 验证权限只能修改自己的回复
SysAnswerAgain existing = sysAnswerAgainService.selectSysAnswerAgainById(sysAnswerAgain.getId());
if (existing != null) {
Long currentUserId = SecurityUtils.getUserId();
if (!existing.getUserId().equals(currentUserId.toString())) {
return error("只能修改自己的回复");
}
}
return toAjax(sysAnswerAgainService.updateSysAnswerAgain(sysAnswerAgain));
}
/**
* 删除回复
*/
@PreAuthorize("@ss.hasPermi('system:again:remove') or @ss.hasRole('muhu')")
@Log(title = "回复", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
// 验证权限只能删除自己的回复
Long currentUserId = SecurityUtils.getUserId();
for (Long id : ids) {
SysAnswerAgain existing = sysAnswerAgainService.selectSysAnswerAgainById(id);
if (existing != null && !existing.getUserId().equals(currentUserId.toString())) {
return error("只能删除自己的回复");
}
}
return toAjax(sysAnswerAgainService.deleteSysAnswerAgainByIds(ids));
}
}

101
chenhai-admin/src/main/java/com/chenhai/web/controller/system/SysAnswersController.java

@ -1,6 +1,8 @@
package com.chenhai.web.controller.system;
import java.util.List;
import com.chenhai.common.exception.ServiceException;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
@ -41,7 +43,7 @@ public class SysAnswersController extends BaseController
/**
* 查询答复列表
*/
@PreAuthorize("@ss.hasPermi('system:answers:list')")
@PreAuthorize("@ss.hasPermi('system:answers:list') or @ss.hasRole('muhu')")
@GetMapping("/list")
public TableDataInfo list(SysAnswers sysAnswers)
{
@ -50,10 +52,42 @@ public class SysAnswersController extends BaseController
return getDataTable(list);
}
/**
* 根据问题ID查询顶级答复列表
*/
@GetMapping("/question/{questionId}/roots")
public TableDataInfo listRootAnswers(@PathVariable("questionId") Long questionId)
{
startPage();
List<SysAnswers> list = sysAnswersService.selectRootAnswers(questionId);
return getDataTable(list);
}
/**
* 根据问题ID查询答复树形结构
*/
@GetMapping("/question/{questionId}/tree")
public AjaxResult getAnswerTree(@PathVariable("questionId") Long questionId)
{
List<SysAnswers> tree = sysAnswersService.selectAnswerTree(questionId);
return success(tree);
}
/**
* 根据父级ID查询子回复列表
*/
@GetMapping("/parent/{parentId}/children")
public TableDataInfo listChildrenAnswers(@PathVariable("parentId") Long parentId)
{
startPage();
List<SysAnswers> list = sysAnswersService.selectChildrenAnswers(parentId);
return getDataTable(list);
}
/**
* 根据问题ID查询答复列表
*/
@PreAuthorize("@ss.hasPermi('system:answers:list')")
@PreAuthorize("@ss.hasPermi('system:answers:list') or @ss.hasRole('muhu')")
@GetMapping("/question/{questionId}")
public TableDataInfo listByQuestionId(@PathVariable("questionId") Long questionId)
{
@ -65,7 +99,7 @@ public class SysAnswersController extends BaseController
/**
* 导出答复列表
*/
@PreAuthorize("@ss.hasPermi('system:answers:export')")
@PreAuthorize("@ss.hasPermi('system:answers:export') or @ss.hasRole('muhu')")
@Log(title = "答复", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, SysAnswers sysAnswers)
@ -78,7 +112,7 @@ public class SysAnswersController extends BaseController
/**
* 获取答复详细信息
*/
@PreAuthorize("@ss.hasPermi('system:answers:query')")
@PreAuthorize("@ss.hasPermi('system:answers:query') or @ss.hasRole('muhu')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
@ -88,16 +122,11 @@ public class SysAnswersController extends BaseController
/**
* 新增答复使用当前登录用户信息
*/
@PreAuthorize("@ss.hasPermi('system:answers:add')")
@PreAuthorize("@ss.hasPermi('system:answers:add') or @ss.hasRole('muhu')")
@Log(title = "答复", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody SysAnswers sysAnswers)
{
// 验证问题是否存在
if (sysAnswers.getQuestionId() == null) {
return error("问题ID不能为空");
}
// 检查问题是否存在
try {
sysQuestionsService.selectSysQuestionsById(sysAnswers.getQuestionId());
@ -109,9 +138,57 @@ public class SysAnswersController extends BaseController
}
/**
* 修改答复
* 新增回复
*/
@PreAuthorize("@ss.hasPermi('system:answers:add') or @ss.hasRole('muhu')")
@Log(title = "回复", businessType = BusinessType.INSERT)
@PostMapping("/reply")
public AjaxResult addReply(@RequestBody SysAnswers sysAnswers)
{
try {
int result = sysAnswersService.insertReply(sysAnswers);
return toAjax(result);
} catch (ServiceException e) {
return error(e.getMessage());
}
}
/**
* 点赞答复
*/
@PreAuthorize("@ss.hasPermi('system:answers:edit')")
@Log(title = "点赞答复", businessType = BusinessType.UPDATE)
@PostMapping("/{id}/like")
public AjaxResult like(@PathVariable("id") Long id)
{
try {
int result = sysAnswersService.likeAnswer(id);
return toAjax(result);
} catch (ServiceException e) {
return error(e.getMessage());
}
}
/**
* 取消点赞答复
*/
@PreAuthorize("@ss.hasPermi('system:answers:edit')")
@Log(title = "取消点赞答复", businessType = BusinessType.UPDATE)
@PostMapping("/{id}/unlike")
public AjaxResult unlike(@PathVariable("id") Long id)
{
try {
int result = sysAnswersService.unlikeAnswer(id);
return toAjax(result);
} catch (ServiceException e) {
return error(e.getMessage());
}
}
/**
* 修改答复
*/
@PreAuthorize("@ss.hasPermi('system:answers:edit') or @ss.hasRole('muhu')")
@Log(title = "答复", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody SysAnswers sysAnswers)
@ -122,7 +199,7 @@ public class SysAnswersController extends BaseController
/**
* 删除答复
*/
@PreAuthorize("@ss.hasPermi('system:answers:remove')")
@PreAuthorize("@ss.hasPermi('system:answers:remove') or @ss.hasRole('muhu')")
@Log(title = "答复", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)

104
chenhai-admin/src/main/java/com/chenhai/web/controller/system/SysMuhuUserController.java

@ -0,0 +1,104 @@
package com.chenhai.web.controller.system;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.chenhai.common.annotation.Log;
import com.chenhai.common.core.controller.BaseController;
import com.chenhai.common.core.domain.AjaxResult;
import com.chenhai.common.enums.BusinessType;
import com.chenhai.system.domain.SysMuhuUser;
import com.chenhai.system.service.ISysMuhuUserService;
import com.chenhai.common.utils.poi.ExcelUtil;
import com.chenhai.common.core.page.TableDataInfo;
/**
* 牧户用户Controller
*
* @author ruoyi
* @date 2026-02-03
*/
@RestController
@RequestMapping("/system/muhuuser")
public class SysMuhuUserController extends BaseController
{
@Autowired
private ISysMuhuUserService sysMuhuUserService;
/**
* 查询牧户用户列表
*/
@PreAuthorize("@ss.hasPermi('system:muhuuser:list')")
@GetMapping("/list")
public TableDataInfo list(SysMuhuUser sysMuhuUser)
{
startPage();
List<SysMuhuUser> list = sysMuhuUserService.selectSysMuhuUserList(sysMuhuUser);
return getDataTable(list);
}
/**
* 导出牧户用户列表
*/
@PreAuthorize("@ss.hasPermi('system:muhuuser:export')")
@Log(title = "牧户用户", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, SysMuhuUser sysMuhuUser)
{
List<SysMuhuUser> list = sysMuhuUserService.selectSysMuhuUserList(sysMuhuUser);
ExcelUtil<SysMuhuUser> util = new ExcelUtil<SysMuhuUser>(SysMuhuUser.class);
util.exportExcel(response, list, "牧户用户数据");
}
/**
* 获取牧户用户详细信息
*/
@PreAuthorize("@ss.hasPermi('system:muhuuser:query')")
@GetMapping(value = "/{userId}")
public AjaxResult getInfo(@PathVariable("userId") Long userId)
{
return success(sysMuhuUserService.selectSysMuhuUserByUserId(userId));
}
/**
* 新增牧户用户
*/
@PreAuthorize("@ss.hasPermi('system:muhuuser:add')")
@Log(title = "牧户用户", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody SysMuhuUser sysMuhuUser)
{
return toAjax(sysMuhuUserService.insertSysMuhuUser(sysMuhuUser));
}
/**
* 修改牧户用户
*/
@PreAuthorize("@ss.hasPermi('system:muhuuser:edit')")
@Log(title = "牧户用户", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody SysMuhuUser sysMuhuUser)
{
return toAjax(sysMuhuUserService.updateSysMuhuUser(sysMuhuUser));
}
/**
* 删除牧户用户
*/
@PreAuthorize("@ss.hasPermi('system:muhuuser:remove')")
@Log(title = "牧户用户", businessType = BusinessType.DELETE)
@DeleteMapping("/{userIds}")
public AjaxResult remove(@PathVariable Long[] userIds)
{
return toAjax(sysMuhuUserService.deleteSysMuhuUserByUserIds(userIds));
}
}

32
chenhai-admin/src/main/java/com/chenhai/web/controller/system/SysQuestionsController.java

@ -1,5 +1,6 @@
package com.chenhai.web.controller.system;
import java.util.Date;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
@ -47,7 +48,7 @@ public class SysQuestionsController extends BaseController
/**
* 查询问答列表
*/
@PreAuthorize("@ss.hasPermi('system:questions:list')")
@PreAuthorize("@ss.hasPermi('system:questions:list') or @ss.hasRole('muhu')")
@GetMapping("/list")
public TableDataInfo list(SysQuestions sysQuestions)
{
@ -59,7 +60,7 @@ public class SysQuestionsController extends BaseController
/**
* 导出问答列表
*/
@PreAuthorize("@ss.hasPermi('system:questions:export')")
@PreAuthorize("@ss.hasPermi('system:questions:export') or @ss.hasRole('muhu')")
@Log(title = "问答", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, SysQuestions sysQuestions)
@ -72,17 +73,34 @@ public class SysQuestionsController extends BaseController
/**
* 获取问答详细信息
*/
@PreAuthorize("@ss.hasPermi('system:questions:query')")
@PreAuthorize("@ss.hasPermi('system:questions:query') or @ss.hasRole('muhu')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(sysQuestionsService.selectSysQuestionsById(id));
// 先获取问题详情
SysQuestions question = sysQuestionsService.selectSysQuestionsById(id);
if (question == null) {
return error("问题不存在");
}
// 增加观看次数
Long currentViewCount = question.getViewCount() != null ? question.getViewCount() : 0;
question.setViewCount(currentViewCount + 1);
question.setUpdatedAt(new Date());
// 保存更新后的浏览量
Long currentUserId = getCurrentUserId();
sysQuestionsService.updateSysQuestions(question, currentUserId);
// 重新获取更新后的问题详情
question = sysQuestionsService.selectSysQuestionsById(id);
return success(question);
}
/**
* 新增问答
*/
@PreAuthorize("@ss.hasPermi('system:questions:add')")
@PreAuthorize("@ss.hasPermi('system:questions:add') or @ss.hasRole('muhu')")
@Log(title = "问答", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody SysQuestions sysQuestions)
@ -94,7 +112,7 @@ public class SysQuestionsController extends BaseController
/**
* 修改问答
*/
@PreAuthorize("@ss.hasPermi('system:questions:edit')")
@PreAuthorize("@ss.hasPermi('system:questions:edit') or @ss.hasRole('muhu')")
@Log(title = "问答", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody SysQuestions sysQuestions)
@ -106,7 +124,7 @@ public class SysQuestionsController extends BaseController
/**
* 删除问答
*/
@PreAuthorize("@ss.hasPermi('system:questions:remove')")
@PreAuthorize("@ss.hasPermi('system:questions:remove') or @ss.hasRole('muhu')")
@Log(title = "问答", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)

433
chenhai-system/src/main/java/com/chenhai/muhu/domain/MuhuExperienceShare.java

@ -0,0 +1,433 @@
package com.chenhai.muhu.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.chenhai.common.annotation.Excel;
import com.chenhai.common.core.domain.BaseEntity;
/**
* 经验分享对象 muhu_experience_share
*
* @author ruoyi
* @date 2026-01-30
*/
public class MuhuExperienceShare extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 发布用户ID */
@Excel(name = "发布用户ID")
private Long userId;
/** 发布用户名 */
@Excel(name = "发布用户名")
private String username;
/** 用户昵称 */
@Excel(name = "用户昵称")
private String userNickname;
/** 用户头像 */
@Excel(name = "用户头像")
private String userAvatar;
/** 文章标题 */
@Excel(name = "文章标题")
private String title;
/** 文章内容 */
@Excel(name = "文章内容")
private String content;
/** 文章摘要 */
@Excel(name = "文章摘要")
private String summary;
/** 分类(如:养殖技术、疾病防治等) */
@Excel(name = "分类", readConverterExp = "如=:养殖技术、疾病防治等")
private String category;
/** 标签(多个用逗号分隔) */
@Excel(name = "标签", readConverterExp = "多=个用逗号分隔")
private String tags;
/** 封面图片 */
@Excel(name = "封面图片")
private String coverImage;
/** 文章图片(多个用逗号分隔) */
@Excel(name = "文章图片", readConverterExp = "多=个用逗号分隔")
private String images;
/** 附件(多个用逗号分隔) */
@Excel(name = "附件", readConverterExp = "多=个用逗号分隔")
private String attachments;
/** 状态(0-待审核,1-已发布,2-审核不通过,3-已下架) */
@Excel(name = "状态", readConverterExp = "0=-待审核,1-已发布,2-审核不通过,3-已下架")
private Integer status;
/** 审核备注 */
@Excel(name = "审核备注")
private String auditRemark;
/** 审核时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "审核时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date auditTime;
/** 审核人ID */
@Excel(name = "审核人ID")
private Long auditUserId;
/** 是否包含敏感词(0-否,1-是) */
@Excel(name = "是否包含敏感词", readConverterExp = "0=-否,1-是")
private Integer hasSensitive;
/** 敏感词列表(多个用逗号分隔) */
@Excel(name = "敏感词列表", readConverterExp = "多=个用逗号分隔")
private String sensitiveWords;
/** 是否已处理敏感词(0-未处理,1-已处理) */
@Excel(name = "是否已处理敏感词", readConverterExp = "0=-未处理,1-已处理")
private Integer isHandled;
/** 浏览次数 */
@Excel(name = "浏览次数")
private Long viewCount;
/** 点赞数 */
@Excel(name = "点赞数")
private Long likeCount;
/** 收藏数 */
@Excel(name = "收藏数")
private Long collectCount;
/** 分享数 */
@Excel(name = "分享数")
private Long shareCount;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date createdAt;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date updatedAt;
/** 发布时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "发布时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date publishedAt;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setUserId(Long userId)
{
this.userId = userId;
}
public Long getUserId()
{
return userId;
}
public void setUsername(String username)
{
this.username = username;
}
public String getUsername()
{
return username;
}
public void setUserNickname(String userNickname)
{
this.userNickname = userNickname;
}
public String getUserNickname()
{
return userNickname;
}
public void setUserAvatar(String userAvatar)
{
this.userAvatar = userAvatar;
}
public String getUserAvatar()
{
return userAvatar;
}
public void setTitle(String title)
{
this.title = title;
}
public String getTitle()
{
return title;
}
public void setContent(String content)
{
this.content = content;
}
public String getContent()
{
return content;
}
public void setSummary(String summary)
{
this.summary = summary;
}
public String getSummary()
{
return summary;
}
public void setCategory(String category)
{
this.category = category;
}
public String getCategory()
{
return category;
}
public void setTags(String tags)
{
this.tags = tags;
}
public String getTags()
{
return tags;
}
public void setCoverImage(String coverImage)
{
this.coverImage = coverImage;
}
public String getCoverImage()
{
return coverImage;
}
public void setImages(String images)
{
this.images = images;
}
public String getImages()
{
return images;
}
public void setAttachments(String attachments)
{
this.attachments = attachments;
}
public String getAttachments()
{
return attachments;
}
public void setStatus(Integer status)
{
this.status = status;
}
public Integer getStatus()
{
return status;
}
public void setAuditRemark(String auditRemark)
{
this.auditRemark = auditRemark;
}
public String getAuditRemark()
{
return auditRemark;
}
public void setAuditTime(Date auditTime)
{
this.auditTime = auditTime;
}
public Date getAuditTime()
{
return auditTime;
}
public void setAuditUserId(Long auditUserId)
{
this.auditUserId = auditUserId;
}
public Long getAuditUserId()
{
return auditUserId;
}
public void setHasSensitive(Integer hasSensitive)
{
this.hasSensitive = hasSensitive;
}
public Integer getHasSensitive()
{
return hasSensitive;
}
public void setSensitiveWords(String sensitiveWords)
{
this.sensitiveWords = sensitiveWords;
}
public String getSensitiveWords()
{
return sensitiveWords;
}
public void setIsHandled(Integer isHandled)
{
this.isHandled = isHandled;
}
public Integer getIsHandled()
{
return isHandled;
}
public void setViewCount(Long viewCount)
{
this.viewCount = viewCount;
}
public Long getViewCount()
{
return viewCount;
}
public void setLikeCount(Long likeCount)
{
this.likeCount = likeCount;
}
public Long getLikeCount()
{
return likeCount;
}
public void setCollectCount(Long collectCount)
{
this.collectCount = collectCount;
}
public Long getCollectCount()
{
return collectCount;
}
public void setShareCount(Long shareCount)
{
this.shareCount = shareCount;
}
public Long getShareCount()
{
return shareCount;
}
public void setCreatedAt(Date createdAt)
{
this.createdAt = createdAt;
}
public Date getCreatedAt()
{
return createdAt;
}
public void setUpdatedAt(Date updatedAt)
{
this.updatedAt = updatedAt;
}
public Date getUpdatedAt()
{
return updatedAt;
}
public void setPublishedAt(Date publishedAt)
{
this.publishedAt = publishedAt;
}
public Date getPublishedAt()
{
return publishedAt;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("userId", getUserId())
.append("username", getUsername())
.append("userNickname", getUserNickname())
.append("userAvatar", getUserAvatar())
.append("title", getTitle())
.append("content", getContent())
.append("summary", getSummary())
.append("category", getCategory())
.append("tags", getTags())
.append("coverImage", getCoverImage())
.append("images", getImages())
.append("attachments", getAttachments())
.append("status", getStatus())
.append("auditRemark", getAuditRemark())
.append("auditTime", getAuditTime())
.append("auditUserId", getAuditUserId())
.append("hasSensitive", getHasSensitive())
.append("sensitiveWords", getSensitiveWords())
.append("isHandled", getIsHandled())
.append("viewCount", getViewCount())
.append("likeCount", getLikeCount())
.append("collectCount", getCollectCount())
.append("shareCount", getShareCount())
.append("createdAt", getCreatedAt())
.append("updatedAt", getUpdatedAt())
.append("publishedAt", getPublishedAt())
.toString();
}
}

61
chenhai-system/src/main/java/com/chenhai/muhu/mapper/MuhuExperienceShareMapper.java

@ -0,0 +1,61 @@
package com.chenhai.muhu.mapper;
import java.util.List;
import com.chenhai.muhu.domain.MuhuExperienceShare;
/**
* 经验分享Mapper接口
*
* @author ruoyi
* @date 2026-01-30
*/
public interface MuhuExperienceShareMapper
{
/**
* 查询经验分享
*
* @param id 经验分享主键
* @return 经验分享
*/
public MuhuExperienceShare selectMuhuExperienceShareById(Long id);
/**
* 查询经验分享列表
*
* @param muhuExperienceShare 经验分享
* @return 经验分享集合
*/
public List<MuhuExperienceShare> selectMuhuExperienceShareList(MuhuExperienceShare muhuExperienceShare);
/**
* 新增经验分享
*
* @param muhuExperienceShare 经验分享
* @return 结果
*/
public int insertMuhuExperienceShare(MuhuExperienceShare muhuExperienceShare);
/**
* 修改经验分享
*
* @param muhuExperienceShare 经验分享
* @return 结果
*/
public int updateMuhuExperienceShare(MuhuExperienceShare muhuExperienceShare);
/**
* 删除经验分享
*
* @param id 经验分享主键
* @return 结果
*/
public int deleteMuhuExperienceShareById(Long id);
/**
* 批量删除经验分享
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteMuhuExperienceShareByIds(Long[] ids);
}

61
chenhai-system/src/main/java/com/chenhai/muhu/service/IMuhuExperienceShareService.java

@ -0,0 +1,61 @@
package com.chenhai.muhu.service;
import java.util.List;
import com.chenhai.muhu.domain.MuhuExperienceShare;
/**
* 经验分享Service接口
*
* @author ruoyi
* @date 2026-01-30
*/
public interface IMuhuExperienceShareService
{
/**
* 查询经验分享
*
* @param id 经验分享主键
* @return 经验分享
*/
public MuhuExperienceShare selectMuhuExperienceShareById(Long id);
/**
* 查询经验分享列表
*
* @param muhuExperienceShare 经验分享
* @return 经验分享集合
*/
public List<MuhuExperienceShare> selectMuhuExperienceShareList(MuhuExperienceShare muhuExperienceShare);
/**
* 新增经验分享
*
* @param muhuExperienceShare 经验分享
* @return 结果
*/
public int insertMuhuExperienceShare(MuhuExperienceShare muhuExperienceShare);
/**
* 修改经验分享
*
* @param muhuExperienceShare 经验分享
* @return 结果
*/
public int updateMuhuExperienceShare(MuhuExperienceShare muhuExperienceShare);
/**
* 批量删除经验分享
*
* @param ids 需要删除的经验分享主键集合
* @return 结果
*/
public int deleteMuhuExperienceShareByIds(Long[] ids);
/**
* 删除经验分享信息
*
* @param id 经验分享主键
* @return 结果
*/
public int deleteMuhuExperienceShareById(Long id);
}

93
chenhai-system/src/main/java/com/chenhai/muhu/service/impl/MuhuExperienceShareServiceImpl.java

@ -0,0 +1,93 @@
package com.chenhai.muhu.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.chenhai.muhu.mapper.MuhuExperienceShareMapper;
import com.chenhai.muhu.domain.MuhuExperienceShare;
import com.chenhai.muhu.service.IMuhuExperienceShareService;
/**
* 经验分享Service业务层处理
*
* @author ruoyi
* @date 2026-01-30
*/
@Service
public class MuhuExperienceShareServiceImpl implements IMuhuExperienceShareService
{
@Autowired
private MuhuExperienceShareMapper muhuExperienceShareMapper;
/**
* 查询经验分享
*
* @param id 经验分享主键
* @return 经验分享
*/
@Override
public MuhuExperienceShare selectMuhuExperienceShareById(Long id)
{
return muhuExperienceShareMapper.selectMuhuExperienceShareById(id);
}
/**
* 查询经验分享列表
*
* @param muhuExperienceShare 经验分享
* @return 经验分享
*/
@Override
public List<MuhuExperienceShare> selectMuhuExperienceShareList(MuhuExperienceShare muhuExperienceShare)
{
return muhuExperienceShareMapper.selectMuhuExperienceShareList(muhuExperienceShare);
}
/**
* 新增经验分享
*
* @param muhuExperienceShare 经验分享
* @return 结果
*/
@Override
public int insertMuhuExperienceShare(MuhuExperienceShare muhuExperienceShare)
{
return muhuExperienceShareMapper.insertMuhuExperienceShare(muhuExperienceShare);
}
/**
* 修改经验分享
*
* @param muhuExperienceShare 经验分享
* @return 结果
*/
@Override
public int updateMuhuExperienceShare(MuhuExperienceShare muhuExperienceShare)
{
return muhuExperienceShareMapper.updateMuhuExperienceShare(muhuExperienceShare);
}
/**
* 批量删除经验分享
*
* @param ids 需要删除的经验分享主键
* @return 结果
*/
@Override
public int deleteMuhuExperienceShareByIds(Long[] ids)
{
return muhuExperienceShareMapper.deleteMuhuExperienceShareByIds(ids);
}
/**
* 删除经验分享信息
*
* @param id 经验分享主键
* @return 结果
*/
@Override
public int deleteMuhuExperienceShareById(Long id)
{
return muhuExperienceShareMapper.deleteMuhuExperienceShareById(id);
}
}

155
chenhai-system/src/main/java/com/chenhai/system/domain/SysAnswerAgain.java

@ -0,0 +1,155 @@
package com.chenhai.system.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.chenhai.common.annotation.Excel;
import com.chenhai.common.core.domain.BaseEntity;
/**
* 回复对象 sys_answer_again
*
* @author ruoyi
* @date 2026-02-02
*/
public class SysAnswerAgain extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** $column.columnComment */
private Long id;
/** 对应的回复id */
@Excel(name = "对应的回复id")
private Long answerId;
/** 用户id */
@Excel(name = "用户id")
private String userId;
/** 用户名 */
@Excel(name = "用户名")
private String username;
/** 用户昵称 */
private String nickName;
/** 用户头像 */
private String avatar;
/** 回复内容 */
@Excel(name = "回复内容")
private String content;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date createdAt;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date updatedAt;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setAnswerId(Long answerId)
{
this.answerId = answerId;
}
public Long getAnswerId()
{
return answerId;
}
public void setUserId(String userId)
{
this.userId = userId;
}
public String getUserId()
{
return userId;
}
public void setUsername(String username)
{
this.username = username;
}
public String getUsername()
{
return username;
}
public void setContent(String content)
{
this.content = content;
}
public String getContent()
{
return content;
}
public void setCreatedAt(Date createdAt)
{
this.createdAt = createdAt;
}
public Date getCreatedAt()
{
return createdAt;
}
public void setUpdatedAt(Date updatedAt)
{
this.updatedAt = updatedAt;
}
public Date getUpdatedAt()
{
return updatedAt;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("answerId", getAnswerId())
.append("userId", getUserId())
.append("username", getUsername())
.append("content", getContent())
.append("createdAt", getCreatedAt())
.append("updatedAt", getUpdatedAt())
.append("nickName", getNickName())
.append("avatar", getAvatar())
.toString();
}
}

169
chenhai-system/src/main/java/com/chenhai/system/domain/SysAnswers.java

@ -1,6 +1,7 @@
package com.chenhai.system.domain;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -33,7 +34,6 @@ public class SysAnswers extends BaseEntity
private String username;
/** 用户昵称 */
@Excel(name = "用户昵称")
private String nickName;
/** 用户头像 */
@ -53,6 +53,53 @@ public class SysAnswers extends BaseEntity
@Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date updatedAt;
/** 图片(多个用逗号分隔) */
@Excel(name = "图片")
private String images;
/** 父级答复ID(0表示顶级答复) */
@Excel(name = "父级答复ID")
private Long parentId;
/** 回复层级(从0开始) */
@Excel(name = "回复层级")
private Integer level;
/** 回复路径(用于快速查询子回复) */
private String path;
/** 回复数量 */
@Excel(name = "回复数量")
private Integer replyCount;
/** 点赞数量 */
@Excel(name = "点赞数量")
private Integer likeCount;
/** 是否已点赞(前端展示用) */
private Boolean liked;
/** 子回复列表(非数据库字段) */
private List<SysAnswers> children;
/** 父级答复信息(非数据库字段) */
private SysAnswers parentAnswer;
// /** 最新回复内容(预览用) */
// private String latestReplyContent;
//
// /** 最新回复用户昵称 */
// private String latestReplyNickName;
//
// /** 最新回复用户头像 */
// private String latestReplyAvatar;
//
// /** 最新回复时间 */
// private Date latestReplyTime;
//
// /** 是否有更多回复(超过预览数量时) */
// private Boolean hasMoreReplies;
public void setId(Long id)
{
this.id = id;
@ -143,6 +190,120 @@ public class SysAnswers extends BaseEntity
return updatedAt;
}
public void setImages(String images)
{
this.images = images;
}
public String getImages()
{
return images;
}
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public Integer getLevel() {
return level;
}
public void setLevel(Integer level) {
this.level = level;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public Integer getReplyCount() {
return replyCount;
}
public void setReplyCount(Integer replyCount) {
this.replyCount = replyCount;
}
public Integer getLikeCount() {
return likeCount;
}
public void setLikeCount(Integer likeCount) {
this.likeCount = likeCount;
}
public Boolean getLiked() {
return liked;
}
public void setLiked(Boolean liked) {
this.liked = liked;
}
public List<SysAnswers> getChildren() {
return children;
}
public void setChildren(List<SysAnswers> children) {
this.children = children;
}
public SysAnswers getParentAnswer() {
return parentAnswer;
}
public void setParentAnswer(SysAnswers parentAnswer) {
this.parentAnswer = parentAnswer;
}
// public String getLatestReplyContent() {
// return latestReplyContent;
// }
//
// public void setLatestReplyContent(String latestReplyContent) {
// this.latestReplyContent = latestReplyContent;
// }
//
// public String getLatestReplyNickName() {
// return latestReplyNickName;
// }
//
// public void setLatestReplyNickName(String latestReplyNickName) {
// this.latestReplyNickName = latestReplyNickName;
// }
//
// public String getLatestReplyAvatar() {
// return latestReplyAvatar;
// }
//
// public void setLatestReplyAvatar(String latestReplyAvatar) {
// this.latestReplyAvatar = latestReplyAvatar;
// }
//
// public Date getLatestReplyTime() {
// return latestReplyTime;
// }
//
// public void setLatestReplyTime(Date latestReplyTime) {
// this.latestReplyTime = latestReplyTime;
// }
//
// public Boolean getHasMoreReplies() {
// return hasMoreReplies;
// }
//
// public void setHasMoreReplies(Boolean hasMoreReplies) {
// this.hasMoreReplies = hasMoreReplies;
// }
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@ -155,6 +316,12 @@ public class SysAnswers extends BaseEntity
.append("content", getContent())
.append("createdAt", getCreatedAt())
.append("updatedAt", getUpdatedAt())
.append("images", getImages())
.append("parentId", getParentId())
.append("level", getLevel())
.append("path", getPath())
.append("replyCount", getReplyCount())
.append("likeCount", getLikeCount())
.toString();
}
}

222
chenhai-system/src/main/java/com/chenhai/system/domain/SysMuhuUser.java

@ -0,0 +1,222 @@
package com.chenhai.system.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.chenhai.common.annotation.Excel;
import com.chenhai.common.core.domain.BaseEntity;
/**
* 牧户用户对象 sys_muhu_user
*
* @author ruoyi
* @date 2026-02-03
*/
public class SysMuhuUser extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 用户ID,关联 sys_user.user_id */
private Long userId;
/** 真实姓名 */
@Excel(name = "真实姓名")
private String realName;
/** 身份证号 */
@Excel(name = "身份证号")
private String idCard;
/** 身份证正面照 */
@Excel(name = "身份证正面照")
private String idCardFront;
/** 身份证反面照 */
@Excel(name = "身份证反面照")
private String idCardBack;
/** 人脸识别照片 */
@Excel(name = "人脸识别照片")
private String faceImage;
/** 认证状态(0未认证 1已认证 2审核中 3认证失败) */
@Excel(name = "认证状态", readConverterExp = "0=未认证,1=已认证,2=审核中,3=认证失败")
private String authStatus;
/** 认证时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "认证时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date authTime;
/** 认证失败原因 */
@Excel(name = "认证失败原因")
private String authFailReason;
/** 安全问题 */
@Excel(name = "安全问题")
private String securityQuestion;
/** 安全答案 */
@Excel(name = "安全答案")
private String securityAnswer;
/** 最后登录时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date lastLoginTime;
/** 最后登录IP */
@Excel(name = "最后登录IP")
private String lastLoginIp;
public void setUserId(Long userId)
{
this.userId = userId;
}
public Long getUserId()
{
return userId;
}
public void setRealName(String realName)
{
this.realName = realName;
}
public String getRealName()
{
return realName;
}
public void setIdCard(String idCard)
{
this.idCard = idCard;
}
public String getIdCard()
{
return idCard;
}
public void setIdCardFront(String idCardFront)
{
this.idCardFront = idCardFront;
}
public String getIdCardFront()
{
return idCardFront;
}
public void setIdCardBack(String idCardBack)
{
this.idCardBack = idCardBack;
}
public String getIdCardBack()
{
return idCardBack;
}
public void setFaceImage(String faceImage)
{
this.faceImage = faceImage;
}
public String getFaceImage()
{
return faceImage;
}
public void setAuthStatus(String authStatus)
{
this.authStatus = authStatus;
}
public String getAuthStatus()
{
return authStatus;
}
public void setAuthTime(Date authTime)
{
this.authTime = authTime;
}
public Date getAuthTime()
{
return authTime;
}
public void setAuthFailReason(String authFailReason)
{
this.authFailReason = authFailReason;
}
public String getAuthFailReason()
{
return authFailReason;
}
public void setSecurityQuestion(String securityQuestion)
{
this.securityQuestion = securityQuestion;
}
public String getSecurityQuestion()
{
return securityQuestion;
}
public void setSecurityAnswer(String securityAnswer)
{
this.securityAnswer = securityAnswer;
}
public String getSecurityAnswer()
{
return securityAnswer;
}
public void setLastLoginTime(Date lastLoginTime)
{
this.lastLoginTime = lastLoginTime;
}
public Date getLastLoginTime()
{
return lastLoginTime;
}
public void setLastLoginIp(String lastLoginIp)
{
this.lastLoginIp = lastLoginIp;
}
public String getLastLoginIp()
{
return lastLoginIp;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("userId", getUserId())
.append("realName", getRealName())
.append("idCard", getIdCard())
.append("idCardFront", getIdCardFront())
.append("idCardBack", getIdCardBack())
.append("faceImage", getFaceImage())
.append("authStatus", getAuthStatus())
.append("authTime", getAuthTime())
.append("authFailReason", getAuthFailReason())
.append("securityQuestion", getSecurityQuestion())
.append("securityAnswer", getSecurityAnswer())
.append("lastLoginTime", getLastLoginTime())
.append("lastLoginIp", getLastLoginIp())
.append("updateTime", getUpdateTime())
.toString();
}
}

40
chenhai-system/src/main/java/com/chenhai/system/domain/SysQuestions.java

@ -61,6 +61,17 @@ public class SysQuestions extends BaseEntity
/** 用户头像 */
private String avatar;
/** 图片(多个用逗号分隔) */
@Excel(name = "图片")
private String images;
/** 搜索关键字 - 新增字段 */
private String searchKey;
/** 浏览量 */
@Excel(name = "浏览量")
private Long viewCount;
public void setId(Long id)
{
this.id = id;
@ -167,6 +178,32 @@ public class SysQuestions extends BaseEntity
this.avatar = avatar;
}
public void setImages(String images)
{
this.images = images;
}
public String getImages()
{
return images;
}
public String getSearchKey() {
return searchKey;
}
public void setSearchKey(String searchKey) {
this.searchKey = searchKey;
}
public Long getViewCount() {
return viewCount;
}
public void setViewCount(Long viewCount) {
this.viewCount = viewCount;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@ -181,6 +218,9 @@ public class SysQuestions extends BaseEntity
.append("updatedAt", getUpdatedAt())
.append("nickName", getNickName())
.append("avatar", getAvatar())
.append("images", getImages())
.append("searchKey", getSearchKey())
.append("viewCount", getViewCount())
.toString();
}
}

61
chenhai-system/src/main/java/com/chenhai/system/mapper/SysAnswerAgainMapper.java

@ -0,0 +1,61 @@
package com.chenhai.system.mapper;
import java.util.List;
import com.chenhai.system.domain.SysAnswerAgain;
/**
* 回复Mapper接口
*
* @author ruoyi
* @date 2026-02-02
*/
public interface SysAnswerAgainMapper
{
/**
* 查询回复
*
* @param id 回复主键
* @return 回复
*/
public SysAnswerAgain selectSysAnswerAgainById(Long id);
/**
* 查询回复列表
*
* @param sysAnswerAgain 回复
* @return 回复集合
*/
public List<SysAnswerAgain> selectSysAnswerAgainList(SysAnswerAgain sysAnswerAgain);
/**
* 新增回复
*
* @param sysAnswerAgain 回复
* @return 结果
*/
public int insertSysAnswerAgain(SysAnswerAgain sysAnswerAgain);
/**
* 修改回复
*
* @param sysAnswerAgain 回复
* @return 结果
*/
public int updateSysAnswerAgain(SysAnswerAgain sysAnswerAgain);
/**
* 删除回复
*
* @param id 回复主键
* @return 结果
*/
public int deleteSysAnswerAgainById(Long id);
/**
* 批量删除回复
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteSysAnswerAgainByIds(Long[] ids);
}

51
chenhai-system/src/main/java/com/chenhai/system/mapper/SysAnswersMapper.java

@ -75,4 +75,55 @@ public interface SysAnswersMapper
* @return 答案数量
*/
public Long countAnswersByQuestionId(Long questionId);
// 以下是新增的回复相关方法
/**
* 查询顶级答复列表parent_id = 0
*
* @param questionId 问题ID
* @return 答复集合
*/
public List<SysAnswers> selectRootAnswers(Long questionId);
/**
* 查询子回复列表
*
* @param parentId 父答复ID
* @return 子回复集合
*/
public List<SysAnswers> selectChildrenAnswers(Long parentId);
/**
* 查询答复的完整树结构
*
* @param questionId 问题ID
* @return 答复集合
*/
public List<SysAnswers> selectAnswerTree(Long questionId);
/**
* 更新答复的回复数量
*
* @param id 答复ID
* @return 结果
*/
public int updateReplyCount(Long id);
/**
* 更新点赞数量
*
* @param id 答复ID
* @param increment 增量1或-1
* @return 结果
*/
public int updateLikeCount(@Param("id") Long id, @Param("increment") int increment);
/**
* 查询某个答复的所有后代ID用于级联删除
*
* @param id 答复ID
* @return 后代ID集合
*/
public List<Long> selectDescendantIds(Long id);
}

61
chenhai-system/src/main/java/com/chenhai/system/mapper/SysMuhuUserMapper.java

@ -0,0 +1,61 @@
package com.chenhai.system.mapper;
import java.util.List;
import com.chenhai.system.domain.SysMuhuUser;
/**
* 牧户用户Mapper接口
*
* @author ruoyi
* @date 2026-02-03
*/
public interface SysMuhuUserMapper
{
/**
* 查询牧户用户
*
* @param userId 牧户用户主键
* @return 牧户用户
*/
public SysMuhuUser selectSysMuhuUserByUserId(Long userId);
/**
* 查询牧户用户列表
*
* @param sysMuhuUser 牧户用户
* @return 牧户用户集合
*/
public List<SysMuhuUser> selectSysMuhuUserList(SysMuhuUser sysMuhuUser);
/**
* 新增牧户用户
*
* @param sysMuhuUser 牧户用户
* @return 结果
*/
public int insertSysMuhuUser(SysMuhuUser sysMuhuUser);
/**
* 修改牧户用户
*
* @param sysMuhuUser 牧户用户
* @return 结果
*/
public int updateSysMuhuUser(SysMuhuUser sysMuhuUser);
/**
* 删除牧户用户
*
* @param userId 牧户用户主键
* @return 结果
*/
public int deleteSysMuhuUserByUserId(Long userId);
/**
* 批量删除牧户用户
*
* @param userIds 需要删除的数据主键集合
* @return 结果
*/
public int deleteSysMuhuUserByUserIds(Long[] userIds);
}

61
chenhai-system/src/main/java/com/chenhai/system/service/ISysAnswerAgainService.java

@ -0,0 +1,61 @@
package com.chenhai.system.service;
import java.util.List;
import com.chenhai.system.domain.SysAnswerAgain;
/**
* 回复Service接口
*
* @author ruoyi
* @date 2026-02-02
*/
public interface ISysAnswerAgainService
{
/**
* 查询回复
*
* @param id 回复主键
* @return 回复
*/
public SysAnswerAgain selectSysAnswerAgainById(Long id);
/**
* 查询回复列表
*
* @param sysAnswerAgain 回复
* @return 回复集合
*/
public List<SysAnswerAgain> selectSysAnswerAgainList(SysAnswerAgain sysAnswerAgain);
/**
* 新增回复
*
* @param sysAnswerAgain 回复
* @return 结果
*/
public int insertSysAnswerAgain(SysAnswerAgain sysAnswerAgain);
/**
* 修改回复
*
* @param sysAnswerAgain 回复
* @return 结果
*/
public int updateSysAnswerAgain(SysAnswerAgain sysAnswerAgain);
/**
* 批量删除回复
*
* @param ids 需要删除的回复主键集合
* @return 结果
*/
public int deleteSysAnswerAgainByIds(Long[] ids);
/**
* 删除回复信息
*
* @param id 回复主键
* @return 结果
*/
public int deleteSysAnswerAgainById(Long id);
}

57
chenhai-system/src/main/java/com/chenhai/system/service/ISysAnswersService.java

@ -27,6 +27,30 @@ public interface ISysAnswersService
*/
public List<SysAnswers> selectSysAnswersList(SysAnswers sysAnswers);
/**
* 查询顶级答复列表parent_id = 0
*
* @param questionId 问题ID
* @return 答复集合
*/
public List<SysAnswers> selectRootAnswers(Long questionId);
/**
* 查询答复的树形结构
*
* @param questionId 问题ID
* @return 树形答复集合
*/
public List<SysAnswers> selectAnswerTree(Long questionId);
/**
* 查询子回复列表
*
* @param parentId 父答复ID
* @return 子回复集合
*/
public List<SysAnswers> selectChildrenAnswers(Long parentId);
/**
* 新增答复
*
@ -43,6 +67,14 @@ public interface ISysAnswersService
*/
public int insertSysAnswersWithCurrentUser(SysAnswers sysAnswers);
/**
* 新增回复
*
* @param sysAnswers 回复
* @return 结果
*/
public int insertReply(SysAnswers sysAnswers);
/**
* 修改答复
*
@ -90,4 +122,29 @@ public interface ISysAnswersService
* @return 答案数量
*/
public Long countAnswersByQuestionId(Long questionId);
/**
* 点赞答复
*
* @param id 答复ID
* @return 结果
*/
public int likeAnswer(Long id);
/**
* 取消点赞答复
*
* @param id 答复ID
* @return 结果
*/
public int unlikeAnswer(Long id);
/**
* 检查用户是否点赞
*
* @param id 答复ID
* @param userId 用户ID
* @return 是否点赞
*/
public boolean checkUserLike(Long id, String userId);
}

61
chenhai-system/src/main/java/com/chenhai/system/service/ISysMuhuUserService.java

@ -0,0 +1,61 @@
package com.chenhai.system.service;
import java.util.List;
import com.chenhai.system.domain.SysMuhuUser;
/**
* 牧户用户Service接口
*
* @author ruoyi
* @date 2026-02-03
*/
public interface ISysMuhuUserService
{
/**
* 查询牧户用户
*
* @param userId 牧户用户主键
* @return 牧户用户
*/
public SysMuhuUser selectSysMuhuUserByUserId(Long userId);
/**
* 查询牧户用户列表
*
* @param sysMuhuUser 牧户用户
* @return 牧户用户集合
*/
public List<SysMuhuUser> selectSysMuhuUserList(SysMuhuUser sysMuhuUser);
/**
* 新增牧户用户
*
* @param sysMuhuUser 牧户用户
* @return 结果
*/
public int insertSysMuhuUser(SysMuhuUser sysMuhuUser);
/**
* 修改牧户用户
*
* @param sysMuhuUser 牧户用户
* @return 结果
*/
public int updateSysMuhuUser(SysMuhuUser sysMuhuUser);
/**
* 批量删除牧户用户
*
* @param userIds 需要删除的牧户用户主键集合
* @return 结果
*/
public int deleteSysMuhuUserByUserIds(Long[] userIds);
/**
* 删除牧户用户信息
*
* @param userId 牧户用户主键
* @return 结果
*/
public int deleteSysMuhuUserByUserId(Long userId);
}

121
chenhai-system/src/main/java/com/chenhai/system/service/impl/SysAnswerAgainServiceImpl.java

@ -0,0 +1,121 @@
package com.chenhai.system.service.impl;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.chenhai.common.utils.SecurityUtils;
import com.chenhai.system.domain.SysAnswerAgain;
import com.chenhai.system.mapper.SysAnswerAgainMapper;
import com.chenhai.system.mapper.SysUserMapper;
import com.chenhai.system.service.ISysAnswerAgainService;
/**
* 回复Service业务层处理
*
* @author ruoyi
* @date 2026-02-02
*/
@Service
public class SysAnswerAgainServiceImpl implements ISysAnswerAgainService
{
@Autowired
private SysAnswerAgainMapper sysAnswerAgainMapper;
@Autowired
private SysUserMapper sysUserMapper;
/**
* 查询回复
*
* @param id 回复主键
* @return 回复
*/
@Override
public SysAnswerAgain selectSysAnswerAgainById(Long id)
{
return sysAnswerAgainMapper.selectSysAnswerAgainById(id);
}
/**
* 查询回复列表
*
* @param sysAnswerAgain 回复
* @return 回复
*/
@Override
public List<SysAnswerAgain> selectSysAnswerAgainList(SysAnswerAgain sysAnswerAgain)
{
return sysAnswerAgainMapper.selectSysAnswerAgainList(sysAnswerAgain);
}
/**
* 新增回复
*
* @param sysAnswerAgain 回复
* @return 结果
*/
@Override
public int insertSysAnswerAgain(SysAnswerAgain sysAnswerAgain)
{
// 获取当前登录用户信息
Long userId = SecurityUtils.getUserId();
com.chenhai.common.core.domain.entity.SysUser currentUser = sysUserMapper.selectUserById(userId);
if (currentUser != null) {
// 自动设置用户信息
sysAnswerAgain.setUserId(currentUser.getUserId().toString());
sysAnswerAgain.setUsername(currentUser.getUserName());
}
// 设置创建和更新时间
Date now = new Date();
if (sysAnswerAgain.getCreatedAt() == null) {
sysAnswerAgain.setCreatedAt(now);
}
if (sysAnswerAgain.getUpdatedAt() == null) {
sysAnswerAgain.setUpdatedAt(now);
}
return sysAnswerAgainMapper.insertSysAnswerAgain(sysAnswerAgain);
}
/**
* 修改回复
*
* @param sysAnswerAgain 回复
* @return 结果
*/
@Override
public int updateSysAnswerAgain(SysAnswerAgain sysAnswerAgain)
{
// 更新时设置更新时间
sysAnswerAgain.setUpdatedAt(new Date());
return sysAnswerAgainMapper.updateSysAnswerAgain(sysAnswerAgain);
}
/**
* 批量删除回复
*
* @param ids 需要删除的回复主键
* @return 结果
*/
@Override
public int deleteSysAnswerAgainByIds(Long[] ids)
{
return sysAnswerAgainMapper.deleteSysAnswerAgainByIds(ids);
}
/**
* 删除回复信息
*
* @param id 回复主键
* @return 结果
*/
@Override
public int deleteSysAnswerAgainById(Long id)
{
return sysAnswerAgainMapper.deleteSysAnswerAgainById(id);
}
}

370
chenhai-system/src/main/java/com/chenhai/system/service/impl/SysAnswersServiceImpl.java

@ -1,6 +1,7 @@
package com.chenhai.system.service.impl;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -13,6 +14,9 @@ import com.chenhai.system.service.ISysAnswersService;
import com.chenhai.system.service.ISysQuestionsService;
import com.chenhai.common.exception.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 答复Service业务层处理
*
@ -22,6 +26,8 @@ import com.chenhai.common.exception.ServiceException;
@Service
public class SysAnswersServiceImpl implements ISysAnswersService
{
private static final Logger log = LoggerFactory.getLogger(SysAnswersServiceImpl.class);
@Autowired
private SysAnswersMapper sysAnswersMapper;
@ -37,7 +43,20 @@ public class SysAnswersServiceImpl implements ISysAnswersService
@Override
public SysAnswers selectSysAnswersById(Long id)
{
return sysAnswersMapper.selectSysAnswersById(id);
SysAnswers answer = sysAnswersMapper.selectSysAnswersById(id);
if (answer != null) {
if (SecurityUtils.getLoginUser() != null) {
String userId = String.valueOf(SecurityUtils.getUserId());
answer.setLiked(checkUserLike(id, userId));
}
// 如果这个回答有回复加载前3条回复到children
if (answer.getReplyCount() != null && answer.getReplyCount() > 0) {
List<SysAnswers> children = selectChildrenAnswers(answer.getId());
answer.setChildren(children);
}
}
return answer;
}
/**
@ -49,7 +68,112 @@ public class SysAnswersServiceImpl implements ISysAnswersService
@Override
public List<SysAnswers> selectSysAnswersList(SysAnswers sysAnswers)
{
return sysAnswersMapper.selectSysAnswersList(sysAnswers);
// 默认只查询顶级回答
if (sysAnswers.getParentId() == null) {
sysAnswers.setParentId(0L);
}
// 执行基础查询
List<SysAnswers> list = sysAnswersMapper.selectSysAnswersList(sysAnswers);
if (SecurityUtils.getLoginUser() != null) {
String userId = String.valueOf(SecurityUtils.getUserId());
list.forEach(answer -> {
answer.setLiked(checkUserLike(answer.getId(), userId));
// 如果这个回答有回复加载前3条回复到children
if (answer.getReplyCount() != null && answer.getReplyCount() > 0) {
List<SysAnswers> children = selectChildrenAnswers(answer.getId());
answer.setChildren(children);
}
});
}
return list;
}
/**
* 查询顶级答复列表
*
* @param questionId 问题ID
* @return 答复集合
*/
@Override
public List<SysAnswers> selectRootAnswers(Long questionId)
{
List<SysAnswers> list = sysAnswersMapper.selectRootAnswers(questionId);
if (SecurityUtils.getLoginUser() != null) {
String userId = String.valueOf(SecurityUtils.getUserId());
list.forEach(answer -> {
answer.setLiked(checkUserLike(answer.getId(), userId));
// 加载前3条回复
List<SysAnswers> children = selectChildrenAnswers(answer.getId());
answer.setChildren(children);
});
}
return list;
}
/**
* 查询答复的树形结构
*
* @param questionId 问题ID
* @return 树形答复集合
*/
@Override
public List<SysAnswers> selectAnswerTree(Long questionId)
{
List<SysAnswers> allAnswers = sysAnswersMapper.selectAnswerTree(questionId);
// 构建树形结构
Map<Long, SysAnswers> answerMap = new HashMap<>();
List<SysAnswers> rootAnswers = new ArrayList<>();
// 放入map并设置点赞状态
String userId = null;
if (SecurityUtils.getLoginUser() != null) {
userId = String.valueOf(SecurityUtils.getUserId());
}
for (SysAnswers answer : allAnswers) {
if (userId != null) {
answer.setLiked(checkUserLike(answer.getId(), userId));
}
answerMap.put(answer.getId(), answer);
}
// 构建树
for (SysAnswers answer : allAnswers) {
if (answer.getParentId() == null || answer.getParentId() == 0) {
rootAnswers.add(answer);
} else {
SysAnswers parent = answerMap.get(answer.getParentId());
if (parent != null) {
if (parent.getChildren() == null) {
parent.setChildren(new ArrayList<>());
}
parent.getChildren().add(answer);
}
}
}
return rootAnswers;
}
/**
* 查询子回复列表
*
* @param parentId 父答复ID
* @return 子回复集合
*/
@Override
public List<SysAnswers> selectChildrenAnswers(Long parentId)
{
List<SysAnswers> list = sysAnswersMapper.selectChildrenAnswers(parentId);
if (SecurityUtils.getLoginUser() != null) {
String userId = String.valueOf(SecurityUtils.getUserId());
list.forEach(answer -> answer.setLiked(checkUserLike(answer.getId(), userId)));
}
return list;
}
/**
@ -62,13 +186,38 @@ public class SysAnswersServiceImpl implements ISysAnswersService
@Transactional
public int insertSysAnswers(SysAnswers sysAnswers)
{
// 验证问题ID是否存在
if (sysAnswers.getQuestionId() == null) {
throw new ServiceException("问题ID不能为空");
// 设置默认值
if (sysAnswers.getParentId() == null) {
sysAnswers.setParentId(0L);
sysAnswers.setLevel(0);
sysAnswers.setPath("0/");
}
if (sysAnswers.getReplyCount() == null) {
sysAnswers.setReplyCount(0);
}
if (sysAnswers.getLikeCount() == null) {
sysAnswers.setLikeCount(0);
}
int result = sysAnswersMapper.insertSysAnswers(sysAnswers);
// 更新path
if (sysAnswers.getParentId() == 0) {
sysAnswers.setPath("0/" + sysAnswers.getId() + "/");
} else {
SysAnswers parent = sysAnswersMapper.selectSysAnswersById(sysAnswers.getParentId());
if (parent != null) {
sysAnswers.setLevel(parent.getLevel() + 1);
sysAnswers.setPath(parent.getPath() + sysAnswers.getId() + "/");
}
sysAnswersMapper.updateSysAnswers(sysAnswers);
// 更新父级的回复数量
sysAnswersMapper.updateReplyCount(sysAnswers.getParentId());
}
if (result > 0) {
// 增加问题的答案数量
sysQuestionsService.increaseAnswerCount(sysAnswers.getQuestionId());
@ -87,11 +236,6 @@ public class SysAnswersServiceImpl implements ISysAnswersService
@Transactional
public int insertSysAnswersWithCurrentUser(SysAnswers sysAnswers)
{
// 验证问题ID是否存在
if (sysAnswers.getQuestionId() == null) {
throw new ServiceException("问题ID不能为空");
}
// 获取当前登录用户信息
SysUser currentUser = SecurityUtils.getLoginUser().getUser();
@ -105,11 +249,93 @@ public class SysAnswersServiceImpl implements ISysAnswersService
// 设置头像
sysAnswers.setAvatar(currentUser.getAvatar());
return insertSysAnswers(sysAnswers);
}
/**
* 新增回复
*
* @param sysAnswers 回复
* @return 结果
*/
@Override
@Transactional
public int insertReply(SysAnswers sysAnswers)
{
log.info("开始新增回复,参数: {}", sysAnswers);
// 检查父级答复是否存在
SysAnswers parentAnswer = null;
Long parentId = sysAnswers.getParentId();
log.info("父级ID: {}", parentId);
if (parentId == null) {
log.error("父级答复ID为null");
throw new ServiceException("父级答复ID不能为空");
}
if (parentId > 0) {
parentAnswer = sysAnswersMapper.selectSysAnswersById(parentId);
if (parentAnswer == null) {
throw new ServiceException("父级答复不存在");
}
// 设置问题ID为父级答复的问题ID
sysAnswers.setQuestionId(parentAnswer.getQuestionId());
} else {
// parentId 0表示这是顶级回答不是回复
throw new ServiceException("回复必须有父级答复");
}
// 获取当前登录用户信息
SysUser currentUser = SecurityUtils.getLoginUser().getUser();
sysAnswers.setUserId(String.valueOf(currentUser.getUserId()));
sysAnswers.setUsername(currentUser.getUserName());
// 设置层级和路径 - 修复这里的逻辑
if (parentAnswer != null) {
Integer parentLevel = parentAnswer.getLevel();
if (parentLevel == null) {
parentLevel = 0;
}
sysAnswers.setLevel(parentLevel + 1);
// 修复路径设置逻辑
// 父级路径应该是父级自己的完整路径比如父级ID=13path="0/13/"
// 新回复的path应该是 "0/13/24/"
String parentPath = parentAnswer.getPath();
if (parentPath == null || "0/".equals(parentPath)) {
// 如果父级路径是根路径需要修正为包含父级ID的路径
parentPath = "0/" + parentId + "/";
}
sysAnswers.setPath(parentPath + "pending/");
log.info("设置回复路径 - 父级路径: {}, 新回复临时路径: {}", parentPath, sysAnswers.getPath());
}
// 确保 replyCount likeCount 不为 null
if (sysAnswers.getReplyCount() == null) {
sysAnswers.setReplyCount(0);
}
if (sysAnswers.getLikeCount() == null) {
sysAnswers.setLikeCount(0);
}
int result = sysAnswersMapper.insertSysAnswers(sysAnswers);
if (result > 0) {
// 增加问题的答案数量
sysQuestionsService.increaseAnswerCount(sysAnswers.getQuestionId());
// 更新path - 这里也需要修复
if (parentAnswer != null) {
String parentPath = parentAnswer.getPath();
if (parentPath == null || "0/".equals(parentPath)) {
// 修正父级路径
parentPath = "0/" + parentId + "/";
}
sysAnswers.setPath(parentPath + sysAnswers.getId() + "/");
sysAnswersMapper.updateSysAnswers(sysAnswers);
// 更新父级的回复数量
sysAnswersMapper.updateReplyCount(parentId);
log.info("更新回复路径 - 最终路径: {}", sysAnswers.getPath());
}
return result;
@ -125,6 +351,15 @@ public class SysAnswersServiceImpl implements ISysAnswersService
@Transactional
public int updateSysAnswers(SysAnswers sysAnswers)
{
// 不允许修改parentId和questionId
if (sysAnswers.getParentId() != null || sysAnswers.getQuestionId() != null) {
SysAnswers oldAnswer = sysAnswersMapper.selectSysAnswersById(sysAnswers.getId());
if (oldAnswer != null) {
sysAnswers.setParentId(oldAnswer.getParentId());
sysAnswers.setQuestionId(oldAnswer.getQuestionId());
}
}
return sysAnswersMapper.updateSysAnswers(sysAnswers);
}
@ -138,20 +373,17 @@ public class SysAnswersServiceImpl implements ISysAnswersService
@Transactional
public int deleteSysAnswersByIds(Long[] ids)
{
// 先获取所有要删除的答案记录对应的问题ID
int totalDeleted = 0;
for (Long id : ids) {
SysAnswers answer = sysAnswersMapper.selectSysAnswersById(id);
if (answer != null) {
// 减少问题的答案数量
sysQuestionsService.decreaseAnswerCount(answer.getQuestionId());
}
totalDeleted += deleteSysAnswersById(id);
}
return sysAnswersMapper.deleteSysAnswersByIds(ids);
return totalDeleted;
}
/**
* 删除答复信息
* 删除答复信息级联删除所有子回复
*
* @param id 答复主键
* @return 结果
@ -165,12 +397,27 @@ public class SysAnswersServiceImpl implements ISysAnswersService
throw new ServiceException("答复不存在");
}
Long questionId = answer.getQuestionId();
int result = sysAnswersMapper.deleteSysAnswersById(id);
// 获取所有后代ID
List<Long> descendantIds = sysAnswersMapper.selectDescendantIds(id);
List<Long> allIds = new ArrayList<>();
allIds.add(id);
allIds.addAll(descendantIds);
// 删除所有相关答复
int result = 0;
if (!allIds.isEmpty()) {
Long[] idsArray = allIds.toArray(new Long[0]);
result = sysAnswersMapper.deleteSysAnswersByIds(idsArray);
}
if (result > 0) {
// 减少问题的答案数量
sysQuestionsService.decreaseAnswerCount(questionId);
// 如果是顶级答复减少问题的答案数量
if (answer.getParentId() == 0) {
sysQuestionsService.decreaseAnswerCount(answer.getQuestionId());
} else {
// 如果是子回复更新父级的回复数量
sysAnswersMapper.updateReplyCount(answer.getParentId());
}
}
return result;
@ -186,10 +433,6 @@ public class SysAnswersServiceImpl implements ISysAnswersService
@Transactional
public int deleteSysAnswersByQuestionId(Long questionId)
{
// 获取该问题的所有答案
List<SysAnswers> answers = selectAnswersByQuestionId(questionId);
// 删除所有答案
int result = sysAnswersMapper.deleteAnswersByQuestionId(questionId);
if (result > 0) {
@ -211,7 +454,7 @@ public class SysAnswersServiceImpl implements ISysAnswersService
{
SysAnswers query = new SysAnswers();
query.setQuestionId(questionId);
return sysAnswersMapper.selectSysAnswersList(query);
return selectSysAnswersList(query);
}
/**
@ -229,6 +472,71 @@ public class SysAnswersServiceImpl implements ISysAnswersService
return (long) answers.size();
}
/**
* 点赞答复
*
* @param id 答复ID
* @return 结果
*/
@Override
@Transactional
public int likeAnswer(Long id)
{
// 这里需要实现点赞记录表这里简化为直接更新点赞数
// 实际项目中应该有一个 sys_answer_likes 表来记录用户点赞
// 检查用户是否已经点赞
if (SecurityUtils.getLoginUser() != null) {
String userId = String.valueOf(SecurityUtils.getUserId());
if (checkUserLike(id, userId)) {
throw new ServiceException("已经点赞过了");
}
// 记录用户点赞这里需要实现点赞记录表
// saveLikeRecord(id, userId);
}
return sysAnswersMapper.updateLikeCount(id, 1);
}
/**
* 取消点赞答复
*
* @param id 答复ID
* @return 结果
*/
@Override
@Transactional
public int unlikeAnswer(Long id)
{
if (SecurityUtils.getLoginUser() != null) {
String userId = String.valueOf(SecurityUtils.getUserId());
if (!checkUserLike(id, userId)) {
throw new ServiceException("还没有点赞");
}
// 删除点赞记录
// deleteLikeRecord(id, userId);
}
return sysAnswersMapper.updateLikeCount(id, -1);
}
/**
* 检查用户是否点赞
*
* @param id 答复ID
* @param userId 用户ID
* @return 是否点赞
*/
@Override
public boolean checkUserLike(Long id, String userId)
{
// 这里需要查询点赞记录表
// 暂时返回false实际项目中需要实现
return false;
}
/**
* 重置问题的答案数量为0私有方法
*

95
chenhai-system/src/main/java/com/chenhai/system/service/impl/SysMuhuUserServiceImpl.java

@ -0,0 +1,95 @@
package com.chenhai.system.service.impl;
import java.util.List;
import com.chenhai.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.chenhai.system.mapper.SysMuhuUserMapper;
import com.chenhai.system.domain.SysMuhuUser;
import com.chenhai.system.service.ISysMuhuUserService;
/**
* 牧户用户Service业务层处理
*
* @author ruoyi
* @date 2026-02-03
*/
@Service
public class SysMuhuUserServiceImpl implements ISysMuhuUserService
{
@Autowired
private SysMuhuUserMapper sysMuhuUserMapper;
/**
* 查询牧户用户
*
* @param userId 牧户用户主键
* @return 牧户用户
*/
@Override
public SysMuhuUser selectSysMuhuUserByUserId(Long userId)
{
return sysMuhuUserMapper.selectSysMuhuUserByUserId(userId);
}
/**
* 查询牧户用户列表
*
* @param sysMuhuUser 牧户用户
* @return 牧户用户
*/
@Override
public List<SysMuhuUser> selectSysMuhuUserList(SysMuhuUser sysMuhuUser)
{
return sysMuhuUserMapper.selectSysMuhuUserList(sysMuhuUser);
}
/**
* 新增牧户用户
*
* @param sysMuhuUser 牧户用户
* @return 结果
*/
@Override
public int insertSysMuhuUser(SysMuhuUser sysMuhuUser)
{
return sysMuhuUserMapper.insertSysMuhuUser(sysMuhuUser);
}
/**
* 修改牧户用户
*
* @param sysMuhuUser 牧户用户
* @return 结果
*/
@Override
public int updateSysMuhuUser(SysMuhuUser sysMuhuUser)
{
sysMuhuUser.setUpdateTime(DateUtils.getNowDate());
return sysMuhuUserMapper.updateSysMuhuUser(sysMuhuUser);
}
/**
* 批量删除牧户用户
*
* @param userIds 需要删除的牧户用户主键
* @return 结果
*/
@Override
public int deleteSysMuhuUserByUserIds(Long[] userIds)
{
return sysMuhuUserMapper.deleteSysMuhuUserByUserIds(userIds);
}
/**
* 删除牧户用户信息
*
* @param userId 牧户用户主键
* @return 结果
*/
@Override
public int deleteSysMuhuUserByUserId(Long userId)
{
return sysMuhuUserMapper.deleteSysMuhuUserByUserId(userId);
}
}

36
chenhai-system/src/main/java/com/chenhai/system/service/impl/SysQuestionsServiceImpl.java

@ -94,10 +94,10 @@ public class SysQuestionsServiceImpl implements ISysQuestionsService
throw new ServiceException("问题不存在");
}
// 检查权限只能修改自己的问题
if (!existingQuestion.getUserId().equals(userId)) {
throw new ServiceException("没有权限修改此问题");
}
// // 检查权限只能修改自己的问题
// if (!existingQuestion.getUserId().equals(userId)) {
// throw new ServiceException("没有权限修改此问题");
// }
// 设置更新时间
sysQuestions.setUpdatedAt(new Date());
@ -116,16 +116,16 @@ public class SysQuestionsServiceImpl implements ISysQuestionsService
@Transactional
public int deleteSysQuestionsByIds(Long[] ids, Long userId)
{
// 检查权限只能删除自己的问题
for (Long id : ids) {
SysQuestions question = sysQuestionsMapper.selectSysQuestionsById(id);
if (question == null) {
throw new ServiceException("问题不存在,ID: " + id);
}
if (!question.getUserId().equals(userId)) {
throw new ServiceException("没有权限删除此问题,ID: " + id);
}
}
// // 检查权限只能删除自己的问题
// for (Long id : ids) {
// SysQuestions question = sysQuestionsMapper.selectSysQuestionsById(id);
// if (question == null) {
// throw new ServiceException("问题不存在,ID: " + id);
// }
// if (!question.getUserId().equals(userId)) {
// throw new ServiceException("没有权限删除此问题,ID: " + id);
// }
// }
return sysQuestionsMapper.deleteSysQuestionsByIds(ids);
}
@ -147,10 +147,10 @@ public class SysQuestionsServiceImpl implements ISysQuestionsService
throw new ServiceException("问题不存在");
}
// 检查权限只能删除自己的问题
if (!question.getUserId().equals(userId)) {
throw new ServiceException("没有权限删除此问题");
}
// // 检查权限只能删除自己的问题
// if (!question.getUserId().equals(userId)) {
// throw new ServiceException("没有权限删除此问题");
// }
return sysQuestionsMapper.deleteSysQuestionsById(id);
}

181
chenhai-system/src/main/resources/mapper/muhu/MuhuExperienceShareMapper.xml

@ -0,0 +1,181 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chenhai.muhu.mapper.MuhuExperienceShareMapper">
<resultMap type="MuhuExperienceShare" id="MuhuExperienceShareResult">
<result property="id" column="id" />
<result property="userId" column="user_id" />
<result property="username" column="username" />
<result property="userNickname" column="user_nickname" />
<result property="userAvatar" column="user_avatar" />
<result property="title" column="title" />
<result property="content" column="content" />
<result property="summary" column="summary" />
<result property="category" column="category" />
<result property="tags" column="tags" />
<result property="coverImage" column="cover_image" />
<result property="images" column="images" />
<result property="attachments" column="attachments" />
<result property="status" column="status" />
<result property="auditRemark" column="audit_remark" />
<result property="auditTime" column="audit_time" />
<result property="auditUserId" column="audit_user_id" />
<result property="hasSensitive" column="has_sensitive" />
<result property="sensitiveWords" column="sensitive_words" />
<result property="isHandled" column="is_handled" />
<result property="viewCount" column="view_count" />
<result property="likeCount" column="like_count" />
<result property="collectCount" column="collect_count" />
<result property="shareCount" column="share_count" />
<result property="createdAt" column="created_at" />
<result property="updatedAt" column="updated_at" />
<result property="publishedAt" column="published_at" />
</resultMap>
<sql id="selectMuhuExperienceShareVo">
select id, user_id, username, user_nickname, user_avatar, title, content, summary, category, tags, cover_image, images, attachments, status, audit_remark, audit_time, audit_user_id, has_sensitive, sensitive_words, is_handled, view_count, like_count, collect_count, share_count, created_at, updated_at, published_at from muhu_experience_share
</sql>
<select id="selectMuhuExperienceShareList" parameterType="MuhuExperienceShare" resultMap="MuhuExperienceShareResult">
<include refid="selectMuhuExperienceShareVo"/>
<where>
<if test="userId != null "> and user_id = #{userId}</if>
<if test="username != null and username != ''"> and username like concat('%', #{username}, '%')</if>
<if test="userNickname != null and userNickname != ''"> and user_nickname like concat('%', #{userNickname}, '%')</if>
<if test="userAvatar != null and userAvatar != ''"> and user_avatar = #{userAvatar}</if>
<if test="title != null and title != ''"> and title = #{title}</if>
<if test="content != null and content != ''"> and content = #{content}</if>
<if test="summary != null and summary != ''"> and summary = #{summary}</if>
<if test="category != null and category != ''"> and category = #{category}</if>
<if test="tags != null and tags != ''"> and tags = #{tags}</if>
<if test="coverImage != null and coverImage != ''"> and cover_image = #{coverImage}</if>
<if test="images != null and images != ''"> and images = #{images}</if>
<if test="attachments != null and attachments != ''"> and attachments = #{attachments}</if>
<if test="status != null "> and status = #{status}</if>
<if test="auditRemark != null and auditRemark != ''"> and audit_remark = #{auditRemark}</if>
<if test="auditTime != null "> and audit_time = #{auditTime}</if>
<if test="auditUserId != null "> and audit_user_id = #{auditUserId}</if>
<if test="hasSensitive != null "> and has_sensitive = #{hasSensitive}</if>
<if test="sensitiveWords != null and sensitiveWords != ''"> and sensitive_words = #{sensitiveWords}</if>
<if test="isHandled != null "> and is_handled = #{isHandled}</if>
<if test="viewCount != null "> and view_count = #{viewCount}</if>
<if test="likeCount != null "> and like_count = #{likeCount}</if>
<if test="collectCount != null "> and collect_count = #{collectCount}</if>
<if test="shareCount != null "> and share_count = #{shareCount}</if>
<if test="createdAt != null "> and created_at = #{createdAt}</if>
<if test="updatedAt != null "> and updated_at = #{updatedAt}</if>
<if test="publishedAt != null "> and published_at = #{publishedAt}</if>
</where>
</select>
<select id="selectMuhuExperienceShareById" parameterType="Long" resultMap="MuhuExperienceShareResult">
<include refid="selectMuhuExperienceShareVo"/>
where id = #{id}
</select>
<insert id="insertMuhuExperienceShare" parameterType="MuhuExperienceShare" useGeneratedKeys="true" keyProperty="id">
insert into muhu_experience_share
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="userId != null">user_id,</if>
<if test="username != null">username,</if>
<if test="userNickname != null">user_nickname,</if>
<if test="userAvatar != null">user_avatar,</if>
<if test="title != null and title != ''">title,</if>
<if test="content != null and content != ''">content,</if>
<if test="summary != null">summary,</if>
<if test="category != null">category,</if>
<if test="tags != null">tags,</if>
<if test="coverImage != null">cover_image,</if>
<if test="images != null">images,</if>
<if test="attachments != null">attachments,</if>
<if test="status != null">status,</if>
<if test="auditRemark != null">audit_remark,</if>
<if test="auditTime != null">audit_time,</if>
<if test="auditUserId != null">audit_user_id,</if>
<if test="hasSensitive != null">has_sensitive,</if>
<if test="sensitiveWords != null">sensitive_words,</if>
<if test="isHandled != null">is_handled,</if>
<if test="viewCount != null">view_count,</if>
<if test="likeCount != null">like_count,</if>
<if test="collectCount != null">collect_count,</if>
<if test="shareCount != null">share_count,</if>
<if test="createdAt != null">created_at,</if>
<if test="updatedAt != null">updated_at,</if>
<if test="publishedAt != null">published_at,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null">#{userId},</if>
<if test="username != null">#{username},</if>
<if test="userNickname != null">#{userNickname},</if>
<if test="userAvatar != null">#{userAvatar},</if>
<if test="title != null and title != ''">#{title},</if>
<if test="content != null and content != ''">#{content},</if>
<if test="summary != null">#{summary},</if>
<if test="category != null">#{category},</if>
<if test="tags != null">#{tags},</if>
<if test="coverImage != null">#{coverImage},</if>
<if test="images != null">#{images},</if>
<if test="attachments != null">#{attachments},</if>
<if test="status != null">#{status},</if>
<if test="auditRemark != null">#{auditRemark},</if>
<if test="auditTime != null">#{auditTime},</if>
<if test="auditUserId != null">#{auditUserId},</if>
<if test="hasSensitive != null">#{hasSensitive},</if>
<if test="sensitiveWords != null">#{sensitiveWords},</if>
<if test="isHandled != null">#{isHandled},</if>
<if test="viewCount != null">#{viewCount},</if>
<if test="likeCount != null">#{likeCount},</if>
<if test="collectCount != null">#{collectCount},</if>
<if test="shareCount != null">#{shareCount},</if>
<if test="createdAt != null">#{createdAt},</if>
<if test="updatedAt != null">#{updatedAt},</if>
<if test="publishedAt != null">#{publishedAt},</if>
</trim>
</insert>
<update id="updateMuhuExperienceShare" parameterType="MuhuExperienceShare">
update muhu_experience_share
<trim prefix="SET" suffixOverrides=",">
<if test="userId != null">user_id = #{userId},</if>
<if test="username != null">username = #{username},</if>
<if test="userNickname != null">user_nickname = #{userNickname},</if>
<if test="userAvatar != null">user_avatar = #{userAvatar},</if>
<if test="title != null and title != ''">title = #{title},</if>
<if test="content != null and content != ''">content = #{content},</if>
<if test="summary != null">summary = #{summary},</if>
<if test="category != null">category = #{category},</if>
<if test="tags != null">tags = #{tags},</if>
<if test="coverImage != null">cover_image = #{coverImage},</if>
<if test="images != null">images = #{images},</if>
<if test="attachments != null">attachments = #{attachments},</if>
<if test="status != null">status = #{status},</if>
<if test="auditRemark != null">audit_remark = #{auditRemark},</if>
<if test="auditTime != null">audit_time = #{auditTime},</if>
<if test="auditUserId != null">audit_user_id = #{auditUserId},</if>
<if test="hasSensitive != null">has_sensitive = #{hasSensitive},</if>
<if test="sensitiveWords != null">sensitive_words = #{sensitiveWords},</if>
<if test="isHandled != null">is_handled = #{isHandled},</if>
<if test="viewCount != null">view_count = #{viewCount},</if>
<if test="likeCount != null">like_count = #{likeCount},</if>
<if test="collectCount != null">collect_count = #{collectCount},</if>
<if test="shareCount != null">share_count = #{shareCount},</if>
<if test="createdAt != null">created_at = #{createdAt},</if>
<if test="updatedAt != null">updated_at = #{updatedAt},</if>
<if test="publishedAt != null">published_at = #{publishedAt},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteMuhuExperienceShareById" parameterType="Long">
delete from muhu_experience_share where id = #{id}
</delete>
<delete id="deleteMuhuExperienceShareByIds" parameterType="String">
delete from muhu_experience_share where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

94
chenhai-system/src/main/resources/mapper/system/SysAnswerAgainMapper.xml

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chenhai.system.mapper.SysAnswerAgainMapper">
<resultMap type="SysAnswerAgain" id="SysAnswerAgainResult">
<result property="id" column="id" />
<result property="answerId" column="answer_id" />
<result property="userId" column="user_id" />
<result property="username" column="username" />
<result property="content" column="content" />
<result property="createdAt" column="created_at" />
<result property="updatedAt" column="updated_at" />
<result property="nickName" column="nick_name" />
<result property="avatar" column="avatar" />
</resultMap>
<sql id="selectSysAnswerAgainVo">
select
a.id,
a.answer_id,
a.user_id,
a.username,
u.nick_name,
u.avatar,
a.content,
a.created_at,
a.updated_at
from sys_answer_again a
left join sys_user u on a.user_id = u.user_id
</sql>
<select id="selectSysAnswerAgainList" parameterType="SysAnswerAgain" resultMap="SysAnswerAgainResult">
<include refid="selectSysAnswerAgainVo"/>
<where>
<if test="answerId != null "> and answer_id = #{answerId}</if>
<if test="userId != null and userId != ''"> and user_id = #{userId}</if>
<if test="username != null and username != ''"> and username like concat('%', #{username}, '%')</if>
<if test="content != null and content != ''"> and content = #{content}</if>
<if test="createdAt != null "> and created_at = #{createdAt}</if>
<if test="updatedAt != null "> and updated_at = #{updatedAt}</if>
</where>
</select>
<select id="selectSysAnswerAgainById" parameterType="Long" resultMap="SysAnswerAgainResult">
<include refid="selectSysAnswerAgainVo"/>
where id = #{id}
</select>
<insert id="insertSysAnswerAgain" parameterType="SysAnswerAgain" useGeneratedKeys="true" keyProperty="id">
insert into sys_answer_again
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="answerId != null">answer_id,</if>
<if test="userId != null and userId != ''">user_id,</if>
<if test="username != null and username != ''">username,</if>
<if test="content != null and content != ''">content,</if>
<if test="createdAt != null">created_at,</if>
<if test="updatedAt != null">updated_at,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="answerId != null">#{answerId},</if>
<if test="userId != null and userId != ''">#{userId},</if>
<if test="username != null and username != ''">#{username},</if>
<if test="content != null and content != ''">#{content},</if>
<if test="createdAt != null">#{createdAt},</if>
<if test="updatedAt != null">#{updatedAt},</if>
</trim>
</insert>
<update id="updateSysAnswerAgain" parameterType="SysAnswerAgain">
update sys_answer_again
<trim prefix="SET" suffixOverrides=",">
<if test="answerId != null">answer_id = #{answerId},</if>
<if test="userId != null and userId != ''">user_id = #{userId},</if>
<if test="username != null and username != ''">username = #{username},</if>
<if test="content != null and content != ''">content = #{content},</if>
<if test="createdAt != null">created_at = #{createdAt},</if>
<if test="updatedAt != null">updated_at = #{updatedAt},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteSysAnswerAgainById" parameterType="Long">
delete from sys_answer_again where id = #{id}
</delete>
<delete id="deleteSysAnswerAgainByIds" parameterType="String">
delete from sys_answer_again where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

96
chenhai-system/src/main/resources/mapper/system/SysAnswersMapper.xml

@ -14,6 +14,17 @@
<result property="content" column="content" />
<result property="createdAt" column="created_at" />
<result property="updatedAt" column="updated_at" />
<result property="images" column="images" />
<result property="parentId" column="parent_id" />
<result property="level" column="level" />
<result property="path" column="path" />
<result property="replyCount" column="reply_count" />
<result property="likeCount" column="like_count" />
<!-- &lt;!&ndash; 添加新字段 &ndash;&gt;-->
<!-- <result property="latestReplyContent" column="latest_reply_content" />-->
<!-- <result property="latestReplyNickName" column="latest_reply_nick_name" />-->
<!-- <result property="latestReplyAvatar" column="latest_reply_avatar" />-->
<!-- <result property="latestReplyTime" column="latest_reply_time" />-->
</resultMap>
<sql id="selectSysAnswersVo">
@ -26,7 +37,13 @@
u.avatar,
a.content,
a.created_at,
a.updated_at
a.updated_at,
a.images,
a.parent_id,
a.level,
a.path,
a.reply_count,
a.like_count
from sys_answers a
left join sys_user u on a.user_id = u.user_id
</sql>
@ -39,6 +56,8 @@
<if test="username != null and username != ''"> and a.username like concat('%', #{username}, '%')</if>
<if test="nickName != null and nickName != ''"> and u.nick_name like concat('%', #{nickName}, '%')</if>
<if test="content != null and content != ''"> and a.content like concat('%', #{content}, '%')</if>
<if test="parentId != null "> and a.parent_id = #{parentId}</if>
<if test="level != null "> and a.level = #{level}</if>
<if test="createdAt != null "> and date(a.created_at) = date(#{createdAt})</if>
<if test="params.beginTime != null and params.beginTime != ''">
and date_format(a.created_at,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
@ -46,6 +65,7 @@
<if test="params.endTime != null and params.endTime != ''">
and date_format(a.created_at,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
</if>
<if test="images != null and images != ''"> and a.images like concat('%', #{images}, '%')</if>
</where>
order by a.created_at desc
</select>
@ -55,24 +75,53 @@
where a.id = #{id}
</select>
<!-- 查询顶级答复列表(parent_id = 0) -->
<select id="selectRootAnswers" parameterType="Long" resultMap="SysAnswersResult">
<include refid="selectSysAnswersVo"/>
where a.question_id = #{questionId} and a.parent_id = 0
order by a.created_at desc
</select>
<!-- 查询子回复列表 -->
<select id="selectChildrenAnswers" parameterType="Long" resultMap="SysAnswersResult">
<include refid="selectSysAnswersVo"/>
where a.parent_id = #{parentId}
order by a.created_at asc
</select>
<!-- 查询答复的完整树结构 -->
<select id="selectAnswerTree" parameterType="Long" resultMap="SysAnswersResult">
<include refid="selectSysAnswersVo"/>
where a.question_id = #{questionId}
order by a.path, a.created_at asc
</select>
<insert id="insertSysAnswers" parameterType="SysAnswers" useGeneratedKeys="true" keyProperty="id">
insert into sys_answers
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="questionId != null">question_id,</if>
<if test="userId != null and userId != ''">user_id,</if>
<if test="username != null and username != ''">username,</if>
<if test="nickName != null and nickName != ''">nick_name,</if>
<if test="avatar != null and avatar != ''">avatar,</if>
<if test="content != null and content != ''">content,</if>
<if test="images != null and images != ''">images,</if>
<if test="parentId != null">parent_id,</if>
<if test="level != null">level,</if>
<if test="path != null and path != ''">path,</if>
<if test="replyCount != null">reply_count,</if>
<if test="likeCount != null">like_count,</if>
created_at,
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="questionId != null">#{questionId},</if>
<if test="userId != null and userId != ''">#{userId},</if>
<if test="username != null and username != ''">#{username},</if>
<if test="nickName != null and nickName != ''">#{nickName},</if>
<if test="avatar != null and avatar != ''">#{avatar},</if>
<if test="content != null and content != ''">#{content},</if>
<if test="images != null and images != ''">#{images},</if>
<if test="parentId != null">#{parentId},</if>
<if test="level != null">#{level},</if>
<if test="path != null and path != ''">#{path},</if>
<if test="replyCount != null">#{replyCount},</if>
<if test="likeCount != null">#{likeCount},</if>
sysdate(),
</trim>
</insert>
@ -83,14 +132,42 @@
<if test="questionId != null">question_id = #{questionId},</if>
<if test="userId != null and userId != ''">user_id = #{userId},</if>
<if test="username != null and username != ''">username = #{username},</if>
<if test="nickName != null and nickName != ''">nick_name = #{nickName},</if>
<if test="avatar != null and avatar != ''">avatar = #{avatar},</if>
<!-- <if test="nickName != null and nickName != ''">nick_name = #{nickName},</if>-->
<!-- <if test="avatar != null and avatar != ''">avatar = #{avatar},</if>-->
<if test="content != null and content != ''">content = #{content},</if>
<if test="images != null and images != ''">images = #{images},</if>
<if test="parentId != null">parent_id = #{parentId},</if>
<if test="level != null">level = #{level},</if>
<if test="path != null and path != ''">path = #{path},</if>
<if test="replyCount != null">reply_count = #{replyCount},</if>
<if test="likeCount != null">like_count = #{likeCount},</if>
updated_at = sysdate(),
</trim>
where id = #{id}
</update>
<!-- 更新答复的回复数量 -->
<update id="updateReplyCount" parameterType="Long">
update sys_answers
set reply_count = (
select cnt from (
select count(*) as cnt
from sys_answers
where parent_id = #{id}
) as temp
),
updated_at = sysdate()
where id = #{id}
</update>
<!-- 更新点赞数量 -->
<update id="updateLikeCount">
update sys_answers
set like_count = like_count + #{increment},
updated_at = sysdate()
where id = #{id}
</update>
<delete id="deleteSysAnswersById" parameterType="Long">
delete from sys_answers where id = #{id}
</delete>
@ -109,4 +186,9 @@
<select id="countAnswersByQuestionId" parameterType="Long" resultType="Long">
select count(*) from sys_answers where question_id = #{questionId}
</select>
<!-- 查询某个答复的所有后代ID(用于级联删除) -->
<select id="selectDescendantIds" parameterType="Long" resultType="Long">
select id from sys_answers where path like concat('%/', #{id}, '/%')
</select>
</mapper>

117
chenhai-system/src/main/resources/mapper/system/SysMuhuUserMapper.xml

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chenhai.system.mapper.SysMuhuUserMapper">
<resultMap type="SysMuhuUser" id="SysMuhuUserResult">
<result property="userId" column="user_id" />
<result property="realName" column="real_name" />
<result property="idCard" column="id_card" />
<result property="idCardFront" column="id_card_front" />
<result property="idCardBack" column="id_card_back" />
<result property="faceImage" column="face_image" />
<result property="authStatus" column="auth_status" />
<result property="authTime" column="auth_time" />
<result property="authFailReason" column="auth_fail_reason" />
<result property="securityQuestion" column="security_question" />
<result property="securityAnswer" column="security_answer" />
<result property="lastLoginTime" column="last_login_time" />
<result property="lastLoginIp" column="last_login_ip" />
<result property="updateTime" column="update_time" />
</resultMap>
<sql id="selectSysMuhuUserVo">
select user_id, real_name, id_card, id_card_front, id_card_back, face_image, auth_status, auth_time, auth_fail_reason, security_question, security_answer, last_login_time, last_login_ip, update_time from sys_muhu_user
</sql>
<select id="selectSysMuhuUserList" parameterType="SysMuhuUser" resultMap="SysMuhuUserResult">
<include refid="selectSysMuhuUserVo"/>
<where>
<if test="realName != null and realName != ''"> and real_name like concat('%', #{realName}, '%')</if>
<if test="idCard != null and idCard != ''"> and id_card = #{idCard}</if>
<if test="idCardFront != null and idCardFront != ''"> and id_card_front = #{idCardFront}</if>
<if test="idCardBack != null and idCardBack != ''"> and id_card_back = #{idCardBack}</if>
<if test="faceImage != null and faceImage != ''"> and face_image = #{faceImage}</if>
<if test="authStatus != null and authStatus != ''"> and auth_status = #{authStatus}</if>
<if test="authTime != null "> and auth_time = #{authTime}</if>
<if test="authFailReason != null and authFailReason != ''"> and auth_fail_reason = #{authFailReason}</if>
<if test="securityQuestion != null and securityQuestion != ''"> and security_question = #{securityQuestion}</if>
<if test="securityAnswer != null and securityAnswer != ''"> and security_answer = #{securityAnswer}</if>
<if test="lastLoginTime != null "> and last_login_time = #{lastLoginTime}</if>
<if test="lastLoginIp != null and lastLoginIp != ''"> and last_login_ip = #{lastLoginIp}</if>
</where>
</select>
<select id="selectSysMuhuUserByUserId" parameterType="Long" resultMap="SysMuhuUserResult">
<include refid="selectSysMuhuUserVo"/>
where user_id = #{userId}
</select>
<insert id="insertSysMuhuUser" parameterType="SysMuhuUser">
insert into sys_muhu_user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="userId != null">user_id,</if>
<if test="realName != null">real_name,</if>
<if test="idCard != null">id_card,</if>
<if test="idCardFront != null">id_card_front,</if>
<if test="idCardBack != null">id_card_back,</if>
<if test="faceImage != null">face_image,</if>
<if test="authStatus != null">auth_status,</if>
<if test="authTime != null">auth_time,</if>
<if test="authFailReason != null">auth_fail_reason,</if>
<if test="securityQuestion != null">security_question,</if>
<if test="securityAnswer != null">security_answer,</if>
<if test="lastLoginTime != null">last_login_time,</if>
<if test="lastLoginIp != null">last_login_ip,</if>
<if test="updateTime != null">update_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null">#{userId},</if>
<if test="realName != null">#{realName},</if>
<if test="idCard != null">#{idCard},</if>
<if test="idCardFront != null">#{idCardFront},</if>
<if test="idCardBack != null">#{idCardBack},</if>
<if test="faceImage != null">#{faceImage},</if>
<if test="authStatus != null">#{authStatus},</if>
<if test="authTime != null">#{authTime},</if>
<if test="authFailReason != null">#{authFailReason},</if>
<if test="securityQuestion != null">#{securityQuestion},</if>
<if test="securityAnswer != null">#{securityAnswer},</if>
<if test="lastLoginTime != null">#{lastLoginTime},</if>
<if test="lastLoginIp != null">#{lastLoginIp},</if>
<if test="updateTime != null">#{updateTime},</if>
</trim>
</insert>
<update id="updateSysMuhuUser" parameterType="SysMuhuUser">
update sys_muhu_user
<trim prefix="SET" suffixOverrides=",">
<if test="realName != null">real_name = #{realName},</if>
<if test="idCard != null">id_card = #{idCard},</if>
<if test="idCardFront != null">id_card_front = #{idCardFront},</if>
<if test="idCardBack != null">id_card_back = #{idCardBack},</if>
<if test="faceImage != null">face_image = #{faceImage},</if>
<if test="authStatus != null">auth_status = #{authStatus},</if>
<if test="authTime != null">auth_time = #{authTime},</if>
<if test="authFailReason != null">auth_fail_reason = #{authFailReason},</if>
<if test="securityQuestion != null">security_question = #{securityQuestion},</if>
<if test="securityAnswer != null">security_answer = #{securityAnswer},</if>
<if test="lastLoginTime != null">last_login_time = #{lastLoginTime},</if>
<if test="lastLoginIp != null">last_login_ip = #{lastLoginIp},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
</trim>
where user_id = #{userId}
</update>
<delete id="deleteSysMuhuUserByUserId" parameterType="Long">
delete from sys_muhu_user where user_id = #{userId}
</delete>
<delete id="deleteSysMuhuUserByUserIds" parameterType="String">
delete from sys_muhu_user where user_id in
<foreach item="userId" collection="array" open="(" separator="," close=")">
#{userId}
</foreach>
</delete>
</mapper>

22
chenhai-system/src/main/resources/mapper/system/SysQuestionsMapper.xml

@ -16,6 +16,8 @@
<result property="updatedAt" column="updated_at" />
<result property="nickName" column="nick_name" />
<result property="avatar" column="avatar" />
<result property="images" column="images" />
<result property="viewCount" column="view_count" />
</resultMap>
<sql id="selectSysQuestionsVo">
@ -30,7 +32,9 @@
q.tags,
q.answer_count,
q.created_at,
q.updated_at
q.updated_at,
q.images,
q.view_count
FROM sys_questions q
LEFT JOIN sys_user u ON q.user_id = u.user_id
</sql>
@ -47,6 +51,18 @@
<if test="createdAt != null "> and created_at = #{createdAt}</if>
<if test="updatedAt != null "> and updated_at = #{updatedAt}</if>
<if test="nickName != null and nickName != ''"> and u.nick_name like concat('%', #{nickName}, '%')</if>
<if test="images != null and images != ''"> and images like concat('%', #{images}, '%')</if>
<!-- 新增搜索关键字逻辑 -->
<if test="searchKey != null and searchKey != ''">
and (
q.title like concat('%', #{searchKey}, '%')
or q.content like concat('%', #{searchKey}, '%')
or q.tags like concat('%', #{searchKey}, '%')
or u.nick_name like concat('%', #{searchKey}, '%')
or u.user_name like concat('%', #{searchKey}, '%')
)
</if>
<if test="viewCount != null "> and view_count = #{viewCount}</if>
</where>
order by q.created_at desc
</select>
@ -67,6 +83,7 @@
<if test="answerCount != null">answer_count,</if>
<if test="createdAt != null">created_at,</if>
<if test="updatedAt != null">updated_at,</if>
<if test="images != null and images != ''">images,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null and userId != ''">#{userId},</if>
@ -77,6 +94,7 @@
<if test="answerCount != null">#{answerCount},</if>
<if test="createdAt != null">#{createdAt},</if>
<if test="updatedAt != null">#{updatedAt},</if>
<if test="images != null and images != ''">#{images},</if>
</trim>
</insert>
@ -91,6 +109,8 @@
<if test="answerCount != null">answer_count = #{answerCount},</if>
<if test="createdAt != null">created_at = #{createdAt},</if>
<if test="updatedAt != null">updated_at = #{updatedAt},</if>
<if test="images != null and images != ''">images = #{images},</if>
<if test="viewCount != null">view_count = #{viewCount},</if>
</trim>
where id = #{id}
</update>

44
chenhai-ui/src/api/muhu/share.js

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询经验分享列表
export function listShare(query) {
return request({
url: '/muhu/share/list',
method: 'get',
params: query
})
}
// 查询经验分享详细
export function getShare(id) {
return request({
url: '/muhu/share/' + id,
method: 'get'
})
}
// 新增经验分享
export function addShare(data) {
return request({
url: '/muhu/share',
method: 'post',
data: data
})
}
// 修改经验分享
export function updateShare(data) {
return request({
url: '/muhu/share',
method: 'put',
data: data
})
}
// 删除经验分享
export function delShare(id) {
return request({
url: '/muhu/share/' + id,
method: 'delete'
})
}

44
chenhai-ui/src/api/system/again.js

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询回复列表
export function listAgain(query) {
return request({
url: '/system/again/list',
method: 'get',
params: query
})
}
// 查询回复详细
export function getAgain(id) {
return request({
url: '/system/again/' + id,
method: 'get'
})
}
// 新增回复
export function addAgain(data) {
return request({
url: '/system/again',
method: 'post',
data: data
})
}
// 修改回复
export function updateAgain(data) {
return request({
url: '/system/again',
method: 'put',
data: data
})
}
// 删除回复
export function delAgain(id) {
return request({
url: '/system/again/' + id,
method: 'delete'
})
}

61
chenhai-ui/src/api/system/answers.js

@ -42,3 +42,64 @@ export function delAnswers(id) {
method: 'delete'
})
}
// 以下是新增的回复相关API
// 查询顶级答复列表
export function listRootAnswers(questionId, query) {
return request({
url: `/system/answers/question/${questionId}/roots`,
method: 'get',
params: query
})
}
// 查询答复树形结构
export function getAnswerTree(questionId) {
return request({
url: `/system/answers/question/${questionId}/tree`,
method: 'get'
})
}
// 查询子回复列表
export function listChildrenAnswers(parentId, query) {
return request({
url: `/system/answers/parent/${parentId}/children`,
method: 'get',
params: query
})
}
// 新增回复
export function addReply(data) {
return request({
url: '/system/answers/reply',
method: 'post',
data: data
})
}
// 点赞答复
export function likeAnswer(id) {
return request({
url: `/system/answers/${id}/like`,
method: 'post'
})
}
// 取消点赞答复
export function unlikeAnswer(id) {
return request({
url: `/system/answers/${id}/unlike`,
method: 'post'
})
}
// 获取问题的答案数量
export function countAnswersByQuestionId(questionId) {
return request({
url: `/system/answers/count/${questionId}`,
method: 'get'
})
}

44
chenhai-ui/src/api/system/muhuuser.js

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询牧户用户列表
export function listMuhuuser(query) {
return request({
url: '/system/muhuuser/list',
method: 'get',
params: query
})
}
// 查询牧户用户详细
export function getMuhuuser(userId) {
return request({
url: '/system/muhuuser/' + userId,
method: 'get'
})
}
// 新增牧户用户
export function addMuhuuser(data) {
return request({
url: '/system/muhuuser',
method: 'post',
data: data
})
}
// 修改牧户用户
export function updateMuhuuser(data) {
return request({
url: '/system/muhuuser',
method: 'put',
data: data
})
}
// 删除牧户用户
export function delMuhuuser(userId) {
return request({
url: '/system/muhuuser/' + userId,
method: 'delete'
})
}

66
chenhai-ui/src/router/index.js

@ -30,73 +30,7 @@ import Layout from '@/layout'
// 公共路由
export const constantRoutes = [
// 专家管理路由
{
path: '/vet/experts',
component: Layout,
children: [
{
path: 'index',
component: () => import('@/views/vet/experts/index'),
name: 'VetExperts',
meta: {
title: '专家管理',
icon: 'user',
permissions: ['vet:experts:list']
}
}
]
},
// 聊天页面路由
{
path: '/chat',
component: Layout,
children: [
{
path: 'index',
component: () => import('@/views/chat/index'),
name: 'ChatIndex',
meta: {
title: '在线咨询',
icon: 'message'
}
}
]
},
// 在路由文件中添加以下路由
{
path: '/vet/experts',
component: Layout,
redirect: '/expert/chat/list',
meta: {
title: '专家中心',
icon: 'expert',
roles: ['vetnotshenhe', 'admin']
},
children: [
{
path: 'chat/list',
component: () => import('@/views/chat/expert'), // 专家会话列表
name: 'ExpertChatList',
meta: {
title: '我的咨询',
icon: 'list',
roles: ['vetnotshenhe', 'admin']
}
},
{
path: 'chat/index',
component: () => import('@/views/chat/expert'), // 专家聊天页面
name: 'ExpertChat',
meta: {
title: '专家聊天',
icon: 'message',
roles: ['vetnotshenhe', 'admin']
}
}
]
},
{
path: '/redirect',

440
chenhai-ui/src/views/muhu/share/index.vue

@ -0,0 +1,440 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['muhu:share:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['muhu:share:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['muhu:share:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['muhu:share:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="shareList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="发布用户名" align="center" prop="username" />
<el-table-column label="用户昵称" align="center" prop="userNickname" />
<el-table-column label="用户头像" align="center" prop="userAvatar" />
<el-table-column label="文章标题" align="center" prop="title" />
<el-table-column label="文章内容" align="center" prop="content" />
<el-table-column label="文章摘要" align="center" prop="summary" />
<el-table-column label="分类" align="center" prop="category" />
<el-table-column label="标签" align="center" prop="tags" />
<el-table-column label="封面图片" align="center" prop="coverImage" width="100">
<template slot-scope="scope">
<image-preview :src="scope.row.coverImage" :width="50" :height="50"/>
</template>
</el-table-column>
<el-table-column label="文章图片" align="center" prop="images" />
<el-table-column label="附件" align="center" prop="attachments" />
<el-table-column label="状态" align="center" prop="status" />
<el-table-column label="审核备注" align="center" prop="auditRemark" />
<el-table-column label="审核时间" align="center" prop="auditTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.auditTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="审核人ID" align="center" prop="auditUserId" />
<el-table-column label="是否包含敏感词" align="center" prop="hasSensitive" />
<el-table-column label="敏感词列表" align="center" prop="sensitiveWords" />
<el-table-column label="是否已处理敏感词" align="center" prop="isHandled" />
<el-table-column label="浏览次数" align="center" prop="viewCount" />
<el-table-column label="点赞数" align="center" prop="likeCount" />
<el-table-column label="收藏数" align="center" prop="collectCount" />
<el-table-column label="分享数" align="center" prop="shareCount" />
<el-table-column label="创建时间" align="center" prop="createdAt" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createdAt, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="更新时间" align="center" prop="updatedAt" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.updatedAt, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="发布时间" align="center" prop="publishedAt" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.publishedAt, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['muhu:share:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['muhu:share:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改经验分享对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="发布用户ID" prop="userId">
<el-input v-model="form.userId" placeholder="请输入发布用户ID" />
</el-form-item>
<el-form-item label="发布用户名" prop="username">
<el-input v-model="form.username" placeholder="请输入发布用户名" />
</el-form-item>
<el-form-item label="用户昵称" prop="userNickname">
<el-input v-model="form.userNickname" placeholder="请输入用户昵称" />
</el-form-item>
<el-form-item label="用户头像" prop="userAvatar">
<el-input v-model="form.userAvatar" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="文章标题" prop="title">
<el-input v-model="form.title" placeholder="请输入文章标题" />
</el-form-item>
<el-form-item label="文章内容">
<editor v-model="form.content" :min-height="192"/>
</el-form-item>
<el-form-item label="文章摘要" prop="summary">
<el-input v-model="form.summary" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="分类" prop="category">
<el-input v-model="form.category" placeholder="请输入分类" />
</el-form-item>
<el-form-item label="标签" prop="tags">
<el-input v-model="form.tags" placeholder="请输入标签" />
</el-form-item>
<el-form-item label="封面图片" prop="coverImage">
<image-upload v-model="form.coverImage"/>
</el-form-item>
<el-form-item label="文章图片" prop="images">
<el-input v-model="form.images" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="附件" prop="attachments">
<el-input v-model="form.attachments" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="审核备注" prop="auditRemark">
<el-input v-model="form.auditRemark" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="审核时间" prop="auditTime">
<el-date-picker clearable
v-model="form.auditTime"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择审核时间">
</el-date-picker>
</el-form-item>
<el-form-item label="审核人ID" prop="auditUserId">
<el-input v-model="form.auditUserId" placeholder="请输入审核人ID" />
</el-form-item>
<el-form-item label="是否包含敏感词" prop="hasSensitive">
<el-input v-model="form.hasSensitive" placeholder="请输入是否包含敏感词" />
</el-form-item>
<el-form-item label="敏感词列表" prop="sensitiveWords">
<el-input v-model="form.sensitiveWords" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="是否已处理敏感词" prop="isHandled">
<el-input v-model="form.isHandled" placeholder="请输入是否已处理敏感词" />
</el-form-item>
<el-form-item label="浏览次数" prop="viewCount">
<el-input v-model="form.viewCount" placeholder="请输入浏览次数" />
</el-form-item>
<el-form-item label="点赞数" prop="likeCount">
<el-input v-model="form.likeCount" placeholder="请输入点赞数" />
</el-form-item>
<el-form-item label="收藏数" prop="collectCount">
<el-input v-model="form.collectCount" placeholder="请输入收藏数" />
</el-form-item>
<el-form-item label="分享数" prop="shareCount">
<el-input v-model="form.shareCount" placeholder="请输入分享数" />
</el-form-item>
<el-form-item label="创建时间" prop="createdAt">
<el-date-picker clearable
v-model="form.createdAt"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择创建时间">
</el-date-picker>
</el-form-item>
<el-form-item label="更新时间" prop="updatedAt">
<el-date-picker clearable
v-model="form.updatedAt"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择更新时间">
</el-date-picker>
</el-form-item>
<el-form-item label="发布时间" prop="publishedAt">
<el-date-picker clearable
v-model="form.publishedAt"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择发布时间">
</el-date-picker>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listShare, getShare, delShare, addShare, updateShare } from "@/api/muhu/share"
export default {
name: "Share",
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
shareList: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
userId: null,
username: null,
userNickname: null,
userAvatar: null,
title: null,
content: null,
summary: null,
category: null,
tags: null,
coverImage: null,
images: null,
attachments: null,
status: null,
auditRemark: null,
auditTime: null,
auditUserId: null,
hasSensitive: null,
sensitiveWords: null,
isHandled: null,
viewCount: null,
likeCount: null,
collectCount: null,
shareCount: null,
createdAt: null,
updatedAt: null,
publishedAt: null
},
//
form: {},
//
rules: {
userId: [
{ required: true, message: "发布用户ID不能为空", trigger: "blur" }
],
title: [
{ required: true, message: "文章标题不能为空", trigger: "blur" }
],
content: [
{ required: true, message: "文章内容不能为空", trigger: "blur" }
],
status: [
{ required: true, message: "状态不能为空", trigger: "change" }
],
createdAt: [
{ required: true, message: "创建时间不能为空", trigger: "blur" }
],
updatedAt: [
{ required: true, message: "更新时间不能为空", trigger: "blur" }
],
}
}
},
created() {
this.getList()
},
methods: {
/** 查询经验分享列表 */
getList() {
this.loading = true
listShare(this.queryParams).then(response => {
this.shareList = response.rows
this.total = response.total
this.loading = false
})
},
//
cancel() {
this.open = false
this.reset()
},
//
reset() {
this.form = {
id: null,
userId: null,
username: null,
userNickname: null,
userAvatar: null,
title: null,
content: null,
summary: null,
category: null,
tags: null,
coverImage: null,
images: null,
attachments: null,
status: null,
auditRemark: null,
auditTime: null,
auditUserId: null,
hasSensitive: null,
sensitiveWords: null,
isHandled: null,
viewCount: null,
likeCount: null,
collectCount: null,
shareCount: null,
createdAt: null,
updatedAt: null,
publishedAt: null
}
this.resetForm("form")
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm")
this.handleQuery()
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
this.open = true
this.title = "添加经验分享"
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
const id = row.id || this.ids
getShare(id).then(response => {
this.form = response.data
this.open = true
this.title = "修改经验分享"
})
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id != null) {
updateShare(this.form).then(response => {
this.$modal.msgSuccess("修改成功")
this.open = false
this.getList()
})
} else {
addShare(this.form).then(response => {
this.$modal.msgSuccess("新增成功")
this.open = false
this.getList()
})
}
}
})
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids
this.$modal.confirm('是否确认删除经验分享编号为"' + ids + '"的数据项?').then(function() {
return delShare(ids)
}).then(() => {
this.getList()
this.$modal.msgSuccess("删除成功")
}).catch(() => {})
},
/** 导出按钮操作 */
handleExport() {
this.download('muhu/share/export', {
...this.queryParams
}, `share_${new Date().getTime()}.xlsx`)
}
}
}
</script>

275
chenhai-ui/src/views/system/again/index.vue

@ -0,0 +1,275 @@
<!-- src/views/system/again/index.vue -->
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['system:again:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:again:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:again:remove']"
>删除</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="againList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="对应的回复id" align="center" prop="answerId" />
<el-table-column label="用户名" align="center" prop="nickName" />
<el-table-column label="回复内容" align="center" prop="content" />
<el-table-column label="创建时间" align="center" prop="createdAt" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createdAt, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:again:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:again:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改回复对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="回复内容">
<editor v-model="form.content" :min-height="192"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listAgain, getAgain, delAgain, addAgain, updateAgain } from "@/api/system/again"
export default {
name: "Again",
// props
props: {
visible: {
type: Boolean,
default: false
},
answerId: {
type: [Number, String],
default: null
},
title: {
type: String,
default: "回复管理"
}
},
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
againList: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
answerId: this.answerId, // 使propanswerId
userId: null,
username: null,
content: null,
createdAt: null,
updatedAt: null
},
//
form: {},
//
rules: {
content: [
{ required: true, message: "回复内容不能为空", trigger: "blur" }
]
}
}
},
watch: {
// answerId prop
answerId: {
immediate: true,
handler(newVal) {
if (newVal) {
this.queryParams.answerId = newVal
this.getList()
}
}
}
},
created() {
// answerId
if (this.answerId) {
this.getList()
}
},
methods: {
/** 查询回复列表 */
getList() {
this.loading = true
listAgain(this.queryParams).then(response => {
this.againList = response.rows
this.total = response.total
this.loading = false
})
},
//
cancel() {
this.open = false
this.reset()
},
//
reset() {
this.form = {
id: null,
answerId: this.answerId, // answerId
userId: null,
username: null,
content: null,
createdAt: null,
updatedAt: null
}
this.resetForm("form")
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm")
this.queryParams.answerId = this.answerId
this.handleQuery()
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
this.open = true
this.title = "添加回复"
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
const id = row.id || this.ids
getAgain(id).then(response => {
this.form = response.data
this.open = true
this.title = "修改回复"
})
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id != null) {
updateAgain(this.form).then(response => {
this.$modal.msgSuccess("修改成功")
this.open = false
this.getList()
this.$emit('refresh')
})
} else {
addAgain(this.form).then(response => {
this.$modal.msgSuccess("新增成功")
this.open = false
this.getList()
this.$emit('refresh')
})
}
}
})
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids
this.$modal.confirm('是否确认删除回复编号为"' + ids + '"的数据项?').then(function() {
return delAgain(ids)
}).then(() => {
this.getList()
this.$modal.msgSuccess("删除成功")
this.$emit('refresh')
}).catch(() => {})
}
}
}
</script>

619
chenhai-ui/src/views/system/answers/index.vue

@ -16,7 +16,7 @@
size="mini"
@click="handleAdd"
v-hasPermi="['system:answers:add']"
>新增</el-button>
>新增回答</el-button>
</el-col>
<el-col :span="1.5">
<el-button
@ -53,35 +53,137 @@
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="answersList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="对应的问题ID" align="center" prop="questionId" />
<el-table-column label="用户名" align="center" prop="username" />
<el-table-column label="回答内容" align="center" prop="content" />
<el-table-column label="创建时间" align="center" prop="createdAt" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createdAt, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<!-- 回答列表 -->
<div class="answers-container">
<div v-for="answer in answersList" :key="answer.id" class="answer-item">
<!-- 顶级回答 -->
<div class="answer-main">
<div class="answer-header">
<el-avatar :src="getAvatarUrl(answer.avatar)" size="small" class="answer-avatar"></el-avatar>
<div class="answer-user-info">
<span class="user-name">{{ answer.nickName || answer.username }}</span>
<span class="answer-time">{{ parseTime(answer.createdAt, '{y}-{m}-{d} {h}:{i}') }}</span>
</div>
<div class="answer-actions">
<!-- 回复按钮 -->
<el-button
type="text"
size="mini"
icon="el-icon-chat-dot-round"
@click="handleShowReplyForm(answer)"
>
回复{{ answer.replyCount > 0 ? `(${answer.replyCount})` : '' }}
</el-button>
<!-- 展开/收起回复按钮 -->
<el-button
type="text"
size="mini"
icon="el-icon-view"
@click="toggleReplies(answer)"
v-if="answer.replyCount > 0"
>
{{ answer.showReplies ? '收起回复' : `查看回复(${answer.replyCount})` }}
</el-button>
<el-button
type="text"
size="mini"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
@click="handleUpdate(answer)"
v-hasPermi="['system:answers:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
size="mini"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
@click="handleDelete(answer)"
v-hasPermi="['system:answers:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
<div class="answer-content">
{{ answer.content }}
</div>
<!-- 回复预览区域 -->
<div v-if="answer.replyCount > 0 && !answer.showReplies" class="reply-preview">
<div class="reply-preview-header">
<el-avatar
:src="getAvatarUrl(answer.latestReplyAvatar)"
size="small"
class="reply-preview-avatar"
></el-avatar>
<span class="reply-preview-user">{{ answer.latestReplyNickName || '用户' }}</span>
<span class="reply-preview-time">{{ formatReplyTime(answer.latestReplyTime) }}</span>
</div>
<div class="reply-preview-content">
{{ answer.latestReplyContent || '回复内容' }}
</div>
<div class="reply-preview-actions">
<el-button
type="text"
size="mini"
@click="toggleReplies(answer)"
>
查看全部{{ answer.replyCount }}条回复
</el-button>
<el-button
type="text"
size="mini"
@click="handleShowReplyForm(answer)"
class="reply-btn"
>
回复
</el-button>
</div>
</div>
<!-- 快速回复表单 -->
<div v-if="answer.showReplyForm" class="quick-reply-form">
<el-input
v-model="answer.replyContent"
type="textarea"
:rows="2"
placeholder="请输入回复内容"
class="reply-textarea"
></el-input>
<div class="reply-form-actions">
<el-button type="primary" size="mini" @click="submitQuickReply(answer)" :loading="answer.submitting">提交</el-button>
<el-button size="mini" @click="cancelQuickReply(answer)">取消</el-button>
</div>
</div>
</div>
<!-- 回复列表 -->
<div v-if="answer.showReplies && answer.replies && answer.replies.length > 0" class="replies-container">
<div v-for="reply in answer.replies" :key="reply.id" class="reply-item">
<div class="reply-header">
<el-avatar :src="getAvatarUrl(reply.avatar)" size="mini" class="reply-avatar"></el-avatar>
<div class="reply-user-info">
<span class="user-name">{{ reply.nickName || reply.username }}</span>
<span class="reply-time">{{ parseTime(reply.createdAt, '{m}-{d} {h}:{i}') }}</span>
</div>
<div class="reply-actions">
<el-button type="text" size="mini" @click="handleDeleteReply(reply)">删除</el-button>
</div>
</div>
<div class="reply-content">
{{ reply.content }}
</div>
</div>
<!-- 查看更多提示 -->
<div v-if="answer.replyCount > answer.replies.length" class="view-more">
<el-button type="text" size="mini" @click="loadMoreReplies(answer)">
查看更多回复{{ answer.replyCount }}
</el-button>
</div>
</div>
</div>
</div>
<pagination
v-show="total>0"
@ -91,10 +193,10 @@
@pagination="getList"
/>
<!-- 添加或修改答对话框 -->
<!-- 添加或修改答对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="回答内容">
<el-form-item label="回答内容" prop="content">
<editor v-model="form.content" :min-height="192"/>
</el-form-item>
</el-form>
@ -107,31 +209,36 @@
</template>
<script>
import { listAnswers, getAnswers, delAnswers, addAnswers, updateAnswers } from "@/api/system/answers"
import {
listAnswers,
getAnswers,
delAnswers,
addAnswers,
updateAnswers,
listChildrenAnswers,
addReply,
listRootAnswers
} from "@/api/system/answers"
export default {
name: "Answers",
props: {
questionId: {
type: [Number, String],
default: null
}
},
data() {
return {
//
loading: true,
//
loading: false,
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
answersList: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
@ -142,35 +249,221 @@ export default {
createdAt: null,
updatedAt: null
},
//
form: {},
//
rules: {}
rules: {
content: [
{ required: true, message: "回答内容不能为空", trigger: "blur" }
]
}
}
},
watch: {
questionId: {
immediate: true,
handler(newVal) {
if (newVal) {
this.queryParams.questionId = newVal
this.getList()
}
}
}
},
created() {
if (!this.questionId) {
this.getList()
}
},
methods: {
/** 查询答复列表 */
/** 获取头像完整URL */
getAvatarUrl(avatar) {
if (!avatar) return ''
if (avatar.startsWith('http')) return avatar
return avatar.startsWith('/') ? avatar : '/' + avatar
},
/** 格式化回复时间 */
formatReplyTime(time) {
if (!time) return ''
const now = new Date()
const replyTime = new Date(time)
const diffMinutes = Math.floor((now - replyTime) / (1000 * 60))
if (diffMinutes < 1) return '刚刚'
if (diffMinutes < 60) return `${diffMinutes}分钟前`
if (diffMinutes < 1440) return `${Math.floor(diffMinutes / 60)}小时前`
return this.parseTime(time, '{m}-{d} {h}:{i}')
},
/** 显示/隐藏回复列表 */
toggleReplies(answer) {
if (!answer.showReplies) {
//
if (!answer.replies || answer.replies.length === 0) {
this.loadReplies(answer)
} else {
answer.showReplies = !answer.showReplies
}
} else {
answer.showReplies = !answer.showReplies
}
},
/** 加载回复 */
loadReplies(answer) {
listChildrenAnswers(answer.id, {
pageNum: 1,
pageSize: 10
}).then(response => {
if (response.code === 200) {
this.$set(answer, 'replies', response.rows || [])
this.$set(answer, 'showReplies', true)
}
}).catch(error => {
console.error('加载回复失败:', error)
this.$message.error('加载回复失败')
})
},
/** 加载更多回复 */
loadMoreReplies(answer) {
const currentCount = answer.replies.length
listChildrenAnswers(answer.id, {
pageNum: Math.ceil(currentCount / 10) + 1,
pageSize: 10
}).then(response => {
if (response.code === 200) {
const newReplies = response.rows || []
answer.replies = [...answer.replies, ...newReplies]
}
})
},
/** 显示快速回复表单 */
handleShowReplyForm(answer) {
this.answersList.forEach(item => {
if (item.id !== answer.id) {
item.showReplyForm = false
}
})
if (answer.showReplyForm) {
this.$set(answer, 'showReplyForm', false)
} else {
this.$set(answer, 'showReplyForm', true)
this.$set(answer, 'replyContent', '')
this.$set(answer, 'submitting', false)
}
},
/** 提交快速回复 */
submitQuickReply(answer) {
if (!answer.replyContent || !answer.replyContent.trim()) {
this.$message.warning('请输入回复内容')
return
}
answer.submitting = true
const replyData = {
parentId: answer.id,
content: answer.replyContent.trim()
}
// questionId
if (this.questionId) {
replyData.questionId = Number(this.questionId)
}
addReply(replyData).then(response => {
if (response.code === 200) {
this.$message.success('回复成功')
answer.replyContent = ''
answer.showReplyForm = false
//
this.getList()
} else {
this.$message.error(response.msg || '回复失败')
}
answer.submitting = false
}).catch(error => {
console.error('回复失败:', error)
this.$message.error('回复失败: ' + (error.message || '未知错误'))
answer.submitting = false
})
},
/** 取消快速回复 */
cancelQuickReply(answer) {
answer.showReplyForm = false
answer.replyContent = ''
},
/** 删除回复 */
handleDeleteReply(reply) {
this.$modal.confirm('是否确认删除该回复?').then(() => {
delAnswers(reply.id).then(response => {
if (response.code === 200) {
this.$message.success('删除成功')
//
this.getList()
} else {
this.$message.error(response.msg || '删除失败')
}
})
})
},
/** 查询答复列表 - 修改为使用listRootAnswers */
getList() {
if (!this.queryParams.questionId && this.questionId) {
this.queryParams.questionId = this.questionId
}
this.loading = true
listAnswers(this.queryParams).then(response => {
this.answersList = response.rows
this.total = response.total
// 使
listRootAnswers(this.queryParams.questionId, this.queryParams).then(response => {
if (response.code === 200) {
//
this.answersList = (response.rows || []).map(answer => ({
...answer,
showReplyForm: false,
showReplies: false,
replies: [],
replyContent: '',
submitting: false
}))
this.total = response.total || 0
} else {
this.$message.error(response.msg || '获取回答列表失败')
}
this.loading = false
}).catch(error => {
this.$message.error('获取回答列表失败: ' + (error.message || '未知错误'))
this.loading = false
})
},
//
// ...
/** 刷新方法 */
refresh() {
this.queryParams.pageNum = 1
this.getList()
},
/** 取消按钮 */
cancel() {
this.open = false
this.reset()
},
//
/** 表单重置 */
reset() {
this.form = {
id: null,
questionId: null,
questionId: this.queryParams.questionId,
userId: null,
username: null,
content: null,
@ -179,74 +472,304 @@ export default {
}
this.resetForm("form")
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm")
this.queryParams.questionId = this.questionId
this.handleQuery()
},
//
/** 多选框选中数据 */
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
this.open = true
this.title = "添加答"
this.title = "添加答"
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
const id = row.id || this.ids
const id = row.id || this.ids[0]
getAnswers(id).then(response => {
this.form = response.data
this.open = true
this.title = "修改答"
this.title = "修改答"
})
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (!this.form.questionId && this.form.id == null) {
this.form.questionId = this.queryParams.questionId
}
if (this.form.id != null) {
updateAnswers(this.form).then(response => {
this.$modal.msgSuccess("修改成功")
this.open = false
this.getList()
this.$emit('refresh')
})
} else {
addAnswers(this.form).then(response => {
this.$modal.msgSuccess("新增成功")
this.open = false
this.getList()
this.$emit('refresh')
})
}
}
})
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids
this.$modal.confirm('是否确认删除答复编号为"' + ids + '"的数据项?').then(function() {
this.$modal.confirm('是否确认删除答复编号为"' + ids + '"的数据项?').then(() => {
return delAnswers(ids)
}).then(() => {
this.getList()
this.$modal.msgSuccess("删除成功")
this.$emit('refresh')
}).catch(() => {})
},
/** 导出按钮操作 */
handleExport() {
this.download('system/answers/export', {
...this.queryParams
}, `answers_${new Date().getTime()}.xlsx`)
},
/** 时间格式化 */
parseTime(time, pattern) {
if (!time) return ''
const date = new Date(time)
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
const value = formatObj[key]
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
return value.toString().padStart(2, '0')
})
return time_str
}
}
}
</script>
<style scoped>
.app-container {
padding: 20px;
}
.page-header {
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #e4e7ed;
}
.page-header h2 {
margin: 0;
color: #303133;
font-size: 20px;
}
.answers-container {
margin-top: 20px;
}
.answer-item {
border: 1px solid #e4e7ed;
border-radius: 4px;
padding: 16px;
margin-bottom: 16px;
background-color: #fff;
transition: all 0.3s;
}
.answer-item:hover {
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.answer-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.answer-avatar {
margin-right: 10px;
}
.answer-user-info {
flex: 1;
}
.user-name {
font-weight: 600;
color: #303133;
margin-right: 10px;
}
.answer-time {
font-size: 12px;
color: #909399;
}
.answer-content {
line-height: 1.6;
color: #303133;
margin-bottom: 12px;
white-space: pre-wrap;
word-break: break-word;
}
/* 回复预览样式 */
.reply-preview {
margin-top: 12px;
padding: 12px;
background-color: #f9f9f9;
border-radius: 4px;
border-left: 3px solid #409eff;
}
.reply-preview-header {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.reply-preview-avatar {
margin-right: 8px;
}
.reply-preview-user {
font-weight: 500;
color: #409eff;
margin-right: 10px;
}
.reply-preview-time {
font-size: 12px;
color: #909399;
}
.reply-preview-content {
margin-left: 40px;
color: #606266;
line-height: 1.5;
font-size: 14px;
}
.reply-preview-actions {
margin-top: 8px;
text-align: right;
}
.reply-preview-actions .el-button {
margin-left: 10px;
font-size: 12px;
}
.reply-preview-actions .reply-btn {
color: #67c23a;
}
/* 快速回复表单样式 */
.quick-reply-form {
margin: 12px 0;
padding: 12px;
background-color: #f9f9f9;
border-radius: 4px;
}
.reply-textarea {
margin-bottom: 8px;
}
.reply-form-actions {
display: flex;
justify-content: flex-end;
}
/* 回复列表样式 */
.replies-container {
margin-top: 16px;
padding-left: 20px;
border-left: 2px solid #e4e7ed;
}
.reply-item {
padding: 12px 0;
border-bottom: 1px solid #f0f0f0;
}
.reply-item:last-child {
border-bottom: none;
}
.reply-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
.reply-user-info {
flex: 1;
margin-left: 10px;
}
.reply-content {
margin-left: 50px; /* 头像宽度 + 间距 */
font-size: 14px;
color: #606266;
line-height: 1.5;
}
.view-more {
text-align: center;
margin-top: 12px;
padding: 8px 0;
}
.view-more .el-button {
font-size: 12px;
color: #409eff;
}
/* 按钮样式优化 */
.el-button--text {
padding: 0 8px;
}
.answer-actions .el-button {
margin-left: 8px;
}
</style>

323
chenhai-ui/src/views/system/muhuuser/index.vue

@ -0,0 +1,323 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['system:muhuuser:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:muhuuser:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:muhuuser:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['system:muhuuser:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="muhuuserList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="用户ID" align="center" prop="userId" />
<el-table-column label="真实姓名" align="center" prop="realName" />
<el-table-column label="身份证号" align="center" prop="idCard" />
<el-table-column label="身份证正面照" align="center" prop="idCardFront" />
<el-table-column label="身份证反面照" align="center" prop="idCardBack" />
<el-table-column label="人脸识别照片" align="center" prop="faceImage" width="100">
<template slot-scope="scope">
<image-preview :src="scope.row.faceImage" :width="50" :height="50"/>
</template>
</el-table-column>
<el-table-column label="认证状态" align="center" prop="authStatus" />
<el-table-column label="认证时间" align="center" prop="authTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.authTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="认证失败原因" align="center" prop="authFailReason" />
<el-table-column label="安全问题" align="center" prop="securityQuestion" />
<el-table-column label="安全答案" align="center" prop="securityAnswer" />
<el-table-column label="最后登录时间" align="center" prop="lastLoginTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.lastLoginTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="最后登录IP" align="center" prop="lastLoginIp" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:muhuuser:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:muhuuser:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改牧户用户对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="真实姓名" prop="realName">
<el-input v-model="form.realName" placeholder="请输入真实姓名" />
</el-form-item>
<el-form-item label="身份证号" prop="idCard">
<el-input v-model="form.idCard" placeholder="请输入身份证号" />
</el-form-item>
<el-form-item label="身份证正面照" prop="idCardFront">
<el-input v-model="form.idCardFront" placeholder="请输入身份证正面照" />
</el-form-item>
<el-form-item label="身份证反面照" prop="idCardBack">
<el-input v-model="form.idCardBack" placeholder="请输入身份证反面照" />
</el-form-item>
<el-form-item label="人脸识别照片" prop="faceImage">
<image-upload v-model="form.faceImage"/>
</el-form-item>
<el-form-item label="认证时间" prop="authTime">
<el-date-picker clearable
v-model="form.authTime"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择认证时间">
</el-date-picker>
</el-form-item>
<el-form-item label="认证失败原因" prop="authFailReason">
<el-input v-model="form.authFailReason" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="安全问题" prop="securityQuestion">
<el-input v-model="form.securityQuestion" placeholder="请输入安全问题" />
</el-form-item>
<el-form-item label="安全答案" prop="securityAnswer">
<el-input v-model="form.securityAnswer" placeholder="请输入安全答案" />
</el-form-item>
<el-form-item label="最后登录时间" prop="lastLoginTime">
<el-date-picker clearable
v-model="form.lastLoginTime"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择最后登录时间">
</el-date-picker>
</el-form-item>
<el-form-item label="最后登录IP" prop="lastLoginIp">
<el-input v-model="form.lastLoginIp" placeholder="请输入最后登录IP" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listMuhuuser, getMuhuuser, delMuhuuser, addMuhuuser, updateMuhuuser } from "@/api/system/muhuuser"
export default {
name: "Muhuuser",
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
muhuuserList: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
realName: null,
idCard: null,
idCardFront: null,
idCardBack: null,
faceImage: null,
authStatus: null,
authTime: null,
authFailReason: null,
securityQuestion: null,
securityAnswer: null,
lastLoginTime: null,
lastLoginIp: null,
},
//
form: {},
//
rules: {
}
}
},
created() {
this.getList()
},
methods: {
/** 查询牧户用户列表 */
getList() {
this.loading = true
listMuhuuser(this.queryParams).then(response => {
this.muhuuserList = response.rows
this.total = response.total
this.loading = false
})
},
//
cancel() {
this.open = false
this.reset()
},
//
reset() {
this.form = {
userId: null,
realName: null,
idCard: null,
idCardFront: null,
idCardBack: null,
faceImage: null,
authStatus: null,
authTime: null,
authFailReason: null,
securityQuestion: null,
securityAnswer: null,
lastLoginTime: null,
lastLoginIp: null,
updateTime: null
}
this.resetForm("form")
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm")
this.handleQuery()
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.userId)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
this.open = true
this.title = "添加牧户用户"
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
const userId = row.userId || this.ids
getMuhuuser(userId).then(response => {
this.form = response.data
this.open = true
this.title = "修改牧户用户"
})
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.userId != null) {
updateMuhuuser(this.form).then(response => {
this.$modal.msgSuccess("修改成功")
this.open = false
this.getList()
})
} else {
addMuhuuser(this.form).then(response => {
this.$modal.msgSuccess("新增成功")
this.open = false
this.getList()
})
}
}
})
},
/** 删除按钮操作 */
handleDelete(row) {
const userIds = row.userId || this.ids
this.$modal.confirm('是否确认删除牧户用户编号为"' + userIds + '"的数据项?').then(function() {
return delMuhuuser(userIds)
}).then(() => {
this.getList()
this.$modal.msgSuccess("删除成功")
}).catch(() => {})
},
/** 导出按钮操作 */
handleExport() {
this.download('system/muhuuser/export', {
...this.queryParams
}, `muhuuser_${new Date().getTime()}.xlsx`)
}
}
}
</script>

260
chenhai-ui/src/views/system/questions/index.vue

@ -3,49 +3,6 @@
<!-- 搜索区域 -->
<div class="search-area">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
<el-form-item label="用户ID" prop="userId">
<el-input
v-model="queryParams.userId"
placeholder="请输入用户ID"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="用户名" prop="username">
<el-input
v-model="queryParams.username"
placeholder="请输入用户名"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="问题标题" prop="title">
<el-input
v-model="queryParams.title"
placeholder="请输入问题标题"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="标签" prop="tags">
<el-input
v-model="queryParams.tags"
placeholder="请输入标签"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="创建时间" prop="createdAt">
<el-date-picker
v-model="queryParams.createdAt"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
>
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@ -88,17 +45,6 @@
v-hasPermi="['system:questions:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-chat-dot-round"
size="mini"
:disabled="single"
@click="viewAnswers"
v-hasPermi="['system:answers:view']"
>查看答案</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
@ -113,10 +59,9 @@
</el-row>
</div>
<!-- 主内容区域 -->
<div class="main-content" :class="{ 'has-answers': showAnswersPanel }">
<!-- 左侧问题列表 -->
<div class="questions-list" :style="{ width: showAnswersPanel ? '50%' : '100%' }">
<!-- 问题列表 -->
<div class="main-content">
<div class="questions-list">
<el-table
v-loading="loading"
:data="questionsList"
@ -127,7 +72,7 @@
height="calc(100vh - 260px)"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="用户名" align="center" prop="username" width="120" />
<el-table-column label="用户名" align="center" prop="nickName" width="120" />
<el-table-column label="问题标题" align="left" prop="title" min-width="150">
<template slot-scope="scope">
<el-tooltip :content="scope.row.title" placement="top">
@ -140,12 +85,18 @@
<div class="content-preview" v-html="scope.row.content"></div>
</template>
</el-table-column>
<el-table-column label="图片" align="center" prop="images" width="100">
<template slot-scope="scope">
<image-preview :src="scope.row.images" :width="50" :height="50" />
</template>
</el-table-column>
<el-table-column label="标签" align="center" prop="tags" width="120">
<template slot-scope="scope">
<el-tag v-if="scope.row.tags" size="mini">{{ scope.row.tags }}</el-tag>
</template>
</el-table-column>
<el-table-column label="回答数" align="center" prop="answerCount" width="100"/>
<el-table-column label="回复数" align="center" prop="answerCount" width="100"/>
<el-table-column label="浏览量" align="center" prop="viewCount" width="100"/>
<el-table-column label="创建时间" align="center" prop="createdAt" width="160" />
<el-table-column label="操作" align="center" width="180" fixed="right">
<template slot-scope="scope">
@ -160,9 +111,9 @@
size="mini"
type="text"
icon="el-icon-chat-dot-round"
@click="toggleAnswersPanel(scope.row)"
@click="openAnswersDialog(scope.row)"
v-hasPermi="['system:answers:view']"
>答案</el-button>
>回复</el-button>
<el-button
size="mini"
type="text"
@ -183,32 +134,9 @@
@pagination="getList"
/>
</div>
<!-- 右侧答案管理面板 -->
<div v-if="showAnswersPanel" class="answers-panel">
<div class="panel-header">
<div class="panel-title">
<span>{{ currentQuestionTitle }}</span>
<span class="answer-count">({{ currentAnswerCount }}个回答)</span>
</div>
<div class="panel-actions">
<el-button size="mini" icon="el-icon-refresh" @click="refreshAnswers">刷新</el-button>
<el-button size="mini" icon="el-icon-close" @click="closeAnswersPanel">关闭</el-button>
</div>
</div>
<div class="panel-content">
<AnswersManagement
ref="answersManagement"
:question-id="currentQuestionId"
:key="currentQuestionId"
@refresh="handleAnswersRefresh"
:table-height="'calc(100vh - 320px)'"
/>
</div>
</div>
</div>
<!-- 添加或修改问对话框 -->
<!-- 添加或修改问题对话框 -->
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="问题标题" prop="title">
@ -220,23 +148,45 @@
<el-form-item label="标签" prop="tags">
<el-input v-model="form.tags" placeholder="请输入标签,多个用逗号分隔" />
</el-form-item>
<el-form-item label="图片" prop="images">
<image-upload v-model="form.images" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<!-- 回复管理弹窗 -->
<el-dialog
:title="'回复管理 - ' + currentQuestionTitle"
:visible.sync="answersDialogVisible"
width="90%"
top="5vh"
append-to-body
class="answers-dialog"
>
<AnswersManagementDialog
ref="answersDialog"
:question-id="currentQuestionId"
:question-title="currentQuestionTitle"
:answer-count="currentAnswerCount"
@refresh="handleAnswersRefresh"
@close="answersDialogVisible = false"
/>
</el-dialog>
</div>
</template>
<script>
import { listQuestions, getQuestions, delQuestions, addQuestions, updateQuestions } from "@/api/system/questions"
import AnswersManagement from '../answers/index' // Answers
import AnswersManagementDialog from '../answers/index.vue'
export default {
name: "Questions",
components: {
AnswersManagement
AnswersManagementDialog
},
data() {
return {
@ -258,14 +208,13 @@ export default {
title: "",
//
open: false,
//
showAnswersPanel: false, //
currentQuestionId: null, // ID
currentQuestionTitle: '', //
currentAnswerCount: 0, //
selectedRow: null, //
//
answersDialogVisible: false,
//
currentQuestionId: null,
currentQuestionTitle: '',
currentAnswerCount: 0,
selectedRow: null,
//
queryParams: {
pageNum: 1,
@ -283,12 +232,6 @@ export default {
form: {},
//
rules: {
userId: [
{ required: true, message: "用户ID不能为空", trigger: "blur" }
],
username: [
{ required: true, message: "用户名不能为空", trigger: "blur" }
],
title: [
{ required: true, message: "问题标题不能为空", trigger: "blur" }
],
@ -306,8 +249,8 @@ export default {
getList() {
this.loading = true
listQuestions(this.queryParams).then(response => {
this.questionsList = response.rows
this.total = response.total
this.questionsList = response.rows || []
this.total = response.total || 0
this.loading = false
}).catch(() => {
this.loading = false
@ -407,9 +350,9 @@ export default {
}).then(() => {
this.getList()
this.$modal.msgSuccess("删除成功")
//
//
if (this.currentQuestionId && ids.includes(this.currentQuestionId)) {
this.closeAnswersPanel()
this.answersDialogVisible = false
}
}).catch(() => {})
},
@ -421,57 +364,29 @@ export default {
}, `questions_${new Date().getTime()}.xlsx`)
},
/** 查看选中问题的答案 */
viewAnswers() {
if (this.single) {
this.$modal.msgWarning('请选择一条问题记录')
return
}
const questionId = this.ids[0]
const selectedQuestion = this.questionsList.find(item => item.id === questionId)
if (selectedQuestion) {
this.toggleAnswersPanel(selectedQuestion)
}
},
/** 切换答案面板 */
toggleAnswersPanel(row) {
if (this.showAnswersPanel && this.currentQuestionId === row.id) {
this.closeAnswersPanel()
return
}
/** 打开回复管理弹窗 */
openAnswersDialog(row) {
this.currentQuestionId = row.id
this.currentQuestionTitle = row.title
this.currentAnswerCount = row.answerCount || 0
this.selectedRow = row
this.showAnswersPanel = true
},
this.answersDialogVisible = true
/** 关闭答案面板 */
closeAnswersPanel() {
this.showAnswersPanel = false
this.currentQuestionId = null
this.currentQuestionTitle = ''
this.currentAnswerCount = 0
this.selectedRow = null
},
/** 刷新答案数据 */
refreshAnswers() {
if (this.$refs.answersManagement) {
this.$refs.answersManagement.refresh()
//
this.$nextTick(() => {
if (this.$refs.answersDialog) {
this.$refs.answersDialog.refresh()
}
})
},
/** 答案管理组件刷新后的回调 */
handleAnswersRefresh() {
//
if (this.currentQuestionId) {
//
//
const index = this.questionsList.findIndex(item => item.id === this.currentQuestionId)
if (index !== -1) {
// API
this.questionsList[index].answerCount += 1
this.currentAnswerCount = this.questionsList[index].answerCount
}
@ -506,12 +421,11 @@ export default {
.main-content {
flex: 1;
display: flex;
gap: 10px;
min-height: 0;
}
.questions-list {
transition: width 0.3s;
width: 100%;
background: #fff;
padding: 15px;
border-radius: 4px;
@ -519,42 +433,6 @@ export default {
flex-direction: column;
}
.answers-panel {
width: 50%;
background: #fff;
border-radius: 4px;
display: flex;
flex-direction: column;
border-left: 1px solid #ebeef5;
}
.panel-header {
padding: 15px;
border-bottom: 1px solid #ebeef5;
display: flex;
justify-content: space-between;
align-items: center;
background: #f8f9fa;
}
.panel-title {
font-size: 16px;
font-weight: bold;
color: #303133;
}
.answer-count {
font-size: 14px;
color: #909399;
margin-left: 8px;
}
.panel-content {
flex: 1;
overflow: hidden;
padding: 15px;
}
.title-text {
display: inline-block;
max-width: 150px;
@ -570,10 +448,6 @@ export default {
word-break: break-all;
}
.item ::v-deep .el-badge__content {
transform: translateY(-50%) translateX(100%);
}
::v-deep .el-form-item {
margin-bottom: 18px;
}
@ -581,4 +455,20 @@ export default {
::v-deep .el-table .current-row {
background-color: #f5f7fa !important;
}
/* 回复弹窗样式 */
::v-deep .answers-dialog {
max-width: 1200px;
}
::v-deep .answers-dialog .el-dialog__body {
padding: 0;
max-height: 80vh;
overflow: hidden;
}
::v-deep .answers-dialog .el-dialog__header {
border-bottom: 1px solid #ebeef5;
padding-bottom: 15px;
}
</style>
Loading…
Cancel
Save