This commit is contained in:
Vinjor 2025-07-18 14:42:19 +08:00
parent 26d4bef142
commit 437224cad5
18 changed files with 585 additions and 66 deletions

View File

@ -139,6 +139,11 @@
<artifactId>ip2region</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
</dependencies>
<build>

View File

@ -7,6 +7,8 @@ import com.ruoyi.busi.domain.BusiProdNew;
import com.ruoyi.busi.domain.BusiProdRandom;
import com.ruoyi.busi.service.IBusiProdNewService;
import com.ruoyi.busi.service.IBusiProdRandomService;
import com.ruoyi.busi.utils.SimHash;
import com.ruoyi.busi.utils.TextPreprocessor;
import com.ruoyi.busi.vo.ProdNewVO;
import com.ruoyi.busi.vo.ProdRandomVO;
import com.ruoyi.common.annotation.Log;
@ -110,6 +112,7 @@ public class BusiNewController extends BaseController
@PostMapping
public AjaxResult add(@RequestBody ProdNewVO prodNewVO){
prodNewVO.setDataType(DATA_TYPE_NEWS);
prodNewVO.setSimHash(SimHash.compute(TextPreprocessor.combineFields(prodNewVO.getTitle(),prodNewVO.getDescription(),prodNewVO.getContent())));
busiProdNewService.save(prodNewVO);
if(null!=prodNewVO.getFileList() && !prodNewVO.getFileList().isEmpty()){
prodNewVO.getFileList().forEach(item->{
@ -130,6 +133,7 @@ public class BusiNewController extends BaseController
@PutMapping
public AjaxResult edit(@RequestBody ProdNewVO prodNewVO){
prodNewVO.setDataType(DATA_TYPE_NEWS);
prodNewVO.setSimHash(SimHash.compute(TextPreprocessor.combineFields(prodNewVO.getTitle(),prodNewVO.getDescription(),prodNewVO.getContent())));
busiProdNewService.updateById(prodNewVO);
if(null!=prodNewVO.getFileList() && !prodNewVO.getFileList().isEmpty()){
prodNewVO.getFileList().forEach(item->{
@ -187,4 +191,13 @@ public class BusiNewController extends BaseController
prodRandomService.delProdRandom(randomVO.getProdId(),randomVO.getRandomId(),randomVO.getTenantId());
return success();
}
/**
* 相似度检测
*/
@PostMapping("/checkContent")
public AjaxResult checkContent(@RequestBody ProdNewVO prodNewVO){
prodNewVO.setDataType(DATA_TYPE_NEWS);
return success(busiProdNewService.checkContent(prodNewVO));
}
}

View File

@ -4,13 +4,18 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.base.service.IBasePicsService;
import com.ruoyi.busi.domain.BusiProdRandom;
import com.ruoyi.busi.service.IBusiProdRandomService;
import com.ruoyi.busi.utils.SimHash;
import com.ruoyi.busi.utils.TextPreprocessor;
import com.ruoyi.busi.vo.ProdNewVO;
import com.ruoyi.busi.vo.ProdRandomVO;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@ -118,6 +123,7 @@ public class BusiProdController extends BaseController
@PostMapping
public AjaxResult add(@RequestBody ProdNewVO prodNewVO){
prodNewVO.setDataType(DATA_TYPE_PRODUCT);
prodNewVO.setSimHash(SimHash.compute(TextPreprocessor.combineFields(prodNewVO.getTitle(),prodNewVO.getDescription(),prodNewVO.getContent())));
busiProdNewService.save(prodNewVO);
if(null!=prodNewVO.getFileList() && !prodNewVO.getFileList().isEmpty()){
prodNewVO.getFileList().forEach(item->{
@ -138,6 +144,7 @@ public class BusiProdController extends BaseController
@PutMapping
public AjaxResult edit(@RequestBody ProdNewVO prodNewVO){
prodNewVO.setDataType(DATA_TYPE_PRODUCT);
prodNewVO.setSimHash(SimHash.compute(TextPreprocessor.combineFields(prodNewVO.getTitle(),prodNewVO.getDescription(),prodNewVO.getContent())));
busiProdNewService.updateById(prodNewVO);
if(null!=prodNewVO.getFileList() && !prodNewVO.getFileList().isEmpty()){
prodNewVO.getFileList().forEach(item->{
@ -195,4 +202,13 @@ public class BusiProdController extends BaseController
prodRandomService.delProdRandom(randomVO.getProdId(),randomVO.getRandomId(),randomVO.getTenantId());
return success();
}
/**
* 相似度检测
*/
@PostMapping("/checkContent")
public AjaxResult checkContent(@RequestBody ProdNewVO prodNewVO){
prodNewVO.setDataType(DATA_TYPE_PRODUCT);
return success(busiProdNewService.checkContent(prodNewVO));
}
}

View File

@ -97,6 +97,10 @@ public class BusiProdNew extends DlBaseEntity
@ApiModelProperty("排序")
private Long sort;
/** simHash值 */
@ApiModelProperty("simHash值")
private Long simHash;
/** 是否首页推荐显示 */
@Excel(name = "是否首页推荐显示")
@ApiModelProperty("是否首页推荐显示")

View File

@ -20,6 +20,8 @@ public interface BusiProdNewMapper extends BaseMapper<BusiProdNew>
{
IPage<ProdNewVO> queryListPage(@Param("entity") ProdNewVO entity, Page<BusiProdNew> page);
List<ProdNewVO> selectCusList(@Param("entity") ProdNewVO entity);
Long selectMaxSort(@Param("tenantId")String tenantId,@Param("dataType")String dataType);
/**

View File

@ -74,4 +74,13 @@ public interface IBusiProdNewService extends IService<BusiProdNew>
* @return com.baomidou.mybatisplus.core.metadata.IPage<com.ruoyi.busi.vo.ProdNewVO>
**/
IPage<BusiProdNew> searchTextAll(String tenantId, String text,Page<BusiProdNew> page);
/**
* 相似度检测
* @author vinjor-M
* @date 10:24 2025/7/18
* @param prodNewVO
* @return java.util.List<com.ruoyi.busi.vo.ProdNewVO>
**/
List<ProdNewVO> checkContent(ProdNewVO prodNewVO);
}

View File

@ -1,9 +1,6 @@
package com.ruoyi.busi.service.impl;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -11,12 +8,15 @@ import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapp
import com.ruoyi.busi.domain.BusiProdRandom;
import com.ruoyi.busi.mapper.BusiProdRandomMapper;
import com.ruoyi.busi.service.IBusiCategoryService;
import com.ruoyi.busi.utils.SimHash;
import com.ruoyi.busi.utils.TextPreprocessor;
import com.ruoyi.busi.vo.ProdNewVO;
import com.ruoyi.busi.vo.ProdRandomVO;
import com.ruoyi.busi.vo.WebDetailVO;
import com.ruoyi.common.utils.DateUtils;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@ -26,6 +26,8 @@ import com.ruoyi.busi.service.IBusiProdNewService;
import static com.ruoyi.constant.DictConstants.DATA_TYPE_NEWS;
import static com.ruoyi.constant.DictConstants.DATA_TYPE_PRODUCT;
import static com.ruoyi.constant.StrConstants.HASH_BITS;
import static com.ruoyi.constant.StrConstants.MAX_HAMMING_DISTANCE;
/**
* 产品文章Service业务层处理
@ -203,4 +205,40 @@ public class BusiProdNewServiceImpl extends ServiceImpl<BusiProdNewMapper,BusiPr
.orderByDesc(BusiProdNew::getSort);
return this.page(page,queryWrapper);
}
/**
* 相似度检测
*
* @param prodNewVO
* @return java.util.List<com.ruoyi.busi.vo.ProdNewVO>
* @author vinjor-M
* @date 10:24 2025/7/18
**/
@Override
public List<ProdNewVO> checkContent(ProdNewVO prodNewVO) {
//查出除了本产品/新闻以外的所有其他产品/新闻
prodNewVO.setIfPublic(true);
if(StringUtils.isNotEmpty(prodNewVO.getId())){
prodNewVO.setExcludeProdId(prodNewVO.getId());
}
List<ProdNewVO> list = busiProdNewMapper.selectCusList(prodNewVO);
//本产品/新闻的hash值
long thisHash = SimHash.compute(TextPreprocessor.combineFields(prodNewVO.getTitle(),prodNewVO.getDescription(),prodNewVO.getContent()));
//相似的文章
List<ProdNewVO> similarArticles = new ArrayList<>();
//遍历每一个产品/新闻
for (ProdNewVO prodItem:list){
if(null!=prodItem.getSimHash()){
if (SimHash.isSimilar(thisHash, prodItem.getSimHash(), MAX_HAMMING_DISTANCE)) {
double similarity = 1 - (SimHash.hammingDistance(thisHash, prodItem.getSimHash()) / (double)HASH_BITS);
prodItem.setSimilarity(similarity);
prodItem.setSimilarityStr(String.format("%.2f%%", similarity * 100));
similarArticles.add(prodItem);
}
}
}
// 按相似度降序排序
similarArticles.sort((a, b) -> Double.compare(b.getSimilarity(), a.getSimilarity()));
return similarArticles;
}
}

View File

@ -0,0 +1,111 @@
package com.ruoyi.busi.utils;
import java.util.*;
import static com.ruoyi.constant.StrConstants.HASH_BITS;
import static com.ruoyi.constant.StrConstants.TOP_N;
/**
* SimHash实现类
* @author vinjor-M
* @date 17:25 2025/7/17
**/
public class SimHash {
/**
* 计算文本的SimHash值
*/
public static long compute(String text) {
if (text == null || text.trim().isEmpty()) {
return 0L;
}
// 1. 分词并计算词频
Map<String, Integer> words = tokenize(text);
// 2. 初始化向量
int[] v = new int[HASH_BITS];
// 3. 计算每个词的哈希贡献
for (Map.Entry<String, Integer> entry : words.entrySet()) {
String word = entry.getKey();
int weight = entry.getValue();
// 计算词的哈希值
long hash = hash(word);
// 更新向量
for (int i = 0; i < HASH_BITS; i++) {
long mask = 1L << i;
if ((hash & mask) != 0) {
v[i] += weight;
} else {
v[i] -= weight;
}
}
}
// 4. 生成最终的SimHash值
long simHash = 0;
for (int i = 0; i < HASH_BITS; i++) {
if (v[i] > 0) {
simHash |= 1L << i;
}
}
return simHash;
}
/**
* 计算两个SimHash值的海明距离
*/
public static int hammingDistance(long hash1, long hash2) {
long diff = hash1 ^ hash2;
int distance = 0;
while (diff != 0) {
distance += diff & 1;
diff >>>= 1;
}
return distance;
}
/**
* 判断是否相似根据海明距离
*/
public static boolean isSimilar(long hash1, long hash2, int maxDistance) {
return hammingDistance(hash1, hash2) <= maxDistance;
}
// 辅助方法分词并计算词频简化版实际项目中可以使用更专业的分词工具
private static Map<String, Integer> tokenize(String text) {
String[] words = text.split("\\s+");
Map<String, Integer> wordCounts = new HashMap<>();
for (String word : words) {
if (word.length() > 1) {
// 忽略单字
wordCounts.put(word, wordCounts.getOrDefault(word, 0) + 1);
}
}
// 按词频排序取权重最高的前TOP_N个词
List<Map.Entry<String, Integer>> entries = new ArrayList<>(wordCounts.entrySet());
entries.sort((a, b) -> b.getValue().compareTo(a.getValue()));
Map<String, Integer> topWords = new HashMap<>();
for (int i = 0; i < Math.min(TOP_N, entries.size()); i++) {
topWords.put(entries.get(i).getKey(), entries.get(i).getValue());
}
return topWords;
}
// 辅助方法计算字符串的哈希值
private static long hash(String source) {
if (source == null || source.isEmpty()) {
return 0;
}
long hash = 0;
for (char c : source.toCharArray()) {
hash = (hash << 5) - hash + c;
}
return hash;
}
}

View File

@ -0,0 +1,45 @@
package com.ruoyi.busi.utils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
/**
* 文本预处理工具类
* @author vinjor-M
* @date 17:23 2025/7/17
**/
public class TextPreprocessor {
/**
* 预处理文本清除HTML转小写去除非文字字符
*/
public static String preprocess(String htmlContent) {
if (htmlContent == null || htmlContent.trim().isEmpty()) {
return "";
}
// 1. 清除HTML标签
String text = Jsoup.parse(htmlContent).text();
// 2. 转换为小写
text = text.toLowerCase();
// 3. 移除标点符号和特殊字符保留中文和英文
text = text.replaceAll("[^a-zA-Z0-9\\u4e00-\\u9fa5]", " ");
// 4. 去除多余空格
text = StringUtils.normalizeSpace(text);
return text;
}
/**
* 合并多个字段的文本
*/
public static String combineFields(String title, String summary, String content) {
StringBuilder sb = new StringBuilder();
if (!StringUtils.isBlank(title)) {
sb.append(preprocess(title)).append(" ");
}
if (!StringUtils.isBlank(summary)) {
sb.append(preprocess(summary)).append(" ");
}
if (!StringUtils.isBlank(content)) {
// 内容字段已经包含HTML需要特殊处理
sb.append(preprocess(content));
}
return sb.toString();
}
}

View File

@ -41,7 +41,10 @@ public class ProdNewVO extends BusiProdNew {
/**同分类下产品数量*/
private Integer amount;
/**文章相似度*/
private Double similarity;
/**文章相似度*/
private String similarityStr;
/**
* 批量传的图片
**/

View File

@ -14,4 +14,17 @@ public class StrConstants {
* 结束时间
*/
public static final String END_DATE =" 23:59:59";
/**
* 相似度阈值海明距离值越小越严格
*/
public static final int MAX_HAMMING_DISTANCE = 3;
/**
* 哈希位数
*/
public static final int HASH_BITS = 64;
/**
* 取权重最高的前N个词
*/
public static final int TOP_N = 3;
}

View File

@ -152,4 +152,31 @@
ORDER BY sort DESC LIMIT 1
</if>
</select>
<select id="selectCusList" resultType="com.ruoyi.busi.vo.ProdNewVO">
SELECT
product.*,
dbc.catg_name AS catg_name
FROM
dl_busi_prod_new product
LEFT JOIN dl_busi_category dbc ON product.catg_id = dbc.id
<where>
product.del_flag = '0'
AND product.tenant_id = #{entity.tenantId}
<if test="entity.ifPublic != null">
AND product.if_public = #{entity.ifPublic}
</if>
<if test="entity.catgId != null and entity.catgId != ''">
AND product.catg_id = #{entity.catgId}
</if>
<if test="entity.excludeProdId != null and entity.excludeProdId != ''">
AND product.id NOT IN
<foreach collection="entity.excludeProdId.split(',')" open="(" separator="," close=")" item="item">
#{item}
</foreach>
</if>
<if test="entity.dataType != null and entity.dataType != ''">
AND product.data_type = #{entity.dataType}
</if>
</where>
</select>
</mapper>

View File

@ -78,3 +78,13 @@ export function delProdRandom(data) {
data: data
})
}
// 查相似新闻
export function checkContent(data) {
return request({
url: '/busi/new/checkContent',
method: 'post',
data: data
})
}

View File

@ -78,3 +78,12 @@ export function delProdRandom(data) {
data: data
})
}
// 查相似产品
export function checkContent(data) {
return request({
url: '/busi/prod/checkContent',
method: 'post',
data: data
})
}

View File

@ -5,6 +5,7 @@
<el-button @click="back"> </el-button>
<el-button type="success" @click="saveTmp"> </el-button>
<el-button type="primary" @click="submitForm"> </el-button>
<el-button type="warning" @click="checkContent">相似度检测</el-button>
</el-col>
</el-row>
<el-divider></el-divider>
@ -100,24 +101,27 @@
<el-button @click="back"> </el-button>
<el-button type="success" @click="saveTmp"> </el-button>
<el-button type="primary" @click="submitForm"> </el-button>
<el-button type="warning" @click="checkContent">相似度检测</el-button>
</el-col>
</el-row>
<select-pic fileType="news" ref="selectPic" @ok="chooseFun" />
<similarity-new :dataList="similarityList" ref="similarityNew"> </similarity-new>
</div>
</template>
<script>
import { listCategory } from "@/api/busi/category";
import { listProdNew, getProdNew, delProdNew,getMaxNewSort, addProdNew, updateProdNew } from '@/api/busi/new'
import { listProdNew, getProdNew, delProdNew,getMaxNewSort, addProdNew, updateProdNew,checkContent } from '@/api/busi/new'
import selectPic from "@/views/base/pics/selectPic";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import SimilarityNew from './similarityNew'
export default {
name: 'newForm',
components: { Treeselect,selectPic},
components: { SimilarityNew, Treeselect,selectPic},
data() {
return {
//--
@ -156,6 +160,8 @@ export default {
//
fileList:[],
},
//
similarityList:[],
//
rules: {
title: [
@ -328,6 +334,15 @@ export default {
*/
listenerPicNum(){
this.canPicsNum = this.picsNum-this.form.pics.split(",").length
},
/**
* 相似度检测
*/
checkContent() {
checkContent(this.form).then(response => {
this.similarityList = response.data
this.$refs.similarityNew.show()
})
}
}
}

View File

@ -0,0 +1,86 @@
<template>
<!-- 当前新闻的相似新闻对话框 -->
<el-dialog :title="title" :visible.sync="open" width="1000px" append-to-body>
<el-table v-loading="loading" :data="dataList" >
<el-table-column type="index" width="60" label="序号" align="center"></el-table-column>
<el-table-column label="新闻标题" align="center" prop="title" >
<template slot="header" slot-scope="scope">
<span>新闻标题</span>
<el-tooltip class="item" effect="dark" content="鼠标单机数据可查看新闻详情"
placement="bottom"
>
<i class="el-icon-question"></i>
</el-tooltip>
</template>
<template slot-scope="scope">
<span @click="goProdDetail(scope.row.id)" style="color: #1890ff;cursor: pointer"
>{{ scope.row.title }}</span>
</template>
</el-table-column>
<el-table-column label="所属栏目" align="center" prop="catgName" width="160" />
<el-table-column label="新闻图" align="center" prop="mainPic" width="200">
<template slot-scope="scope">
<image-preview :src="scope.row.mainPic" :width="50" :height="50"/>
</template>
</el-table-column>
<el-table-column label="相似度" align="center" prop="similarityStr" width="160" >
<!-- <template slot-scope="scope">-->
<!-- <span>{{scope.row.similarity}}%</span>-->
<!-- </template>-->
</el-table-column>
</el-table>
<div slot="footer" class="dialog-footer">
<el-button @click="cancelRandom"> </el-button>
</div>
</el-dialog>
</template>
<script>
export default {
name: 'similarityNew',
props: {
//
dataList: {
type: Array,
default: null,
},
},
data() {
return {
//
title: "相似新闻",
//
open: false,
//
loading: true,
}
},
created() {
},
methods:{
/**
* 组件显示
*/
show(){
this.open=true
this.loading = false
},
//
cancelRandom() {
this.open = false;
this.$emit("refresh")
},
/**
* 查看新闻详情
*/
goProdDetail(id) {
let routeData = this.$router.resolve({ path:'/new/newForm', query: { id: id } });
window.open(routeData.href, '_blank');
}
}
}
</script>
<style scoped>
</style>

View File

@ -5,6 +5,7 @@
<el-button @click="back"> </el-button>
<el-button type="success" @click="saveTmp"> </el-button>
<el-button type="primary" @click="submitForm"> </el-button>
<el-button type="warning" @click="checkContent">相似度检测</el-button>
</el-col>
</el-row>
<el-divider></el-divider>
@ -22,7 +23,9 @@
<el-col :span="8">
<el-form-item label="所属分类" prop="catgId">
<div class="dl-flex-column">
<treeselect style="width: 200px" v-model="form.catgId" :options="catgOptions" :normalizer="normalizer" :noResultsText="'暂无数据'" placeholder="请选择产品分类" />
<treeselect style="width: 200px" v-model="form.catgId" :options="catgOptions" :normalizer="normalizer"
:noResultsText="'暂无数据'" placeholder="请选择产品分类"
/>
<div class="dl-add-catg" @click="goCatgView">添加产品分类</div>
</div>
</el-form-item>
@ -102,36 +105,47 @@
<el-button @click="back"> </el-button>
<el-button type="success" @click="saveTmp"> </el-button>
<el-button type="primary" @click="submitForm"> </el-button>
<el-button type="warning" @click="checkContent">相似度检测</el-button>
</el-col>
</el-row>
<select-pic fileType="product" ref="selectPic" @ok="chooseFun" />
<select-pic fileType="product" ref="selectPic" @ok="chooseFun"/>
<similarity-product :dataList="similarityList" ref="similarityProduct"></similarity-product>
</div>
</template>
<script>
import { listCategory } from "@/api/busi/category";
import { listProdNew, getProdNew, delProdNew,getMaxSort, addProdNew, updateProdNew } from '@/api/busi/prod'
import selectPic from "@/views/base/pics/selectPic";
import { listCategory } from '@/api/busi/category'
import {
listProdNew,
getProdNew,
delProdNew,
getMaxSort,
addProdNew,
updateProdNew,
checkContent
} from '@/api/busi/prod'
import selectPic from '@/views/base/pics/selectPic'
import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import SimilarityProduct from './similarityProduct'
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
name: 'prodForm',
components: { Treeselect,selectPic},
components: { SimilarityProduct, Treeselect, selectPic },
data() {
return {
//--
cursorPos: null,
//
catgOptions:[],
catgOptions: [],
//
nowChooseStr:null,
nowChooseStr: null,
//
picsNum:9,
picsNum: 9,
//
canPicsNum:9,
canPicsNum: 9,
//
form: {
id: null,
@ -155,8 +169,10 @@ export default {
updateTime: null,
delFlag: null,
//
fileList:[],
fileList: []
},
//
similarityList: [],
//
rules: {
title: [
@ -179,55 +195,55 @@ export default {
},
mounted() {
this.initCatg()
if(this.$route.query.id){
if(this.$route.query.type){
if (this.$route.query.id) {
if (this.$route.query.type) {
//
this.getProdInfo(this.$route.query.id,this.$route.query.type)
}else{
this.getProdInfo(this.$route.query.id, this.$route.query.type)
} else {
//
this.getProdInfo(this.$route.query.id)
}
}else{
} else {
this.initData()
}
},
methods: {
goCatgView(){
this.$router.push({path:'/busi/category'})
goCatgView() {
this.$router.push({ path: '/busi/category' })
},
initCatg(){
initCatg() {
//
listCategory({catgType:"cp"}).then(response => {
response.data.map((item)=>{
item.parentId=0
listCategory({ catgType: 'cp' }).then(response => {
response.data.map((item) => {
item.parentId = 0
})
this.catgOptions = response.data
});
})
},
/** 转换部门数据结构 */
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children;
delete node.children
}
return {
id: node.id,
label: node.label,
children: node.children
};
}
},
initData(){
initData() {
getMaxSort({}).then(response => {
this.form.sort = response.data
})
},
getProdInfo(id,type){
getProdInfo(id, type) {
getProdNew(id).then(response => {
this.form= response.data
if(this.form.pics && this.form.pics.length>0){
this.canPicsNum = this.picsNum-this.form.pics.split(",").length
this.form = response.data
if (this.form.pics && this.form.pics.length > 0) {
this.canPicsNum = this.picsNum - this.form.pics.split(',').length
}
if(type){
this.form.id=null
if (type) {
this.form.id = null
this.initData()
}
})
@ -237,12 +253,12 @@ export default {
*/
back() {
//
this.$store.dispatch("tagsView/delView", this.$route);
this.$store.dispatch('tagsView/delView', this.$route)
//
this.$router.go(-1)
},
//
uploadedImg(fileList){
uploadedImg(fileList) {
this.form.fileList = this.form.fileList.concat(fileList)
},
handleBlur(e) {
@ -262,40 +278,40 @@ export default {
}
},
/** 暂存 */
saveTmp(){
this.form.ifPublic=false
saveTmp() {
this.form.ifPublic = false
this.saveData()
},
/** 提交按钮 */
submitForm() {
this.$refs['form'].validate(valid => {
if (valid) {
this.form.ifPublic=true
this.form.ifPublic = true
this.saveData()
}
})
},
saveData(){
saveData() {
if (this.form.id != null) {
updateProdNew(this.form).then(response => {
this.$modal.msgSuccess('修改成功')
setTimeout(()=>{
setTimeout(() => {
history.back()
},1000)
}, 1000)
})
} else {
addProdNew(this.form).then(response => {
this.$modal.msgSuccess('新增成功')
setTimeout(()=>{
setTimeout(() => {
history.back()
},1000)
}, 1000)
})
}
},
/**
* 图片库上传
*/
choosePic(str,num){
choosePic(str, num) {
this.nowChooseStr = str
this.$refs.selectPic.show(num)
},
@ -303,17 +319,17 @@ export default {
* 图片库选择图片回调
* @param picUrls
*/
chooseFun(picUrls){
if(this.form[this.nowChooseStr]){
if(this.form[this.nowChooseStr].length>0){
this.form[this.nowChooseStr]+=","+picUrls
}else{
chooseFun(picUrls) {
if (this.form[this.nowChooseStr]) {
if (this.form[this.nowChooseStr].length > 0) {
this.form[this.nowChooseStr] += ',' + picUrls
} else {
this.form[this.nowChooseStr] = picUrls
}
}else {
} else {
this.form[this.nowChooseStr] = picUrls
}
if(this.nowChooseStr=="pics"){
if (this.nowChooseStr == 'pics') {
//
this.listenerPicNum()
}
@ -321,24 +337,35 @@ export default {
/**
* 监听图片库数量
*/
listenerPicNum(){
this.canPicsNum = this.picsNum-this.form.pics.split(",").length
listenerPicNum() {
this.canPicsNum = this.picsNum - this.form.pics.split(',').length
},
/**
* 相似度检测
*/
checkContent() {
checkContent(this.form).then(response => {
this.similarityList = response.data
this.$refs.similarityProduct.show()
})
}
}
}
</script>
<style scoped>
/deep/.vue-treeselect__input{
/deep/ .vue-treeselect__input {
vertical-align: middle !important;
}
.dl-add-catg {
cursor: pointer;
width: 110px;
text-align: center;
color: #1890ff;
}
.dl-tooltip-text{
.dl-tooltip-text {
color: #495060;
font-size: 12px;
}

View File

@ -0,0 +1,86 @@
<template>
<!-- 当前产品的相似产品对话框 -->
<el-dialog :title="title" :visible.sync="open" width="1000px" append-to-body>
<el-table v-loading="loading" :data="dataList" >
<el-table-column type="index" width="60" label="序号" align="center"></el-table-column>
<el-table-column label="产品名称" align="center" prop="title" >
<template slot="header" slot-scope="scope">
<span>产品名称</span>
<el-tooltip class="item" effect="dark" content="鼠标单机数据可查看产品详情"
placement="bottom"
>
<i class="el-icon-question"></i>
</el-tooltip>
</template>
<template slot-scope="scope">
<span @click="goProdDetail(scope.row.id)" style="color: #1890ff;cursor: pointer"
>{{ scope.row.title }}</span>
</template>
</el-table-column>
<el-table-column label="所属栏目" align="center" prop="catgName" width="160" />
<el-table-column label="产品图" align="center" prop="mainPic" width="200">
<template slot-scope="scope">
<image-preview :src="scope.row.mainPic" :width="50" :height="50"/>
</template>
</el-table-column>
<el-table-column label="相似度" align="center" prop="similarityStr" width="160" >
<!-- <template slot-scope="scope">-->
<!-- <span>{{scope.row.similarity}}%</span>-->
<!-- </template>-->
</el-table-column>
</el-table>
<div slot="footer" class="dialog-footer">
<el-button @click="cancelRandom"> </el-button>
</div>
</el-dialog>
</template>
<script>
export default {
name: 'similarityProduct',
props: {
//
dataList: {
type: Array,
default: null,
},
},
data() {
return {
//
title: "相似产品",
//
open: false,
//
loading: true,
}
},
created() {
},
methods:{
/**
* 组件显示
*/
show(){
this.open=true
this.loading = false
},
//
cancelRandom() {
this.open = false;
this.$emit("refresh")
},
/**
* 查看产品详情
*/
goProdDetail(id) {
let routeData = this.$router.resolve({ path:'/product/prodForm', query: { id: id } });
window.open(routeData.href, '_blank');
}
}
}
</script>
<style scoped>
</style>