关键词排名

This commit is contained in:
Vinjor 2025-08-21 17:39:59 +08:00
parent 5b1074a8db
commit 0ec0e03967
24 changed files with 1235 additions and 26 deletions

View File

@ -153,11 +153,11 @@ public class WebController extends BaseController {
@ApiOperation("公司介绍-富文本-首页展示区域")
@ApiImplicitParam(name = "tenantId", value = "站点唯一码", required = true, dataType = "string", paramType = "query", dataTypeClass = String.class)
@GetMapping("/indexCompanyInfo")
public R<Map<String,String>> indexCompanyInfo(@RequestParam(required = true) String tenantId) {
public R<Map<String, String>> indexCompanyInfo(@RequestParam(required = true) String tenantId) {
BaseSiteInfo baseSiteInfo = siteInfoService.getSiteInfo(tenantId);
Map<String,String> map = new HashMap<>();
map.put("content",baseSiteInfo.getCompanyInfo());
map.put("contentApp",baseSiteInfo.getCompanyInfoApp());
Map<String, String> map = new HashMap<>();
map.put("content", baseSiteInfo.getCompanyInfo());
map.put("contentApp", baseSiteInfo.getCompanyInfoApp());
return R.ok(map);
}
@ -233,10 +233,11 @@ public class WebController extends BaseController {
@ApiImplicitParam(name = "tenantId", value = "站点唯一码", required = true, dataType = "string", paramType = "query", dataTypeClass = String.class),
@ApiImplicitParam(name = "pageNum", value = "页码1开始", required = true, dataType = "int", paramType = "query", dataTypeClass = Integer.class),
@ApiImplicitParam(name = "pageSize", value = "每页显示数量", required = true, dataType = "int", paramType = "query", dataTypeClass = Integer.class),
@ApiImplicitParam(name = "catgId", value = "分类id", required = false, dataType = "string", paramType = "query", dataTypeClass = String.class)
@ApiImplicitParam(name = "catgId", value = "分类id", required = false, dataType = "string", paramType = "query", dataTypeClass = String.class),
@ApiImplicitParam(name = "text", value = "搜索内容", required = false, dataType = "string", paramType = "query", dataTypeClass = String.class)
})
@GetMapping("/prodPageList")
public R<IPage<BusiProdNew>> prodPageList(String tenantId, String catgId,
public R<IPage<BusiProdNew>> prodPageList(String tenantId, String catgId, String text,
@RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
Page<BusiProdNew> page = new Page<>(pageNum, pageSize);
@ -244,6 +245,13 @@ public class WebController extends BaseController {
.eq(BusiProdNew::getDataType, DATA_TYPE_PRODUCT)
.eq(BusiProdNew::getIfPublic, true)
.eq(BusiProdNew::getTenantId, tenantId);
if (StringUtils.isNotEmpty(text)) {
queryWrapper.and(wq -> wq
.like(BusiProdNew::getTitle, text)
.or().like(BusiProdNew::getDescription, text)
.or().like(BusiProdNew::getContent, text)
);
}
if (StringUtils.isNotEmpty(catgId)) {
List<String> catgIdList = new ArrayList<>();
catgIdList.add(catgId);
@ -257,9 +265,9 @@ public class WebController extends BaseController {
category.setCatgType(CATG_TYPE_CP);
List<BusiCategoryVO> busiCategoryVOList = categoryService.treeCategory(category);
//转map
Map<String,String> catgMap = categoryService.dealFirstIdRtnMap(busiCategoryVOList);
IPage<BusiProdNew> rtnPage = prodNewService.page(page,queryWrapper);
rtnPage.getRecords().forEach(item->item.setMaxCatgId(catgMap.getOrDefault(item.getCatgId(),"")));
Map<String, String> catgMap = categoryService.dealFirstIdRtnMap(busiCategoryVOList);
IPage<BusiProdNew> rtnPage = prodNewService.page(page, queryWrapper);
rtnPage.getRecords().forEach(item -> item.setMaxCatgId(catgMap.getOrDefault(item.getCatgId(), "")));
return R.ok(rtnPage);
}
@ -279,10 +287,11 @@ public class WebController extends BaseController {
@ApiImplicitParam(name = "tenantId", value = "站点唯一码", required = true, dataType = "string", paramType = "query", dataTypeClass = String.class),
@ApiImplicitParam(name = "pageNum", value = "页码1开始", required = true, dataType = "int", paramType = "query", dataTypeClass = Integer.class),
@ApiImplicitParam(name = "pageSize", value = "每页显示数量", required = true, dataType = "int", paramType = "query", dataTypeClass = Integer.class),
@ApiImplicitParam(name = "catgId", value = "分类id", required = false, dataType = "string", paramType = "query", dataTypeClass = String.class)
@ApiImplicitParam(name = "catgId", value = "分类id", required = false, dataType = "string", paramType = "query", dataTypeClass = String.class),
@ApiImplicitParam(name = "text", value = "搜索内容", required = false, dataType = "string", paramType = "query", dataTypeClass = String.class)
})
@GetMapping("/newsPageList")
public R<IPage<BusiProdNew>> newsPageList(String tenantId, String catgId,
public R<IPage<BusiProdNew>> newsPageList(String tenantId, String catgId, String text,
@RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
Page<BusiProdNew> page = new Page<>(pageNum, pageSize);
@ -290,6 +299,13 @@ public class WebController extends BaseController {
.eq(BusiProdNew::getDataType, DATA_TYPE_NEWS)
.eq(BusiProdNew::getIfPublic, true)
.eq(BusiProdNew::getTenantId, tenantId);
if (StringUtils.isNotEmpty(text)) {
queryWrapper.and(wq -> wq
.like(BusiProdNew::getTitle, text)
.or().like(BusiProdNew::getDescription, text)
.or().like(BusiProdNew::getContent, text)
);
}
if (StringUtils.isNotEmpty(catgId)) {
List<String> catgIdList = new ArrayList<>();
catgIdList.add(catgId);
@ -303,9 +319,9 @@ public class WebController extends BaseController {
category.setCatgType(CATG_TYPE_WZ);
List<BusiCategoryVO> busiCategoryVOList = categoryService.treeCategory(category);
//转map
Map<String,String> catgMap = categoryService.dealFirstIdRtnMap(busiCategoryVOList);
Map<String, String> catgMap = categoryService.dealFirstIdRtnMap(busiCategoryVOList);
IPage<BusiProdNew> rtnPage = prodNewService.page(page, queryWrapper);
rtnPage.getRecords().forEach(item->item.setMaxCatgId(catgMap.getOrDefault(item.getCatgId(),"")));
rtnPage.getRecords().forEach(item -> item.setMaxCatgId(catgMap.getOrDefault(item.getCatgId(), "")));
return R.ok(rtnPage);
}
@ -382,7 +398,7 @@ public class WebController extends BaseController {
}
return success(result);
} else {
Map<String,String> ipMap = commonUtils.getIPAndCountry(request);
Map<String, String> ipMap = commonUtils.getIPAndCountry(request);
busiChatMain.setIp(ipMap.get("ip"));
busiChatMain.setNational(ipMap.get("national"));
busiChatMain.setOceania(ipMap.get("oceania"));
@ -427,7 +443,7 @@ public class WebController extends BaseController {
})
@PostMapping("/inquirySave")
public R<String> inquirySave(@ApiIgnore @RequestBody BusiInquiryItem inquiryItem, HttpServletRequest request) {
Map<String,String> ipMap = commonUtils.getIPAndCountry(request);
Map<String, String> ipMap = commonUtils.getIPAndCountry(request);
inquiryItem.setIp(ipMap.get("ip"));
inquiryItem.setNational(ipMap.get("national"));
inquiryItem.setOceania(ipMap.get("oceania"));
@ -437,9 +453,10 @@ public class WebController extends BaseController {
/**
* 导航栏接口--所有分类
*
* @return com.ruoyi.common.core.domain.AjaxResult
* @author vinjor-M
* @date 10:04 2025/7/8
* @return com.ruoyi.common.core.domain.AjaxResult
**/
@ApiOperation("站点地图使用接口")
@ApiImplicitParams(value = {
@ -447,15 +464,16 @@ public class WebController extends BaseController {
@ApiImplicitParam(name = "tenantId", value = "站点编码", required = true, dataType = "string", paramType = "query", dataTypeClass = String.class)
})
@GetMapping("/siteMap")
public R<List<SiteMapVO>> siteMap(@RequestParam(required = true) String tenantId, @RequestParam(required = true) String catgType){
return R.ok(prodNewService.getSiteMap(tenantId,catgType));
public R<List<SiteMapVO>> siteMap(@RequestParam(required = true) String tenantId, @RequestParam(required = true) String catgType) {
return R.ok(prodNewService.getSiteMap(tenantId, catgType));
}
/**
* 记录网页访问次数
*
* @return com.ruoyi.common.core.domain.AjaxResult
* @author vinjor-M
* @date 10:04 2025/7/8
* @return com.ruoyi.common.core.domain.AjaxResult
**/
@ApiOperation("记录网页访问次数")
@ApiImplicitParams(value = {

View File

@ -0,0 +1,126 @@
package com.ruoyi.busi.controller;
import java.util.*;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.busi.vo.BusiKeywordItemQueryVO;
import com.ruoyi.busi.vo.BusiKeywordRankStatVO;
import com.ruoyi.busi.vo.ChartDataVO;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
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.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.busi.domain.BusiKeywordItem;
import com.ruoyi.busi.service.IBusiKeywordItemService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
/**
* 关键词排名明细Controller
*
* @author vinjor-m
* @date 2025-08-21
*/
@RestController
@RequestMapping("/busi/keywordItem")
public class BusiKeywordItemController extends BaseController
{
@Autowired
private IBusiKeywordItemService busiKeywordItemService;
/**
* 查询关键词排名明细列表
*/
@PreAuthorize("@ss.hasPermi('busi:keywordItem:list')")
@GetMapping("/rankStat")
public AjaxResult inquiryCountryList(BusiKeywordItemQueryVO queryVO,
@RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize){
Page<BusiKeywordRankStatVO> page = new Page<>(pageNum, pageSize);
IPage<BusiKeywordRankStatVO> result = busiKeywordItemService.selectKeywordRankStatPage(page, queryVO);
return success(result);
}
/**
* 统计当天1-10名和11-20名的关键词数量
*/
@GetMapping("/todayRankStat")
public AjaxResult todayRankStat(String tenantId) {
if (tenantId == null || tenantId.isEmpty()) {
return AjaxResult.error("租户ID不能为空");
}
Map<String, Integer> result = busiKeywordItemService.selectTodayRankStat( tenantId);
return AjaxResult.success(result);
}
/**
* 导出关键词排名明细列表
*/
@PreAuthorize("@ss.hasPermi('busi:keywordItem:export')")
@Log(title = "关键词排名明细", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, BusiKeywordItem busiKeywordItem)
{
List<BusiKeywordItem> list = busiKeywordItemService.list();
ExcelUtil<BusiKeywordItem> util = new ExcelUtil<BusiKeywordItem>(BusiKeywordItem.class);
util.exportExcel(response, list, "关键词排名明细数据");
}
/**
* 获取关键词排名明细详细信息
*/
@PreAuthorize("@ss.hasPermi('busi:keywordItem:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") String id)
{
return success(busiKeywordItemService.getById(id));
}
/**
* 新增关键词排名明细
*/
@PreAuthorize("@ss.hasPermi('busi:keywordItem:add')")
@Log(title = "关键词排名明细", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BusiKeywordItem busiKeywordItem)
{
return toAjax(busiKeywordItemService.save(busiKeywordItem));
}
/**
* 修改关键词排名明细
*/
@PreAuthorize("@ss.hasPermi('busi:keywordItem:edit')")
@Log(title = "关键词排名明细", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BusiKeywordItem busiKeywordItem)
{
return toAjax(busiKeywordItemService.updateById(busiKeywordItem));
}
/**
* 删除关键词排名明细
*/
@PreAuthorize("@ss.hasPermi('busi:keywordItem:remove')")
@Log(title = "关键词排名明细", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable String[] ids)
{
List<String> list = new ArrayList<>(Arrays.asList(ids));
return toAjax(busiKeywordItemService.removeByIds(list));
}
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.busi.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.ruoyi.common.annotation.Excel;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
@ -111,5 +112,4 @@ public class BusiCategory extends DlBaseEntity
@Excel(name = "站点唯一编码", readConverterExp = "租=户id")
@ApiModelProperty("站点唯一编码")
private String tenantId;
}

View File

@ -0,0 +1,40 @@
package com.ruoyi.busi.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import lombok.*;
import com.ruoyi.common.core.domain.DlBaseEntity;
import java.util.Date;
/**
* 站点使用的关键词对象 dl_busi_keyword
*
* @author vinjor-m
* @date 2025-08-21
*/
@TableName("dl_busi_keyword")
@Data
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BusiKeyword
{
private static final long serialVersionUID = 1L;
/** 关键词 */
@TableId
private String id;
/** 站点唯一编码租户id */
@Excel(name = "站点唯一编码", readConverterExp = "租户id")
private String tenantId;
/** 创建时间 */
@TableField(fill = FieldFill.INSERT)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}

View File

@ -0,0 +1,49 @@
package com.ruoyi.busi.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import com.ruoyi.common.core.domain.DlBaseEntity;
/**
* 关键词排名明细对象 dl_busi_keyword_item
*
* @author vinjor-m
* @date 2025-08-21
*/
@TableName("dl_busi_keyword_item")
@Data
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BusiKeywordItem
{
private static final long serialVersionUID = 1L;
/** 主键 */
@TableId(type = IdType.ASSIGN_UUID)
private String id;
/** 关键词 */
@Excel(name = "关键词")
private String title;
/** 日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date selectDate;
/** 排名 */
@Excel(name = "排名")
private Integer ranking;
/** 站点唯一编码租户id */
@Excel(name = "站点唯一编码", readConverterExp = "租=户id")
private String tenantId;
}

View File

@ -0,0 +1,65 @@
package com.ruoyi.busi.mapper;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.busi.domain.BusiKeywordItem;
import com.ruoyi.busi.vo.BusiKeywordRankStatVO;
import lombok.Data;
import org.apache.ibatis.annotations.Param;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 关键词排名明细Mapper接口
*
* @author vinjor-m
* @date 2025-08-21
*/
@Mapper
public interface BusiKeywordItemMapper extends BaseMapper<BusiKeywordItem>
{
IPage<BusiKeywordItem> queryListPage(@Param("entity") BusiKeywordItem entity, Page<BusiKeywordItem> page);
int deleteBySelectDateInt(@Param("date") String date);
/**
* 分页查询关键词基础信息
*/
IPage<BusiKeywordRankStatVO> selectKeywordPage(
IPage<BusiKeywordRankStatVO> page,
@Param("title") String title,
@Param("tenantId") String tenantId
);
/**
* 查询关键词在时间范围内的排名数据
*/
List<KeywordRankData> selectRankDataByKeywordIds(
@Param("keywordIds") List<String> keywordIds,
@Param("beginDate") Date beginDate,
@Param("endDate") Date endDate,
@Param("tenantId") String tenantId
);
@Data
class KeywordRankData {
private String keywordId;
private String selectDate;
private Integer ranking;
}
/**
* 统计指定日期1-10名和11-20名的关键词数量
* @param selectDate 选择日期
* @param tenantId 租户ID
* @return 排名区间统计列表
*/
List<Map<String, Object>> selectTodayRankStat(
@Param("selectDate") Date selectDate,
@Param("tenantId") String tenantId
);
}

View File

@ -0,0 +1,29 @@
package com.ruoyi.busi.mapper;
import java.util.Date;
import java.util.List;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.busi.domain.BusiKeyword;
import org.apache.ibatis.annotations.Param;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 站点使用的关键词Mapper接口
*
* @author vinjor-m
* @date 2025-08-21
*/
@Mapper
public interface BusiKeywordMapper extends BaseMapper<BusiKeyword>
{
IPage<BusiKeyword> queryListPage(@Param("entity") BusiKeyword entity, Page<BusiKeyword> page);
/**
* 最新一次添加关键词的 时间
* @author vinjor-M
* @date 14:32 2025/8/21
**/
Date selectNewDate();
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.busi.mapper;
import java.util.Date;
import java.util.List;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -45,4 +46,20 @@ public interface BusiProdNewMapper extends BaseMapper<BusiProdNew>
**/
BusiProdNew selectPreOrNext(@Param("dataType") String dataType,@Param("sort") Long sort,@Param("sortType") String sortType,
@Param("tenantId") String tenantId,@Param("catgId") String catgId);
/**
* 查数据库中最新编辑的时间
* @author vinjor-M
* @date 14:29 2025/8/21
* @return java.util.Date
**/
Date selectNewDate();
/**
* 查询更新时间晚于或等于某个时间的数据
* @author vinjor-M
* @date 14:36 2025/8/21
* @param updateTime 某个时间
* @return java.util.List<com.ruoyi.busi.domain.BusiProdNew>
**/
List<BusiProdNew> selectDataByUpdateTime(@Param("updateTime")String updateTime);
}

View File

@ -0,0 +1,46 @@
package com.ruoyi.busi.service;
import java.util.List;
import java.util.Map;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.busi.domain.BusiKeywordItem;
import com.ruoyi.busi.vo.BusiKeywordItemQueryVO;
import com.ruoyi.busi.vo.BusiKeywordRankStatVO;
/**
* 关键词排名明细Service接口
*
* @author vinjor-m
* @date 2025-08-21
*/
public interface IBusiKeywordItemService extends IService<BusiKeywordItem>
{
IPage<BusiKeywordItem> queryListPage(BusiKeywordItem pageReqVO, Page<BusiKeywordItem> page);
/**
* 获取所有关键词的google排名
* @author vinjor-M
* @date 15:10 2025/8/21
**/
void getKeywordRanking();
/**
* 分页查询关键词排名统计
* @param page 分页参数
* @param queryVO 查询参数
* @return 分页结果
*/
IPage<BusiKeywordRankStatVO> selectKeywordRankStatPage(IPage<BusiKeywordRankStatVO> page, BusiKeywordItemQueryVO queryVO);
/**
* 统计当天1-10名和11-20名的关键词数量
* @param selectDate 选择日期
* @param tenantId 租户ID
* @return Map形式的统计结果 {key1: 1-10名数量, key2: 11-20名数量}
*/
Map<String, Integer> selectTodayRankStat( String tenantId);
}

View File

@ -0,0 +1,23 @@
package com.ruoyi.busi.service;
import java.util.List;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.busi.domain.BusiKeyword;
/**
* 站点使用的关键词Service接口
*
* @author vinjor-m
* @date 2025-08-21
*/
public interface IBusiKeywordService extends IService<BusiKeyword> {
IPage<BusiKeyword> queryListPage(BusiKeyword pageReqVO, Page<BusiKeyword> page);
/**
* 检索全站使用的关键词
* @author vinjor-M
* @date 14:26 2025/8/21
**/
void getAllKeyword();
}

View File

@ -0,0 +1,153 @@
package com.ruoyi.busi.service.impl;
import java.util.*;
import java.util.stream.Collectors;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.busi.domain.BusiKeyword;
import com.ruoyi.busi.service.IBusiKeywordService;
import com.ruoyi.busi.vo.BusiKeywordItemQueryVO;
import com.ruoyi.busi.vo.BusiKeywordRankStatVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.busi.mapper.BusiKeywordItemMapper;
import com.ruoyi.busi.domain.BusiKeywordItem;
import com.ruoyi.busi.service.IBusiKeywordItemService;
/**
* 关键词排名明细Service业务层处理
*
* @author vinjor-m
* @date 2025-08-21
*/
@Service
public class BusiKeywordItemServiceImpl extends ServiceImpl<BusiKeywordItemMapper,BusiKeywordItem> implements IBusiKeywordItemService
{
@Autowired
private BusiKeywordItemMapper busiKeywordItemMapper;
@Autowired
private IBusiKeywordService busiKeywordService;
@Override
public IPage<BusiKeywordItem> queryListPage(BusiKeywordItem pageReqVO, Page<BusiKeywordItem> page) {
return busiKeywordItemMapper.queryListPage(pageReqVO, page);
}
/**
* 获取所有关键词的google排名
*
* @author vinjor-M
* @date 15:10 2025/8/21
**/
@Override
public void getKeywordRanking() {
Date nowDate = new Date();
List<BusiKeyword> keywordList = busiKeywordService.list();
//删除今日所有的排名数据
busiKeywordItemMapper.deleteBySelectDateInt(DateUtil.formatDate(nowDate));
List<BusiKeywordItem> insertList = new ArrayList<>();
for (BusiKeyword keyword : keywordList) {
BusiKeywordItem busiKeywordItem = new BusiKeywordItem();
busiKeywordItem.setTitle(keyword.getId());
busiKeywordItem.setSelectDate(nowDate);
//TODO 调朱哥的接口获取排名
busiKeywordItem.setRanking(1);
busiKeywordItem.setTenantId(keyword.getTenantId());
insertList.add(busiKeywordItem);
}
if(!insertList.isEmpty()){
this.saveBatch(insertList);
}
}
@Override
public IPage<BusiKeywordRankStatVO> selectKeywordRankStatPage(IPage<BusiKeywordRankStatVO> page, BusiKeywordItemQueryVO queryVO) {
// 查询关键词数据
IPage<BusiKeywordRankStatVO> resultPage = busiKeywordItemMapper.selectKeywordPage(
page,
queryVO.getTitle(),
queryVO.getTenantId()
);
// 生成查询日期范围内的所有日期列表
List<String> dateList = generateDateList(queryVO.getBeginDate(), queryVO.getEndDate());
// 查询每个关键词的详细排名数据
if (resultPage.getRecords() != null && !resultPage.getRecords().isEmpty()) {
List<String> keywordIds = resultPage.getRecords().stream()
.map(BusiKeywordRankStatVO::getId)
.collect(Collectors.toList());
List<BusiKeywordItemMapper.KeywordRankData> rankDataList = busiKeywordItemMapper.selectRankDataByKeywordIds(
keywordIds,
queryVO.getBeginDate(),
queryVO.getEndDate(),
queryVO.getTenantId()
);
// 按关键词ID分组排名数据
Map<String, List<BusiKeywordItemMapper.KeywordRankData>> keywordRankMap =
rankDataList.stream().collect(Collectors.groupingBy(BusiKeywordItemMapper.KeywordRankData::getKeywordId));
// 为每个关键词构建每日排名映射
resultPage.getRecords().forEach(record -> {
Map<String, Integer> dailyRankings = new LinkedHashMap<>();
// 初始化查询日期范围内的每一天排名设为null
dateList.forEach(date -> dailyRankings.put(date, null));
// 填充实际有数据的排名
List<BusiKeywordItemMapper.KeywordRankData> dataList = keywordRankMap.getOrDefault(record.getId(), new ArrayList<>());
dataList.forEach(data -> dailyRankings.put(data.getSelectDate(), data.getRanking()));
record.setDailyRankings(dailyRankings);
});
}
return resultPage;
}
/**
* 生成日期范围内的所有日期列表
* @param beginDate 开始日期
* @param endDate 结束日期
* @return 日期字符串列表 (yyyy-MM-dd格式)
*/
private List<String> generateDateList(Date beginDate, Date endDate) {
List<String> dateList = new ArrayList<>();
if (beginDate == null || endDate == null) {
return dateList;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(beginDate);
while (!calendar.getTime().after(endDate)) {
dateList.add(DateUtil.format(calendar.getTime(), "yyyy-MM-dd"));
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
return dateList;
}
@Override
public Map<String, Integer> selectTodayRankStat(String tenantId) {
List<Map<String, Object>> resultList = busiKeywordItemMapper.selectTodayRankStat(new Date(), tenantId);
Map<String, Integer> resultMap = new HashMap<>();
resultMap.put("key1", 0); // 1-10名默认为0
resultMap.put("key2", 0); // 11-20名默认为0
for (Map<String, Object> row : resultList) {
String rangeKey = (String) row.get("rangeKey");
Integer count = ((Long) row.get("count")).intValue();
resultMap.put(rangeKey, count);
}
return resultMap;
}
}

View File

@ -0,0 +1,80 @@
package com.ruoyi.busi.service.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.busi.domain.BusiProdNew;
import com.ruoyi.busi.mapper.BusiProdNewMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.busi.mapper.BusiKeywordMapper;
import com.ruoyi.busi.domain.BusiKeyword;
import com.ruoyi.busi.service.IBusiKeywordService;
/**
* 站点使用的关键词Service业务层处理
*
* @author vinjor-m
* @date 2025-08-21
*/
@Service
public class BusiKeywordServiceImpl extends ServiceImpl<BusiKeywordMapper,BusiKeyword> implements IBusiKeywordService
{
@Autowired
private BusiKeywordMapper busiKeywordMapper;
@Autowired
private BusiProdNewMapper busiProdNewMapper;
@Override
public IPage<BusiKeyword> queryListPage(BusiKeyword pageReqVO, Page<BusiKeyword> page) {
return busiKeywordMapper.queryListPage(pageReqVO, page);
}
/**
* 检索全站使用的关键词
*
* @author vinjor-M
* @date 14:26 2025/8/21
**/
@Override
public void getAllKeyword() {
//最近一次编辑产品或文章的时间
Date newDate = busiProdNewMapper.selectNewDate();
if(null==newDate){
//没有新增或编辑文章不需要采集
return;
}
//最近一次插入关键词的时间
Date insertDate = busiKeywordMapper.selectNewDate();
if (null==insertDate || newDate.after(insertDate)) {
//当前时间
Date nowDate = new Date();
//编辑文章的时间晚于最近关键词插入的时间需要采集关键词
List<BusiProdNew> dataList = busiProdNewMapper.selectDataByUpdateTime(null==insertDate?null:insertDate.toString());
if(!dataList.isEmpty()){
//使用的关键词不为空
List<BusiKeyword> insertList = new ArrayList<>();
for (BusiProdNew prodNew : dataList){
List<String> thisKeywordList = Arrays.asList(prodNew.getProdKeyword().split(StrUtil.COMMA));
thisKeywordList.forEach(item -> {
BusiKeyword keywordItem = new BusiKeyword();
keywordItem.setId(item);
keywordItem.setTenantId(prodNew.getTenantId());
keywordItem.setCreateTime(nowDate);
insertList.add(keywordItem);
});
}
if(!insertList.isEmpty()){
this.saveOrUpdateBatch(insertList);
}
}
}
}
}

View File

@ -0,0 +1,26 @@
package com.ruoyi.busi.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
/**
* 关键词排名查询参数VO
*/
@Data
public class BusiKeywordItemQueryVO {
/** 关键词模糊查询 */
private String title;
/** 查询开始时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
private Date beginDate;
/** 查询结束时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
private Date endDate;
/** 租户ID */
private String tenantId;
}

View File

@ -0,0 +1,24 @@
package com.ruoyi.busi.vo;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
/**
* 关键词排名统计结果VO
*/
@Data
public class BusiKeywordRankStatVO {
/** 关键词ID */
private String id;
/** 关键词标题 */
private String title;
/** 每日排名数据 Map<日期, 排名> */
private Map<String, Integer> dailyRankings;
}

View File

@ -0,0 +1,39 @@
package com.ruoyi.task;
import cn.hutool.core.date.DateUtil;
import com.ruoyi.busi.service.IBusiKeywordItemService;
import com.ruoyi.busi.service.IBusiKeywordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 业务模块定时任务调度
*
* @author vinjor-m
*/
@Component("busiTask")
public class BusiTask {
@Autowired
private IBusiKeywordService busiKeywordService;
@Autowired
private IBusiKeywordItemService busiKeywordItemService;
/**
* 更新本站使用的关键词
* @author vinjor-M
* @date 14:51 2025/8/21
**/
public void updateKeyword() {
busiKeywordService.getAllKeyword();
System.out.println(""+ DateUtil.now() +"】执行更新本站使用关键词成功");
}
/**
* 更新本站使用的关键词的google排名
* @author vinjor-M
* @date 14:51 2025/8/21
**/
public void updateKeywordRanking() {
busiKeywordItemService.getKeywordRanking();
System.out.println(""+ DateUtil.now() +"】执行更新本站使用关键词google排名成功");
}
}

View File

@ -5,15 +5,15 @@ spring:
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源-点亮开发库
# master:
# url: jdbc:mysql://82.156.161.160:3306/dl_site_system?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: site
# password: 123456
# 主库数据源-客户测试服务器
master:
url: jdbc:mysql://114.132.197.85:3306/dl_site_system?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://82.156.161.160:3306/dl_site_system?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: site
password: 123456
#主库数据源-客户测试服务器
# master:
# url: jdbc:mysql://114.132.197.85:3306/dl_site_system?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: site
# password: 123456
# 从库数据源
slave:
# 从数据源开关/默认关闭

View File

@ -0,0 +1,90 @@
<?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.ruoyi.busi.mapper.BusiKeywordItemMapper">
<resultMap type="BusiKeywordItem" id="BusiKeywordItemResult">
<result property="id" column="id" />
<result property="title" column="title" />
<result property="selectDate" column="select_date" />
<result property="ranking" column="ranking" />
<result property="tenantId" column="tenant_id" />
</resultMap>
<sql id="selectBusiKeywordItemVo">
select id, title, select_date, ranking, tenant_id from dl_busi_keyword_item
</sql>
<delete id="deleteBySelectDateInt">
delete from dl_busi_keyword_item where select_date = #{date}
</delete>
<select id="queryListPage" parameterType="BusiKeywordItem" resultMap="BusiKeywordItemResult">
<include refid="selectBusiKeywordItemVo"/>
<where>
<if test="entity.title != null and entity.title != ''"> and title = #{entity.title}</if>
<if test="entity.selectDate != null "> and select_date = #{entity.selectDate}</if>
<if test="entity.ranking != null "> and ranking = #{entity.ranking}</if>
<if test="entity.tenantId != null and entity.tenantId != ''"> and tenant_id = #{entity.tenantId}</if>
</where>
</select>
<select id="selectKeywordPage" resultType="com.ruoyi.busi.vo.BusiKeywordRankStatVO">
SELECT
k.id,
k.id AS title
FROM dl_busi_keyword k
<where>
1=1
<if test="tenantId != null and tenantId != ''">
AND k.tenant_id = #{tenantId}
</if>
<if test="title != null and title != ''">
AND k.id LIKE CONCAT('%', #{title}, '%')
</if>
</where>
ORDER BY k.create_time DESC
</select>
<select id="selectRankDataByKeywordIds" resultType="com.ruoyi.busi.mapper.BusiKeywordItemMapper$KeywordRankData">
SELECT
ki.title as keywordId,
DATE_FORMAT(ki.select_date, '%Y-%m-%d') as selectDate,
ki.ranking
FROM dl_busi_keyword_item ki
WHERE ki.tenant_id = #{tenantId}
AND ki.title IN
<foreach collection="keywordIds" item="id" open="(" separator="," close=")">
#{id}
</foreach>
<if test="beginDate != null">
AND ki.select_date &gt;= #{beginDate}
</if>
<if test="endDate != null">
AND ki.select_date &lt;= #{endDate}
</if>
ORDER BY ki.select_date ASC
</select>
<select id="selectTodayRankStat" resultType="java.util.HashMap">
SELECT
CASE
WHEN ranking BETWEEN 1 AND 10 THEN 'key1'
WHEN ranking BETWEEN 11 AND 20 THEN 'key2'
END as rangeKey,
COUNT(*) as count
FROM dl_busi_keyword_item
WHERE tenant_id = #{tenantId}
AND DATE_FORMAT(select_date, '%Y-%m-%d') = DATE_FORMAT(#{selectDate}, '%Y-%m-%d')
AND ranking BETWEEN 1 AND 20
GROUP BY
CASE
WHEN ranking BETWEEN 1 AND 10 THEN 'key1'
WHEN ranking BETWEEN 11 AND 20 THEN 'key2'
END
ORDER BY
CASE
WHEN ranking BETWEEN 1 AND 10 THEN 1
WHEN ranking BETWEEN 11 AND 20 THEN 2
END
</select>
</mapper>

View File

@ -0,0 +1,25 @@
<?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.ruoyi.busi.mapper.BusiKeywordMapper">
<resultMap type="BusiKeyword" id="BusiKeywordResult">
<result property="id" column="id" />
<result property="tenantId" column="tenant_id" />
</resultMap>
<sql id="selectBusiKeywordVo">
select id, tenant_id from dl_busi_keyword
</sql>
<select id="queryListPage" parameterType="BusiKeyword" resultMap="BusiKeywordResult">
<include refid="selectBusiKeywordVo"/>
<where>
<if test="entity.tenantId != null and entity.tenantId != ''"> and tenant_id = #{entity.tenantId}</if>
</where>
</select>
<select id="selectNewDate" resultType="java.util.Date">
select max(create_time) from dl_busi_keyword
</select>
</mapper>

View File

@ -179,4 +179,21 @@
</if>
</where>
</select>
<select id="selectNewDate" resultType="java.util.Date">
SELECT
MAX( create_time )
FROM
dl_busi_prod_new
WHERE
del_flag = '0'
</select>
<select id="selectDataByUpdateTime" resultType="com.ruoyi.busi.domain.BusiProdNew">
SELECT *
FROM dl_busi_prod_new
WHERE del_flag = '0'
AND prod_keyword is not null and prod_keyword!=''
<if test="updateTime != null and updateTime != ''">
AND update_time &gt;= #{updateTime}
</if>
</select>
</mapper>

View File

@ -0,0 +1,52 @@
import request from '@/utils/request'
// 查询关键词排名明细列表
export function listKeywordItem(query) {
return request({
url: '/busi/keywordItem/rankStat',
method: 'get',
params: query
})
}
// 查询关键词排名明细列表
export function todayRankStat(query) {
return request({
url: '/busi/keywordItem/todayRankStat',
method: 'get',
params: query
})
}
// 查询关键词排名明细详细
export function getKeywordItem(id) {
return request({
url: '/busi/keywordItem/' + id,
method: 'get'
})
}
// 新增关键词排名明细
export function addKeywordItem(data) {
return request({
url: '/busi/keywordItem',
method: 'post',
data: data
})
}
// 修改关键词排名明细
export function updateKeywordItem(data) {
return request({
url: '/busi/keywordItem',
method: 'put',
data: data
})
}
// 删除关键词排名明细
export function delKeywordItem(id) {
return request({
url: '/busi/keywordItem/' + id,
method: 'delete'
})
}

View File

@ -101,6 +101,14 @@
</el-col>
<el-col :span="8">
<el-form-item label="排序" prop="sort">
<template v-slot:label>
<span>排序</span>
<el-tooltip class="item" effect="dark" content="数字越大越靠前"
placement="bottom"
>
<i class="el-icon-question"></i>
</el-tooltip>
</template>
<el-input v-model="form.sort" type="number" placeholder="请输入排序"/>
</el-form-item>
</el-col>
@ -113,6 +121,14 @@
</el-col>
<el-col :span="12">
<el-form-item label="页面keyword" prop="keyword">
<template v-slot:label>
<span>页面keyword</span>
<el-tooltip class="item" effect="dark" content="请确保多个关键词用英文逗号','隔开"
placement="bottom"
>
<i class="el-icon-question"></i>
</el-tooltip>
</template>
<el-input type="textarea" v-model="form.keyword" placeholder="请输入页面keyword"/>
</el-form-item>
</el-col>

View File

@ -0,0 +1,242 @@
<template>
<div class="app-container">
<!-- 今日报表情况展示 -->
<div v-if="todayRankData" class="today-rank-summary">
今日报表情况1-10<span>{{ todayRankData.key1 }}</span>个词11-20<span>{{ todayRankData.key2 }}</span>个词
</div>
<!-- 查询条件 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="关键词" prop="title">
<el-input
v-model="queryParams.title"
placeholder="请输入关键词"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="日期范围">
<el-date-picker
v-model="dateRange"
size="small" style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></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>
</el-form-item>
</el-form>
<!-- 数据表格 -->
<el-table v-loading="loading" :data="keywordList" :default-sort="{prop: 'id', order: 'descending'}">
<el-table-column label="关键词" align="center" prop="title" fixed width="200" />
<!-- 动态生成日期列 -->
<el-table-column
v-for="date in dateList"
:key="date"
:label="formatDateLabel(date)"
align="center"
min-width="80"
>
<template slot-scope="scope">
<span :class="getRankClass(scope.row.dailyRankings[date])">
{{ scope.row.dailyRankings[date] || '-' }}
</span>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script>import { listKeywordItem ,todayRankStat} from "@/api/busi/keywordItem";
export default {
name: "BusiKeywordItem",
data() {
// 30
const getDefaultDateRange = () => {
const today = new Date();
const thirtyDaysAgo = new Date(today);
thirtyDaysAgo.setDate(today.getDate() - 30);
return [
this.formatDate(thirtyDaysAgo),
this.formatDate(today)
];
};
return {
//
loading: true,
//
showSearch: true,
//
total: 0,
//
keywordList: [],
//
dateList: [],
// 30
dateRange: getDefaultDateRange(),
//
todayRankData: null,
//
queryParams: {
pageNum: 1,
pageSize: 10,
title: undefined,
beginDate: undefined,
endDate: undefined
}
};
},
created() {
this.getTodayRankData();
this.getList();
},
methods: {
/** 获取今日报表数据 */
getTodayRankData() {
todayRankStat({}).then(response => {
this.todayRankData = response.data;
});
},
/** 查询列表 */
getList() {
this.loading = true;
// 30
if (this.dateRange && this.dateRange.length === 2) {
const startDate = new Date(this.dateRange[0]);
const endDate = new Date(this.dateRange[1]);
//
const timeDiff = Math.abs(endDate.getTime() - startDate.getTime());
//
const diffDays = Math.ceil(timeDiff / (1000 * 3600 * 24));
if (diffDays > 30) {
this.$message.warning("查询时间范围不能超过30天");
this.loading = false;
return;
}
this.queryParams.beginDate = this.dateRange[0];
this.queryParams.endDate = this.dateRange[1];
} else {
this.queryParams.beginDate = undefined;
this.queryParams.endDate = undefined;
}
listKeywordItem(this.queryParams).then(response => {
this.keywordList = response.data.records;
this.total = response.data.total;
//
if (this.keywordList.length > 0) {
this.dateList = Object.keys(this.keywordList[0].dailyRankings);
} else {
this.dateList = [];
}
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
// 30
const today = new Date();
const thirtyDaysAgo = new Date(today);
thirtyDaysAgo.setDate(today.getDate() - 30);
this.dateRange = [
this.formatDate(thirtyDaysAgo),
this.formatDate(today)
];
this.resetForm("queryForm");
this.handleQuery();
},
/** 获取排名样式 */
getRankClass(ranking) {
if (!ranking) return '';
if (ranking <= 3) {
return 'rank-top';
} else if (ranking <= 10) {
return 'rank-good';
} else if (ranking <= 50) {
return 'rank-normal';
} else {
return 'rank-bad';
}
},
/** 格式化日期为 yyyy-MM-dd 格式 */
formatDate(date) {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
},
/** 格式化日期标签 */
formatDateLabel(date) {
if (!date) return '';
const d = new Date(date);
return (d.getMonth() + 1).toString().padStart(2, '0') + '-' +
d.getDate().toString().padStart(2, '0');
}
}
};
</script>
<style scoped lang="scss">
.today-rank-summary {
margin-bottom: 15px;
padding: 10px;
background-color: #f5f7fa;
border-radius: 4px;
font-size: 14px;
color: #606266;
letter-spacing: 1px;
span{
color: red;
font-weight: bold;
padding: 0 4px;
}
}
.rank-top {
color: #f56c6c;
font-weight: bold;
}
.rank-good {
color: #67c23a;
font-weight: bold;
}
.rank-normal {
color: #e6a23c;
}
.rank-bad {
color: #909399;
}
</style>

View File

@ -112,6 +112,14 @@
</el-col>
<el-col :span="8">
<el-form-item label="排序" prop="sort">
<template v-slot:label>
<span>排序</span>
<el-tooltip class="item" effect="dark" content="数字越大越靠前"
placement="bottom"
>
<i class="el-icon-question"></i>
</el-tooltip>
</template>
<el-input v-model="form.sort" type="number" placeholder="请输入排序"/>
</el-form-item>
</el-col>
@ -124,6 +132,14 @@
</el-col>
<el-col :span="8">
<el-form-item label="页面keyword" prop="prodKeyword">
<template v-slot:label>
<span>页面keyword</span>
<el-tooltip class="item" effect="dark" content="请确保多个关键词用英文逗号','隔开"
placement="bottom"
>
<i class="el-icon-question"></i>
</el-tooltip>
</template>
<el-input v-model="form.prodKeyword" type="textarea" placeholder="请输入页面keyword"/>
</el-form-item>
</el-col>

View File

@ -109,6 +109,14 @@
</el-col>
<el-col :span="8">
<el-form-item label="排序" prop="sort">
<template v-slot:label>
<span>排序</span>
<el-tooltip class="item" effect="dark" content="数字越大越靠前"
placement="bottom"
>
<i class="el-icon-question"></i>
</el-tooltip>
</template>
<el-input v-model="form.sort" type="number" placeholder="请输入排序"/>
</el-form-item>
</el-col>
@ -121,6 +129,14 @@
</el-col>
<el-col :span="8">
<el-form-item label="页面keyword" prop="prodKeyword">
<template v-slot:label>
<span>页面keyword</span>
<el-tooltip class="item" effect="dark" content="请确保多个关键词用英文逗号','隔开"
placement="bottom"
>
<i class="el-icon-question"></i>
</el-tooltip>
</template>
<el-input v-model="form.prodKeyword" type="textarea" placeholder="请输入页面keyword"/>
</el-form-item>
</el-col>