提交 edca4d8a 编写于 作者: ylwdev's avatar ylwdev 💻

Merge branch 'interest' into 'master'

新增城市技术兴趣功能

See merge request !20
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
"core-js": "^3.8.3", "core-js": "^3.8.3",
"echarts": "^5.3.3", "echarts": "^5.3.3",
"element-ui": "^2.15.9", "element-ui": "^2.15.9",
"heatmapjs": "^2.0.2",
"node-sass": "^6.0.1", "node-sass": "^6.0.1",
"sass-loader": "^10.0.1", "sass-loader": "^10.0.1",
"vant": "^2.12.9", "vant": "^2.12.9",
......
...@@ -43,6 +43,16 @@ const routes = [ ...@@ -43,6 +43,16 @@ const routes = [
pageSpm: '1011.2266' pageSpm: '1011.2266'
} }
}, },
{
path: 'interest',
component: () => import('./view/pc/interest.vue'),
meta: {
title: '城市技术兴趣排名 - 开源实验室',
name:'城市技术兴趣排名',
navTitle:'技术兴趣',
pageSpm: '1011.2266'
}
},
] ]
}, },
{ {
...@@ -83,6 +93,16 @@ const routes = [ ...@@ -83,6 +93,16 @@ const routes = [
pageSpm: '1011.2266' pageSpm: '1011.2266'
} }
}, },
{
path: 'interest',
component: () => import('./view/wap/interest.vue'),
meta: {
title: '城市技术兴趣排名 - 开源实验室',
name:'城市技术兴趣排名',
navTitle:'技术兴趣',
pageSpm: '1011.2266'
}
},
] ]
}, },
] ]
......
...@@ -10,3 +10,6 @@ export function getHardcoreFanInfo () { // 获取铁粉信息 ...@@ -10,3 +10,6 @@ export function getHardcoreFanInfo () { // 获取铁粉信息
export function getFanDistribution (name) { // 获取粉丝分布 export function getFanDistribution (name) { // 获取粉丝分布
return http.get(`${urlData}/v1/get-fan-distribution?username=${name}`) return http.get(`${urlData}/v1/get-fan-distribution?username=${name}`)
} }
export function getInterestInfo () { // 获取技术兴趣信息
return http.get(`${urlData}/v1/get-interest-info`)
}
\ No newline at end of file
...@@ -68,6 +68,11 @@ export default { ...@@ -68,6 +68,11 @@ export default {
key:'/force', key:'/force',
desc:'原力分数的增减原则', desc:'原力分数的增减原则',
url:'https://bbs.csdn.net/topics/602534373' url:'https://bbs.csdn.net/topics/602534373'
},
{
key:'/interest',
desc:'技术兴趣分数的增减原则',
url:'https://bbs.csdn.net/topics/602534373'
} }
], ],
popoverItem:'', popoverItem:'',
......
<template>
<!-- -->
<div class="force">
<div class="map">
<ForceMap :optionList="optionList" :mapStyle="mapStyle" title="兴趣值" @setrankList="setrankList"/>
</div>
<div class="user-rank-list">
<RankList title="城市技术兴趣榜" @clear="clear" :dropdownList="dropdownList" listTitle="兴趣值" @dropdownFn="dropdownFn" :city="city" :rankData="rankData"/>
</div>
</div>
</template>
<script>
import ForceMap from './mapInterest.vue'
import RankList from "./interestRankList.vue";
import { getInterestInfo } from '@/server/screen-data'
export default {
data() {
return {
optionList: [],
rankData: [],
rankList:[],
city:'全国',
dropdownList:[],
mapStyle:{
width:'1000',
height:'900',
tooltip:true,
zoom:1.5
},
}
},
methods: {
dropdownFn (val){
this.city = val
this.rankData = this.optionList.find(it=>it.city == val).list
},
clear(){
this.rankData = this.rankList
this.city = '全国'
},
getlist() {
getInterestInfo().then((res) => {
if (res.status == 200) {
console.log(res.data.data)
this.optionList = res.data.data.cityInfoList
this.rankData = res.data.data.countryTop
this.rankList = res.data.data.countryTop
this.dropdownList = res.data.data.cityInfoList.map(it=>it.city)
}
}).catch(() => {
})
},
setrankList(data){
this.rankData = data.list
this.city = data.city
}
},
created(){
this.mapStyle.width = document.documentElement.clientWidth
this.mapStyle.height = document.documentElement.clientHeight-66
},
mounted() {
this.getlist()
},
components: {
ForceMap,
RankList
},
}
</script>
<style scoped lang="scss">
.force {
width: 100%;
display: flex;
justify-content: space-between;
position: relative;
.user-rank-list {
top: 40px;
position: absolute;
right: 40px;
}
}
</style>
\ No newline at end of file
<template>
<div class="rank-list-box">
<div class="force-slide" :style="!slideopen?'right:-10px':''" >
<!-- <div class="force-slide" v-if="userItem || ( title !== '铁粉榜' && city !=='全国')" @click="clear"> -->
<img src="@/assets/img/go-back.png" style="margin-bottom:16px;" v-if="slideopen &&(userItem || (city !=='全国'))" @click="clear" alt="">
<div class="force-slide-open" @click="slideopen = !slideopen">
<img src="@/assets/img/rank-open.png" alt="">
<span>{{slideopen?'折叠榜单':'展开榜单'}}</span>
</div>
</div>
<div class="rank-list" v-if="slideopen" :style="`height:${listHeight}px;`" ref="rank-list">
<div class="title">
<span>{{ title }}</span>
<!-- <span class="clear" @click="clear" v-if="cityObj">回退</span> -->
<!-- <p v-if="city" @click="cityFlag=!cityFlag"><img src="@/assets/img/place-pc.png" alt="" /><span style="font-size: 16px;">{{city}}</span></p>
<div class="city-list" v-show="city&&cityFlag">
<p>全国</p>
</div> -->
<el-dropdown v-if="city&&!userItem" trigger="click" class="dropdown" @visible-change="(val)=>{cityFlag = val}" @command="dropdownFn" :hide-on-click="true">
<p><span>地区:</span><span style="font-size: 16px;">{{city}}</span> <i :class="!cityFlag?'el-icon-arrow-down':'el-icon-arrow-up'"></i> </p>
<el-dropdown-menu class="rank-dropdown" slot="dropdown" >
<el-dropdown-item :command="it" v-for="(it,index) in dropdownList" :key="index">{{it}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<p v-if="userItem"><img src="@/assets/img/user.png" alt="" /><a target="_blank" :href="`https://blog.csdn.net/${userItem.name}`">{{userItem.nickname||userItem.name}}</a></p>
</div>
<div class="rank-list-nav border">
<span class="mar">排名</span>
<p><span>技术领域</span></p>
<span>兴趣值</span>
</div>
<ul class="user-list" ref="user-list">
<li class="rank-list-nav active" v-for="(item,index) in rankData" :key="index" @click="rankAdd(item)">
<span class="mar">{{index+1}}</span>
<p>
<span
>{{item.nickname || item.name}}</span
>
</p>
<span>{{item.score}}</span>
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
data() {
return {
userItem:'',
listHeight:'',
cityFlag:false,
slideopen:true
}
},
methods: {
dropdownFn(val) {
this.$emit('dropdownFn',val)
},
clear () {
if(!this.userItem){
this.$emit('clear')
} else {
this.userItem = null;
this.$emit('clearRank');
}
},
rankAdd(item){
if (this.title == '用户原力月榜') {
window.open(`https://blog.csdn.net/`+item.username)
}else{
// this.userItem = item
// this.$emit('rankFans',item)
}
}
},
watch: {
rankData() {
this.$refs['user-list'].scrollTop = 0
},
},
props: ['title','rankData','city','listTitle','cityObj','dropdownList'],
mounted(){
this.listHeight = document.documentElement.clientHeight - 120
window.addEventListener("resize", () => {
this.listHeight = document.documentElement.clientHeight - 120
});
document.addEventListener("click",()=>{
this.cityFlag = false
})
},
beforeDestroy(){
}
}
</script>
<style lang="scss">
.rank-dropdown{
padding:4px 0;
width: 110px;
height:260px;
background: #1B2B4F;
box-shadow: 0px 2px 20px 0px rgba(17,27,51,0.7);
border: 1px solid #578AC5;
border-radius: 0px;
overflow: auto;
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-thumb {
background-color: #375a87;
border-radius: 3px;
transition: background-color 0.3s ease-in-out;
}
.popper__arrow{
display: none;
}
.el-dropdown-menu__item{
height: 36px;
text-align: center;
color: #77C0FF;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
word-break: break-all;
&:focus{
background: #2E4974;
}
&:hover{
background: #2E4974;
opacity: 0.9;
}
}
}
</style>
<style scoped lang="scss">
.rank-list-box{
position: relative;
.force-slide{
// padding: 10px;
position: absolute;
top: 10px;
right: 410px;
// left: 20px;
font-size: 14px;
img{
width: 28px;
height: 28px;
cursor: pointer;
}
.force-slide-open{
cursor: pointer;
font-size: 14px;
font-weight: 500;
color: #97ADC6;
display: flex;
flex-direction: column;
span{
margin-top: 8px;
}
}
}
}
.rank-list {
width: 400px;
height: 800px;
min-height: 500px;
display: flex;
flex-direction: column;
.title {
display: flex;
justify-content: space-between;
height: 50px;
align-items: center;
background-image: url("@/assets/img/rank-right.png");
background-size: cover;
padding: 8px 28px 0px 24px;
span {
font-size: 20px;
font-weight: 500;
color: #77c0ff;
}
.clear{
cursor: pointer;
}
.iconfont{
background-image: url("@/assets/img/rank-right.png");
background-size: cover;
}
p {
display: flex;
align-items: center;
max-width: 200px;
font-size: 16px;
color:#77C0FF ;
span{
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
word-break: break-all;
}
&>span:nth-child(1){
width: 70px;
}
cursor: pointer;
img {
width: 18px;
height: 18px;
margin-right: 4px;
}
a {
color: #77C0FF;
flex: 1;
font-size: 16px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
word-break: break-all;
}
}
}
li,.rank-list-nav {
display: flex;
align-items: center;
height: 50px;
padding: 0 24px;
&.border {
border-bottom: 1px solid #2b4670;
}
&.active:hover {
background: #141f38;
cursor: pointer;
}
p {
width: 188px;
display: flex;
align-items: center;
img {
width: 28px;
height: 28px;
border-radius: 50%;
margin-right: 10px;
}
}
& > span:nth-child(1) {
margin-right: 14px;
width: 40px;
text-align: center;
}
& > span:nth-child(3) {
margin-right: 10px;
width: 60px;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
word-break: break-all;
}
&>span:nth-child(4) {
width: 60px;
text-align: center;
}
&.active > span:nth-child(4) {
font-size: 14px;
font-weight: 500;
color: rgba(41, 255, 163, 0.8);
}
&.active > span {
font-size: 14px;
}
& > p {
// flex: 1;
margin-right: 10px;
span {
// width:160px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
word-break: break-all;
}
}
span {
font-size: 16px;
// font-weight: 500;
color: #77c0ff;
}
}
.border{
border-left: 2px solid #5F97D5;
border-right: 2px solid #5F97D5;
background: #192645;
}
.user-list {
flex: 1;
overflow: auto;
border:2px solid #5F97D5 ;
background: #192645;
border-top:none;
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-thumb {
background-color: #375a87;
border-radius: 3px;
transition: background-color 0.3s ease-in-out;
}
}
}
@media screen and (max-width:1600px){
.rank-list {
// width: 368px;
height: 640px;
// .title{
// height: 40px;
// }
// .user-list li,.rank-list-nav{
// &>span:nth-child(1) {
// margin-right: 10px !important;
// width: 52px !important;
// }
// &>span:nth-child(4) {
// width: 64px !important;
// }
// }
}
}
</style>
\ No newline at end of file
<template>
<div class="csdn-map">
<div id="china_map" :style="`width:${mapStyle.width}px;height:${mapStyle.height}px;`" ref="myEchart1"></div>
</div>
</template>
<script>
// 引入echarts
import echarts from "@/components/echarts";
// 引入中国,城市地图
import { chinaJson } from "../../../public/echarts/city.js"
import Bus from "@/common/bus"
export default {
data() {
return {
flag: true,
initOptions: {
renderer: 'canvas'
},
pointArray: [],
GLOptions: null,
normalDistributionNumber: 150, // 正态分布扩散数量
pointSize: 2, // 地图点的大小
pointColor: '#E95307', // 地图点的颜色
dataArray: [],
dataList:[],
geoGPS: {},
oldMapData: {},
isLoadFlights: true, // 是否设置echrts配置项
};
},
// props:['optionList','title','mapStyle'],
props:{
type: {
type: String,
default: () => {
return ''
}
},
title: {
type: String,
default: () => {
return ''
}
},
optionList: {
type: Array,
default: () => {
return []
}
},
mapStyle: {
type: Object,
default: () => {
return {
width:'1000',
height:'900',
zoom:1,
tooltip:true
}
}
},
},
watch:{
optionList () {
this.renderMap()
},
mapStyle () {
this.renderMap()
}
},
methods: {
randomData() {
return Math.round(Math.random() * 1000);
},
renderMap() {
this.handleData(this.optionList)
this.setEchartOption()
},
handleData(res) {
for (let i = 0; i < res.length; i++) {
let dataObj = {
name: '',
value: ''
}
let resObj = res[i]
dataObj.name = resObj.city
if (this.title !== '铁粉数'){
dataObj.value = resObj.score /110
}else{
dataObj.value = resObj.score/300
}
dataObj.score = resObj.score
dataObj.list = resObj.list
this.dataArray.push(dataObj)
this.geoGPS[resObj.city] = resObj.gps
}
this.dataArray = this.dataArray.sort(function (a, b) {
return b.value - a.value;
})
this.dataList = this.dataArray
},
convertData(data) {
var res = [];
for (var i = 0; i < data.length; i++) {
var geoCoord = this.geoGPS[data[i].name];
if (geoCoord) {
res.push({
name: data[i].name,
value: geoCoord.concat(data[i].value)
})
}
}
return res;
},
setEchartOption() {
if (!this.myEchart) {
echarts.registerMap('chinas', chinaJson);
this.myEchart = echarts.init(document.getElementById("china_map"));
}
var that = this
var option = {
geo: {
map: 'chinas',
zoom: this.mapStyle.zoom, // 地图缩放率
roam: true,
center: this.type === 'wap'?[104.40529, 24.904987]:[120.40529, 35.904987], // 地图中心点
silent: 'move', // 拖拽以及缩放
itemStyle: {
areaColor: '#1E2F56', // 地图颜色
borderColor: '#33547E', // 地图边界颜色
borderWidth: .5
},
scaleLimit:{ //所属组件的z分层,z值小的图形会被z值大的图形覆盖
min:0.8, //最小的缩放值
max:35, //最大的缩放值
}
},
tooltip: {
trigger: "item",
borderColor: 'transparent',
show: this.mapStyle.tooltip,
transitionDuration: 0,
showContent: true,
enterable: true,
backgroundColor: 'transparent', // 提示框浮层的背景颜色。
axisPointer: { // 坐标轴指示器配置项。
type: 'line', // 'line' 直线指示器 'shadow' 阴影指示器 'none' 无指示器 'cross' 十字准星指示器。
axis: 'auto', // 指示器的坐标轴。
snap: true, // 坐标轴指示器是否自动吸附到点上
},
textStyle: { // 提示框浮层的文本样式。
color: '#41feff',
fontStyle: 'normal',
fontWeight: 'normal',
fontFamily: 'sans-serif',
fontSize: 14,
},
padding: 0, // 提示框浮层内边距,
formatter: function (params) {
var str = ''
if (params) {
var demo = ''
if (that.dataArray[params.dataIndex].list.length>0){
that.dataArray[params.dataIndex].list.forEach((item,index) => {
if (index<3){
demo += `<a href="${`https://blog.csdn.net/`+item.name}" target="_blank" class="exhibition active"><p class="back rank_${index+1}"></p> <p class="img"> <span>${item.nickname || item.name}</span></p> <p class="num">${item.score}</p></a>`
}
});
}else{
demo = ` <div class="popover-empty">
<span>暂无上榜</span>
<a href="" target="_blank">前往查看热榜更新规则</a>
</div>`
}
str = `<div class="popover">
<div class="popover-top">
<p class="popover-title"> <a href="${`https://blog.csdn.net/`+that.dataArray[params.dataIndex].list[0].name}" target="_blank"> <span class="name"> ${that.dataArray[params.dataIndex].list[0].nickname || that.dataArray[params.dataIndex].list[0].name} </span></a> 成为 <span> ${params.name} </span> 技术兴趣之首 </p>
<p class="popover-city"><i class="force"></i><span class="city">${params.name}</span> <span>${that.title}</span> <span class="num">${that.dataArray[params.dataIndex].score}</span></p>
<div class="exhibition"><p>排名</p> <p class="img">技术领域</p> <p>${that.title}</p></div>
${demo}
</div>
</div>`
}
return str
},
},
series: [
{
name: 'Top 20',
type: 'effectScatter',
coordinateSystem: 'geo',
data: this.convertData(this.dataArray).slice(0, 10),
symbolSize: function (val) {
if (that.type === 'wap') {
if (val[2] / 40 <= 6) {
return 6;
} else {
return val[2] / 40;
}
}
if (val[2] / 40 <= 8) {
return 8;
} else {
return val[2] / 40;
}
},
encode: {
value: 10
},
showEffectOn: 'render',
emphasis: {
scale: true,
},
label: {
formatter: '{b}',
position: 'left',
show: true,
color: '#fff',
fontSize: 13
},
itemStyle: {
color: '#549AD7',
shadowBlur: 10,
shadowColor: '#333'
},
zlevel: 10
},
]
};
this.myEchart.setOption(option);
window.addEventListener("resize", () => {
if (this.myEchart) {
this.myEchart.resize();
}
});
},
},
beforeDestroy(){
this.myEchart && this.myEchart.clear();
},
mounted() {
this.renderMap()
this.myEchart.on('click', (chinaParam) => {
if (chinaParam.seriesName === "Top 20"){
this.$emit('setrankList',{list:this.dataArray[chinaParam.dataIndex].list,city:chinaParam.name})
Bus.$emit('popupObj',{obj:this.dataArray[chinaParam.dataIndex],title:'上榜用户',navname:"铁粉数"})
}
})
this.myEchart.on('georoam',(params)=> {
// 逻辑代码
let _option = this.myEchart.getOption()
let _zoom = _option.geo[0].zoom;
if ( _zoom<=1.5){
this.dataArray = this.dataList.slice(0,10)
}
if (1.5<_zoom&& _zoom<=3){
this.dataArray = this.dataList.slice(0,20)
}
if (3<_zoom&& _zoom<=4){
this.dataArray = this.dataList.slice(0,50)
}
if (4<_zoom&& _zoom<=6){
this.dataArray = this.dataList.slice(0,90)
}
if (6<_zoom&& _zoom<=9){
this.dataArray = this.dataList.slice(0,280)
}
if (9<_zoom&& _zoom<=10){
this.dataArray = this.dataList
}
_option.series[0].data = this.convertData(this.dataArray);
this.myEchart.setOption(_option);
})
},
};
</script>
<style lang="scss">
html,
body {
width: 100%;
height: 100%;
}
.map-city {
width: 360px;
height: 258px;
}
// #china_map {
// width: 1200px;
// height: 900px;
// z-index: 1;
// }
.csdn-map {
overflow: hidden;
.popover {
width: 300px;
height: 258px;
background-image: url("@/assets/img/popover.png");
background-size: cover;
padding-top: 10px;
.popover-title {
font-size: 14px;
font-weight: 500;
color: rgba(119,192,255,0.7);
height: 36px;
line-height: 36px;
padding: 0 16px;
display: flex;
align-items: center;
width: 100%;
img{
width: 24px;
height: 24px;
border-radius: 50%;
margin-right: 8px;
}
span{
color: #77C0FF;
margin: 0 6px;
}
a{
display: flex;
align-items: center;
}
span.name{
margin-left: 0px;
max-width: 78px;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
word-break: break-all;
}
}
.back{
height: 26px;
width: 20px !important;
margin: 0 5px;
&.rank_1{
background-image: url('@/assets/img/rank_1.png');
background-size: cover;
}
&.rank_2{
background-image: url('@/assets/img/rank_2.png');
background-size: cover;
}
&.rank_3{
background-image: url('@/assets/img/rank_3.png');
background-size: cover;
}
}
.popover-empty {
height: 120px;
text-align: center;
span {
font-size: 18px;
font-weight: 500;
}
a {
font-size: 14px;
font-weight: 500;
color: #77c0ff;
}
}
.popover-city {
height: 44px;
line-height: 44px;
padding: 0 16px;
display: flex;
align-items: center;
i.force {
display: inline-block;
width: 16px;
height: 16px;
background-image: url("@/assets/img/place-pc.png");
background-size: cover;
}
span {
font-size: 14px;
font-weight: 500;
color: #77c0ff;
margin-left: 4px;
&.city {
margin-right: 48px;
}
&.num {
color: rgba(41, 255, 163, 0.8);
}
}
}
.exhibition {
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
text-decoration:none;
padding: 0 16px;
margin: 0 2px;
p {
font-size: 14px;
font-weight: 500;
color: rgba(119, 192, 255, 0.7);
text-align: start;
display: flex;
&:nth-child(1){
width: 30px;
text-align: center;
}
&:nth-child(3){
width: 80px;
text-align: center;
margin-left:10px ;
}
&.img{
width: 120px;
margin-left: 24px;
display: flex;
align-items: center;
span{
display: inline-block;
width: 88px;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
word-break: break-all;
}
}
}
img{
width: 24px;
height: 24px;
border-radius: 50%;
margin-right: 8px;
}
&.active {
p {
color: #77c0ff;
}
.num {
color: rgba(41, 255, 163, 0.8);
}
&:hover {
background: #141f38;
cursor: pointer;
}
}
}
}
}
/* .wrapper{
background: #0f0;
} */
</style>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册