提交 8baf5bd8 编写于 作者: !阳仔's avatar !阳仔

add master

上级
MIT License
Copyright (c) 2022 CSDN 技术社区
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# CSDN数据可视化
## 预览地址
- 线上预览地址:[https://csdn.gitcode.host/csdn-datav](https://csdn.gitcode.host/csdn-datav)
## 开发文档
- 开发文档: [develop.md](https://gitcode.net/csdn/csdn-datav/-/blob/master/doc/develop.md)
- API接口: [api.md](https://gitcode.net/csdn/csdn-datav/-/blob/master/doc/api.md)
## 一、前言
关于开源重要性不多赘述。直接进入主题,我们相信开源的价值,认可这种协作模式能带来更大的价值,同时我们也希望99%的开发者都愿意参与到开源建设中,通过开源学习,成长,找到有相同目标的伙伴,通过参与开源项目建设,可以共享开源的成果。
当前的绝大多数开源项目,大多靠企业、开源基金会或个人赞助,大部分开源贡献者靠着“大爱”在幸苦的维护项目,能获得回报的是少数,这样会打击开源参与者的积极性。
基于此,我们提出「新开源」概念,不回避**参与开源建设能够获得什么回报**这样的问题,**探索开源商业化**,为开源寻找可持续发展之路。
“CSDN数据可视化”就是这样一个项目。
CSDN数据可视化是一个由CSDN内部发起,但完全以开源方式运作的项目,该项目将以开放+新开源的形态去运作。
**开放**:我们将脱敏开放CSDN的内部数据,比如:用户原力值、省/城市原力总分、作者粉丝分布、创作动态等各种数据来支持项目发展。
**新开源**:我们在这个过程中将直面开源商业化,探索项目商业化的可能性,我们计划将该项目的绝大多数甚至全部商业收入分享给所有开源贡献者。
## 二、项目介绍
### 2.1、项目目标:
通过开源和社区项目,让 CSDN 有 “每天值得看” 的独特数据可视化, 成为中国第一名的真实 IT 数据展示平台。
### 2.2、V0.1版本主要功能:
页面展示:
1. 通过中国地图展示各省技术贡献热度值;(暂定:计算各地区TOP1000原力值总分)
2. 在地图上可展示各区域原力分TOP1头像;(点击可进入主页)
3. 点击省份可展示该区域内创作者原力分TOP100;
其他展示:
- 发起者及贡献者头像;
- 开源项目地址;
- 开源项目技术栈;
- 服务支持:[CSDN开发云](https://dev.csdn.net/)
### 2.3、项目技术栈:
vue+echarts(暂定,纯前端项目)
### 2.4、测试/线上服务器支持:
[CSDN开发云](https://dev.csdn.net/)
### 2.5、项目奖励:
设定项目贡献分,可通过参与项目开发、项目宣传、测试、内容共建、技术问答等方式获得贡献分,贡献任务由项目发起者发布。
项目贡献分由CSDN平台及赞助者提供。
### 2.6、项目商业化探索方向(贡献者共同探讨):
- To学习者:如何从零开始建设一个数据可视化项目。(将发布内容共建任务从零开始用文字、图片、视频、代码等方式记录项目开发过程)
- ToC:付费(或会员)可查看更有价值的数据。(待议)
- ToB:设置广告位,接受B端付费广告。(暂定:广告投放需项目贡献者投票确定)
## 三、贡献者招募
- 项目技术/管理顾问:可以根据需求制定合理的技术方案并拆解开发任务;
- 产品:对项目有浓厚的兴趣,了解CSDN,最好是CSDN用户;
- 设计:暂由CSDN UED团队成员担任;
- 开发贡献者:对开源贡献感兴趣,能根据发布的开发任务完成功能开发;
- 开源项目会议记录:有较好的文字功底,参与开源项目讨论,并记录开源项目沟通成果并对外发布;
- 知识共创者:对本项目技术栈较为了解,CSDN博主,根据项目发展共建知识体系;
- 种子用户:对该项目感兴趣,并愿意参与早期项目讨论及功能测试;
### 四、项目参与答疑
待整理
### 五、可视化地图设计图
- 综合热榜
- 省份排名
- 贡献者
- 粉丝分布
[点击查看设计稿链接](https://gitcode.net/oslabs/csdn-datav/-/wikis/home)
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
# csdn-datav
2
## 环境准备
```bash
# 大小写敏感
git config core.ignorecase false
# 修改NPM国内镜像加速
npm config set registry https://registry.npmmirror.com/
# 设置你git提交用户名/邮箱,注意本项目尽量不要带--global
git config user.name "git用户名"
git config user.emall "git邮箱"
```
* *注意*:设置git提交的user.email与[CSDN账号邮箱](https://i.csdn.net/#/user-center/account) 保持一致,避免个人真实信息泄露
## 项目启动
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}
{
"name": "csdn-datav",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build && npm run copy",
"lint": "vue-cli-service lint",
"copy": "shx cp ./dist/index.html ./dist/404.html"
},
"dependencies": {
"axios": "^0.27.2",
"clipboard": "^2.0.11",
"core-js": "^3.8.3",
"echarts": "^5.3.3",
"element-ui": "^2.15.9",
"node-sass": "^6.0.1",
"sass-loader": "^10.0.1",
"vant": "^2.12.9",
"vue": "^2.6.14",
"vue-router": "^3.1.3"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"babel-plugin-import": "^1.13.3",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"postcss": "^8.2.8",
"postcss-pxtorem": "^5.1.1",
"shx": "^0.3.4",
"vue-template-compiler": "^2.6.14"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
module.exports = {
plugins: {
'autoprefixer': {
overrideBrowserslist: ['> 0.15% in CN']
},
'postcss-pxtorem': {
rootValue: 37.5, // 结果为:设计稿元素尺寸/16,比如元素宽320px,最终页面会换算成 20rem
propList: ['*'], // 是一个存储哪些将被转换的属性列表,这里设置为['*']全部,假设需要仅对边框进行设置,可以写['*', '!border*']
unitPrecision: 5, // 保留rem小数点多少位
// selectorBlackList: ['.radius'], //则是一个对css选择器进行过滤的数组,比如你设置为['fs'],那例如fs-xl类名,里面有关px的样式将不被转换,这里也支持正则写法。
replace: true, // 这个真不知到干嘛用的。有知道的告诉我一下
mediaQuery: false, // 媒体查询( @media screen 之类的)中不生效
exclude: function (file) {
if (file.indexOf('components/element-ui') !== -1) { // elementui
return true
} else if (file.indexOf('view/pc') !== -1) {
return true
}
return false
}
}
}
}
因为 它太大了无法显示 source diff 。你可以改为 查看blob
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="https://g.csdnimg.cn/static/logo/favicon32.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<script src="https://webapi.amap.com/maps?v=1.4.15&key=968f3f24decdc7564a37458565d50ad7&plugin=AMap.Scale,AMap.ToolBar"></script>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
<script>
function isMobile () {
var ua = window.navigator.userAgent
if ( ua.match(/Android/i) || ua.match(/webOS/i) || ua.match(/SymbianOS/i) || ua.match(/iPhone/i) || ua.match(/iPad/i) || ua.match(/iPod/i) || ua.match(/BlackBerry/i) || ua.match(/Windows Phone/i) ) {
return true
} else {
return false
}
}
function setFontsize() {
let vWidth = document.documentElement.clientWidth
if (vWidth === 0) {
setTimeout(function() {
setFontsize();
}, 100);
return;
}
document.documentElement.style.fontSize = vWidth / 10 + 'px';
}
if(isMobile()) {
setFontsize()
}
</script>
</html>
<template>
<!-- -->
<div class="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
data(){
return {
}
},
methods: {
}
}
</script>
<style scoped>
*{
-webkit-font-smoothing: antialiased;
}
</style>
\ No newline at end of file
import Vue from 'vue';
let Bus = new Vue();
export default Bus;
\ No newline at end of file
export const isMobile = () => {
var ua = window.navigator.userAgent
if (
ua.match(/Android/i) ||
ua.match(/webOS/i) ||
ua.match(/SymbianOS/i) ||
ua.match(/iPhone/i) ||
ua.match(/iPad/i) ||
ua.match(/iPod/i) ||
ua.match(/BlackBerry/i) ||
ua.match(/Windows Phone/i)
) {
return true;
} else {
return false;
}
}
\ No newline at end of file
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
/*
Write your variables here. All available variables can be
found in element-ui/packages/theme-chalk/src/common/var.scss.
For example, to overwrite the theme color:
*/
$--color-primary: #3399ea;
$--color-danger: #FC5531;
/* icon font path, required */
$--font-path: '~element-ui/lib/theme-chalk/fonts';
@import "~element-ui/packages/theme-chalk/src/index";
import Vue from 'vue';
import './element-variables.scss';
import {
Popover
} from 'element-ui';
Vue.use(Popover)
\ No newline at end of file
import {
Message
} from 'element-ui'
let messageInstance = null
let time = ''
const resetMessage = (options) => {
if (messageInstance) {
messageInstance.close()
}
if (typeof options === 'string') {
options = {
message: options
}
}
if (options.duration === 0) {
time = 0
} else if (options.duration === 5000) {
time = 5000
} else {
time = 1500
}
messageInstance = Message({ ...options, duration: time })
return messageInstance
}
;['error', 'success', 'info', 'warning'].forEach(type => {
resetMessage[type] = options => {
if (typeof options === 'string') {
options = {
message: options
}
}
options.type = type
return resetMessage(options)
}
})
export default resetMessage
/**
* @Author : wzk
* @Date : 2021/3/17Wednesday12
* @Last Modified by : wzk
* @Last Modified time : 2021/3/17Wednesday12
* @Description:
**/
import Vue from "vue";
import {
Button,
Tab,
Tabs,
AddressEdit,
Field,
Checkbox,
CheckboxGroup,
Toast,
PullRefresh,
List,
RadioGroup,
Radio,
Cell,
CellGroup,
Col,
Row,
Stepper,
Divider,
Form,
Empty,
Uploader,
Popup,
Rate
} from "vant";
Vue.use(Rate);
Vue.use(Popup);
Vue.use(Button);
Vue.use(Tab);
Vue.use(Tabs);
Vue.use(AddressEdit);
Vue.use(Field);
Vue.use(Checkbox);
Vue.use(CheckboxGroup);
Vue.use(Toast);
Vue.use(PullRefresh);
Vue.use(List);
Vue.use(Radio);
Vue.use(RadioGroup);
Vue.use(Cell);
Vue.use(Col);
Vue.use(Row);
Vue.use(Stepper);
Vue.use(Divider);
Vue.use(Form);
Vue.use(CellGroup);
Vue.use(Empty);
Vue.use(Uploader);
\ No newline at end of file
import Vue from 'vue'
import App from './App.vue'
import router from './router.js'
import './components/element-ui/element.js'
import './components/vant-ui'
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App),
}).$mount('#app')
// index.js 文件内容
import Vue from 'vue';
import VueRouter from 'vue-router';
import {isMobile} from "@/common/util"
// 定义路由, 创建路由配置项
const routes = [
{
path: '/',
name: 'pcmap',
component: () => import('./view/pc/home.vue'),
beforeEnter (to, from, next) {
if (!isMobile()) {
next();
} else {
next({
path:'/m' ,
});
}
},
children: [
{
path: '',
redirect: 'force'
},
{
path: 'force',
component: () => import('./view/pc/force.vue'),
meta: {
title: '全网博主原力排名 - 开源实验室',
navTitle:'原力排名',
name:'全网博主原力排名',
pageSpm: '1011.2266'
}
},
{
path: 'fans',
component: () => import('./view/pc/fans.vue'),
meta: {
title: '全网博主铁粉排名 - 开源实验室',
name:'全网博主铁粉排名',
navTitle:'铁粉排名',
pageSpm: '1011.2266'
}
},
]
},
{
path: '/m',
name: 'pcmap',
component: () => import('./view/wap/home.vue'),
beforeEnter (to, from, next) {
if (isMobile()) {
next();
} else {
next({
path:'/' ,
});
}
},
children: [
{
path: '',
redirect: 'force'
},
{
path: 'force',
component: () => import('./view/wap/force.vue'),
meta: {
title: '全网博主原力排名 - 开源实验室',
navTitle:'原力排名',
name:'全网博主原力排名',
pageSpm: '1011.2266'
}
},
{
path: 'fans',
component: () => import('./view/wap/fans.vue'),
meta: {
title: '全网博主铁粉排名 - 开源实验室',
navTitle:'铁粉排名',
name:'全网博主铁粉排名',
pageSpm: '1011.2266'
}
},
]
},
]
Vue.use(VueRouter);
// 创建路由对象
const router = new VueRouter({
base: `/csdn-datav`,
mode: 'history',
routes
})
router.beforeEach((to, from, next) => {
if (to.meta.title) {
document.title = to.meta.title
}
next()
})
// 把路由对象作为模块导出
export default router;
import axios from 'axios'
let axiosSignature = axios.create()
initAxios(axiosSignature, true)
function initAxios (axiosObj, isNeedSignature = false) {
// axios 配置
axiosObj.defaults.timeout = 3000
// http response 拦截器
axiosObj.interceptors.response.use(
response => {
return response
},
error => {
var errorMsg = ''
if (error.response) {
if (error.response.data && error.response.data.code && error.response.data.message) {
errorMsg = error.response.data && error.response.data.code && error.response.data.message
return Promise.reject(error.response.data)
} else {
errorMsg =
error.response.status + ' ' + error.response.statusText || '系统异常,请稍后再试'
}
} else {
errorMsg = '服务超时,请稍后重试'
}
let errorData = { code: 0, message: errorMsg }
return Promise.reject(errorData)
}
)
}
export default axiosSignature
\ No newline at end of file
import Vue from 'vue'
import http from './httpinterceptor'
const urlData = 'https://map-api.csdn.net/'
export function getForceInfo () { // 获取原力信息
return http.get(`${urlData}/v1/get-force-info`)
}
export function getHardcoreFanInfo () { // 获取铁粉信息
return http.get(`${urlData}/v1/get-hardcore-fan-info`)
}
export function getFanDistribution (name) { // 获取粉丝分布
return http.get(`${urlData}/v1/get-fan-distribution?username=${name}`)
}
\ No newline at end of file
var data = [
{ name: "海门", value: 9 },
{ name: "鄂尔多斯", value: 12 },
{ name: "招远", value: 12 },
{ name: "舟山", value: 12 },
{ name: "齐齐哈尔", value: 14 },
{ name: "盐城", value: 15 },
{ name: "赤峰", value: 16 },
{ name: "青岛", value: 18 },
{ name: "台北", value: 18 },
{ name: "乳山", value: 18 },
{ name: "金昌", value: 19 },
{ name: "泉州", value: 21 },
{ name: "莱西", value: 21 },
{ name: "日照", value: 21 },
{ name: "胶南", value: 22 },
{ name: "南通", value: 23 },
{ name: "拉萨", value: 24 },
{ name: "云浮", value: 24 },
{ name: "梅州", value: 25 },
{ name: "文登", value: 25 },
{ name: "嘉义", value: 25 },
{ name: "上海", value: 25 },
{ name: "攀枝花", value: 25 },
{ name: "威海", value: 25 },
{ name: "承德", value: 25 },
{ name: "厦门", value: 26 },
{ name: "汕尾", value: 26 },
{ name: "潮州", value: 26 },
{ name: "丹东", value: 27 },
{ name: "太仓", value: 27 },
{ name: "曲靖", value: 27 },
{ name: "烟台", value: 28 },
{ name: "福州", value: 29 },
{ name: "瓦房店", value: 30 },
{ name: "即墨", value: 30 },
{ name: "抚顺", value: 31 },
{ name: "玉溪", value: 31 },
{ name: "张家口", value: 31 },
{ name: "阳泉", value: 31 },
{ name: "莱州", value: 32 },
{ name: "湖州", value: 32 },
{ name: "汕头", value: 32 },
{ name: "昆山", value: 33 },
{ name: "宁波", value: 33 },
{ name: "湛江", value: 33 },
{ name: "揭阳", value: 34 },
{ name: "荣成", value: 34 },
{ name: "连云港", value: 35 },
{ name: "葫芦岛", value: 35 },
{ name: "常熟", value: 36 },
{ name: "东莞", value: 36 },
{ name: "河源", value: 36 },
{ name: "淮安", value: 36 },
{ name: "泰州", value: 36 },
{ name: "南宁", value: 37 },
{ name: "营口", value: 37 },
{ name: "惠州", value: 37 },
{ name: "江阴", value: 37 },
{ name: "蓬莱", value: 37 },
{ name: "高雄", value: 38 },
{ name: "韶关", value: 38 },
{ name: "嘉峪关", value: 38 },
{ name: "广州", value: 38 },
{ name: "延安", value: 38 },
{ name: "太原", value: 39 },
{ name: "清远", value: 39 },
{ name: "中山", value: 39 },
{ name: "昆明", value: 39 },
{ name: "寿光", value: 40 },
{ name: "盘锦", value: 40 },
{ name: "长治", value: 41 },
{ name: "深圳", value: 41 },
{ name: "珠海", value: 42 },
{ name: "宿迁", value: 43 },
{ name: "咸阳", value: 43 },
{ name: "铜川", value: 44 },
{ name: "平度", value: 44 },
{ name: "佛山", value: 44 },
{ name: "海口", value: 44 },
{ name: "江门", value: 45 },
{ name: "章丘", value: 45 },
{ name: "肇庆", value: 46 },
{ name: "大连", value: 47 },
{ name: "临汾", value: 47 },
{ name: "吴江", value: 47 },
{ name: "石嘴山", value: 49 },
{ name: "沈阳", value: 50 },
{ name: "苏州", value: 50 },
{ name: "茂名", value: 50 },
{ name: "嘉兴", value: 51 },
{ name: "长春", value: 51 },
{ name: "胶州", value: 52 },
{ name: "银川", value: 52 },
{ name: "张家港", value: 52 },
{ name: "三门峡", value: 53 },
{ name: "锦州", value: 54 },
{ name: "南昌", value: 54 },
{ name: "柳州", value: 54 },
{ name: "三亚", value: 54 },
{ name: "自贡", value: 56 },
{ name: "吉林", value: 56 },
{ name: "阳江", value: 57 },
{ name: "泸州", value: 57 },
{ name: "西宁", value: 57 },
{ name: "宜宾", value: 58 },
{ name: "呼和浩特", value: 58 },
{ name: "成都", value: 58 },
{ name: "大同", value: 58 },
{ name: "镇江", value: 59 },
{ name: "桂林", value: 59 },
{ name: "张家界", value: 59 },
{ name: "宜兴", value: 59 },
{ name: "北海", value: 60 },
{ name: "西安", value: 61 },
{ name: "金坛", value: 62 },
{ name: "东营", value: 62 },
{ name: "牡丹江", value: 63 },
{ name: "遵义", value: 63 },
{ name: "绍兴", value: 63 },
{ name: "扬州", value: 64 },
{ name: "常州", value: 64 },
{ name: "潍坊", value: 65 },
{ name: "重庆", value: 66 },
{ name: "台州", value: 67 },
{ name: "南京", value: 67 },
{ name: "滨州", value: 70 },
{ name: "贵阳", value: 71 },
{ name: "无锡", value: 71 },
{ name: "本溪", value: 71 },
{ name: "克拉玛依", value: 72 },
{ name: "渭南", value: 72 },
{ name: "马鞍山", value: 72 },
{ name: "宝鸡", value: 72 },
{ name: "焦作", value: 75 },
{ name: "句容", value: 75 },
{ name: "北京", value: 79 },
{ name: "徐州", value: 79 },
{ name: "衡水", value: 80 },
{ name: "包头", value: 80 },
{ name: "绵阳", value: 80 },
{ name: "乌鲁木齐", value: 84 },
{ name: "枣庄", value: 84 },
{ name: "杭州", value: 84 },
{ name: "淄博", value: 85 },
{ name: "鞍山", value: 86 },
{ name: "溧阳", value: 86 },
{ name: "库尔勒", value: 86 },
{ name: "安阳", value: 90 },
{ name: "开封", value: 90 },
{ name: "济南", value: 92 },
{ name: "德阳", value: 93 },
{ name: "温州", value: 95 },
{ name: "九江", value: 96 },
{ name: "邯郸", value: 98 },
{ name: "临安", value: 99 },
{ name: "兰州", value: 99 },
{ name: "沧州", value: 100 },
{ name: "临沂", value: 103 },
{ name: "南充", value: 104 },
{ name: "天津", value: 105 },
{ name: "富阳", value: 106 },
{ name: "泰安", value: 112 },
{ name: "诸暨", value: 112 },
{ name: "郑州", value: 113 },
{ name: "哈尔滨", value: 114 },
{ name: "聊城", value: 116 },
{ name: "芜湖", value: 117 },
{ name: "唐山", value: 119 },
{ name: "平顶山", value: 119 },
{ name: "邢台", value: 119 },
{ name: "德州", value: 120 },
{ name: "济宁", value: 120 },
{ name: "荆州", value: 127 },
{ name: "宜昌", value: 130 },
{ name: "义乌", value: 132 },
{ name: "丽水", value: 133 },
{ name: "洛阳", value: 134 },
{ name: "秦皇岛", value: 136 },
{ name: "株洲", value: 143 },
{ name: "石家庄", value: 147 },
{ name: "莱芜", value: 148 },
{ name: "常德", value: 152 },
{ name: "保定", value: 153 },
{ name: "湘潭", value: 154 },
{ name: "金华", value: 157 },
{ name: "岳阳", value: 169 },
{ name: "长沙", value: 175 },
{ name: "衢州", value: 177 },
{ name: "廊坊", value: 193 },
{ name: "菏泽", value: 194 },
{ name: "合肥", value: 229 },
{ name: "武汉", value: 273 },
{ name: "大庆", value: 279 }
];
var geoCoordMap = {
海门: [121.15, 31.89],
鄂尔多斯: [109.781327, 39.608266],
招远: [120.38, 37.35],
舟山: [122.207216, 29.985295],
齐齐哈尔: [123.97, 47.33],
盐城: [120.13, 33.38],
赤峰: [118.87, 42.28],
青岛: [120.33, 36.07],
乳山: [121.52, 36.89],
金昌: [102.188043, 38.520089],
泉州: [118.58, 24.93],
莱西: [120.53, 36.86],
日照: [119.46, 35.42],
胶南: [119.97, 35.88],
南通: [121.05, 32.08],
拉萨: [91.11, 29.97],
云浮: [112.02, 22.93],
梅州: [116.1, 24.55],
文登: [122.05, 37.2],
上海: [121.48, 31.22],
攀枝花: [101.718637, 26.582347],
威海: [122.1, 37.5],
承德: [117.93, 40.97],
厦门: [118.1, 24.46],
汕尾: [115.375279, 22.786211],
潮州: [116.63, 23.68],
丹东: [124.37, 40.13],
太仓: [121.1, 31.45],
曲靖: [103.79, 25.51],
烟台: [121.39, 37.52],
福州: [119.3, 26.08],
瓦房店: [121.979603, 39.627114],
即墨: [120.45, 36.38],
抚顺: [123.97, 41.97],
玉溪: [102.52, 24.35],
张家口: [114.87, 40.82],
阳泉: [113.57, 37.85],
莱州: [119.942327, 37.177017],
湖州: [120.1, 30.86],
汕头: [116.69, 23.39],
昆山: [120.95, 31.39],
宁波: [121.56, 29.86],
湛江: [110.359377, 21.270708],
揭阳: [116.35, 23.55],
荣成: [122.41, 37.16],
连云港: [119.16, 34.59],
葫芦岛: [120.836932, 40.711052],
常熟: [120.74, 31.64],
东莞: [113.75, 23.04],
河源: [114.68, 23.73],
淮安: [119.15, 33.5],
泰州: [119.9, 32.49],
南宁: [108.33, 22.84],
营口: [122.18, 40.65],
惠州: [114.4, 23.09],
江阴: [120.26, 31.91],
蓬莱: [120.75, 37.8],
韶关: [113.62, 24.84],
嘉峪关: [98.289152, 39.77313],
广州: [113.23, 23.16],
延安: [109.47, 36.6],
太原: [112.53, 37.87],
清远: [113.01, 23.7],
中山: [113.38, 22.52],
昆明: [102.73, 25.04],
寿光: [118.73, 36.86],
盘锦: [122.070714, 41.119997],
长治: [113.08, 36.18],
深圳: [114.07, 22.62],
珠海: [113.52, 22.3],
宿迁: [118.3, 33.96],
咸阳: [108.72, 34.36],
铜川: [109.11, 35.09],
平度: [119.97, 36.77],
佛山: [113.11, 23.05],
海口: [110.35, 20.02],
江门: [113.06, 22.61],
章丘: [117.53, 36.72],
肇庆: [112.44, 23.05],
大连: [121.62, 38.92],
临汾: [111.5, 36.08],
吴江: [120.63, 31.16],
石嘴山: [106.39, 39.04],
沈阳: [123.38, 41.8],
苏州: [120.62, 31.32],
茂名: [110.88, 21.68],
嘉兴: [120.76, 30.77],
长春: [125.35, 43.88],
胶州: [120.03336, 36.264622],
银川: [106.27, 38.47],
张家港: [120.555821, 31.875428],
三门峡: [111.19, 34.76],
锦州: [121.15, 41.13],
南昌: [115.89, 28.68],
柳州: [109.4, 24.33],
三亚: [109.511909, 18.252847],
自贡: [104.778442, 29.33903],
吉林: [126.57, 43.87],
阳江: [111.95, 21.85],
泸州: [105.39, 28.91],
西宁: [101.74, 36.56],
宜宾: [104.56, 29.77],
呼和浩特: [111.65, 40.82],
成都: [104.06, 30.67],
大同: [113.3, 40.12],
镇江: [119.44, 32.2],
桂林: [110.28, 25.29],
张家界: [110.479191, 29.117096],
宜兴: [119.82, 31.36],
北海: [109.12, 21.49],
西安: [108.95, 34.27],
金坛: [119.56, 31.74],
东营: [118.49, 37.46],
牡丹江: [129.58, 44.6],
遵义: [106.9, 27.7],
绍兴: [120.58, 30.01],
扬州: [119.42, 32.39],
常州: [119.95, 31.79],
潍坊: [119.1, 36.62],
重庆: [106.54, 29.59],
台州: [121.420757, 28.656386],
南京: [118.78, 32.04],
滨州: [118.03, 37.36],
贵阳: [106.71, 26.57],
无锡: [120.29, 31.59],
本溪: [123.73, 41.3],
克拉玛依: [84.77, 45.59],
渭南: [109.5, 34.52],
马鞍山: [118.48, 31.56],
宝鸡: [107.15, 34.38],
焦作: [113.21, 35.24],
句容: [119.16, 31.95],
北京: [116.46, 39.92],
徐州: [117.2, 34.26],
衡水: [115.72, 37.72],
包头: [110, 40.58],
绵阳: [104.73, 31.48],
乌鲁木齐: [87.68, 43.77],
枣庄: [117.57, 34.86],
杭州: [120.19, 30.26],
淄博: [118.05, 36.78],
鞍山: [122.85, 41.12],
溧阳: [119.48, 31.43],
库尔勒: [86.06, 41.68],
安阳: [114.35, 36.1],
开封: [114.35, 34.79],
济南: [117, 36.65],
德阳: [104.37, 31.13],
温州: [120.65, 28.01],
九江: [115.97, 29.71],
邯郸: [114.47, 36.6],
临安: [119.72, 30.23],
兰州: [103.73, 36.03],
沧州: [116.83, 38.33],
临沂: [118.35, 35.05],
南充: [106.110698, 30.837793],
天津: [117.2, 39.13],
富阳: [119.95, 30.07],
泰安: [117.13, 36.18],
诸暨: [120.23, 29.71],
郑州: [113.65, 34.76],
哈尔滨: [126.63, 45.75],
聊城: [115.97, 36.45],
芜湖: [118.38, 31.33],
唐山: [118.02, 39.63],
平顶山: [113.29, 33.75],
邢台: [114.48, 37.05],
德州: [116.29, 37.45],
济宁: [116.59, 35.38],
荆州: [112.239741, 30.335165],
宜昌: [111.3, 30.7],
义乌: [120.06, 29.32],
丽水: [119.92, 28.45],
洛阳: [112.44, 34.7],
秦皇岛: [119.57, 39.95],
株洲: [113.16, 27.83],
石家庄: [114.48, 38.03],
莱芜: [117.67, 36.19],
常德: [111.69, 29.05],
保定: [115.48, 38.85],
湘潭: [112.91, 27.87],
金华: [119.64, 29.12],
岳阳: [113.09, 29.37],
长沙: [113, 28.21],
衢州: [118.88, 28.97],
廊坊: [116.7, 39.53],
菏泽: [115.480656, 35.23375],
合肥: [117.27, 31.86],
武汉: [114.31, 30.52],
大庆: [125.03, 46.58],
台北: [121.514449, 25.028879],
高雄: [120.285385, 22.643712],
嘉义: [120.457101, 23.474124]
};
var convertData = function (data) {
var res = [];
for (var i = 0; i < data.length; i++) {
var geoCoord = geoCoordMap[data[i].name];
if (geoCoord) {
res.push({
name: data[i].name,
value: geoCoord.concat(data[i].value)
});
}
}
return res;
};
var option = {
// amap component option
amap: {
// enable 3D mode
viewMode: "3D",
// initial options of AMap
// See https://lbs.amap.com/api/javascript-api/reference/map#MapOption for details
// initial map center [lng, lat]
center: [108.39, 39.9],
// initial map zoom
zoom: 4,
// whether the map and echarts automatically handles browser window resize to update itself.
resizeEnable: true,
// customized map style, see https://lbs.amap.com/dev/mapstyle/index for details
mapStyle: "amap://styles/dark",
// whether echarts layer should be rendered when the map is moving. Default is true.
// if false, it will only be re-rendered after the map `moveend`.
// It's better to set this option to false if data is large.
renderOnMoving: true,
// the zIndex of echarts layer for AMap, default value is 2000.
// deprecated since v1.9.0, use `echartsLayerInteractive` instead.
// echartsLayerZIndex: 2019,
// whether echarts layer is interactive. Default value is true
// supported since v1.9.0
echartsLayerInteractive: true,
// whether to enable large mode. Default value is false
// supported since v1.9.0
largeMode: false,
// whether to return map camera state in `amaproam` event.
// supported since v1.10.0
returnMapCameraState: true,
// Note: Please DO NOT use the initial option `layers` to add Satellite/RoadNet/Other layers now.
// There are some bugs about it, we can use `amap.add` instead.
// Refer to the codes at the bottom.
// More initial options...
},
tooltip: {
trigger: "item"
},
animation: false,
series: [
{
name: "PM2.5",
type: "scatter",
// use `amap` as the coordinate system
coordinateSystem: "amap",
// data items [[lng, lat, value], [lng, lat, value], ...]
data: convertData(data),
symbolSize: function (val) {
return val[2] / 10;
},
encode: {
value: 2
},
label: {
formatter: "{b}",
position: "right",
show: false
},
itemStyle: {
color: "#00c1de"
},
emphasis: {
label: {
show: true
}
}
},
{
name: "Top 5",
type: "effectScatter",
coordinateSystem: "amap",
data: convertData(
data
.sort(function (a, b) {
return b.value - a.value;
})
.slice(0, 6)
),
symbolSize: function (val) {
return val[2] / 10;
},
encode: {
value: 2
},
showEffectOn: "render",
rippleEffect: {
brushType: "stroke"
},
hoverAnimation: true,
label: {
formatter: "{b}",
position: "right",
show: true
},
itemStyle: {
color: "rgba(255, 165, 0, 0.8)",
shadowBlur: 10,
shadowColor: "#333"
},
zlevel: 1
}
]
};
// initialize echart
var chart = echarts.init(document.getElementById("echarts-amap"));
chart.setOption(option);
// listen to AMap roam event
// `returnMapCameraState` should be opened if map camera information is needed
chart.on('amaproam', function(e) {
console.log(e);
});
// get AMap extension component
var amapComponent = chart.getModel().getComponent('amap');
// get the instance of AMap
var amap = amapComponent.getAMap();
// add some controls provided by AMap.
amap.addControl(new AMap.Scale());
amap.addControl(new AMap.ToolBar());
// add SatelliteLayer and RoadNetLayer to map
// var satelliteLayer = new AMap.TileLayer.Satellite();
// var roadNetLayer = new AMap.TileLayer.RoadNet();
// amap.add([satelliteLayer, roadNetLayer]);
// Add a marker to map
amap.add(new AMap.Marker({
position: [110, 35]
}));
// Make the overlay layer of AMap interactive
// amapComponent.setEChartsLayerInteractive(false);
\ No newline at end of file
<template>
<!-- -->
<div class="force">
<div class="map">
<FansceMap v-if="cityObj" :mapStyle="mapStyle" :optionList="optionList" :cityObj="cityObj" />
<ForceMap v-else :mapStyle="mapStyle" :optionList="optionList" :cityObj="cityObj" @setrankList="setrankList" title="铁粉数"/>
</div>
<div class="user-rank-list" ref="user-rank-list">
<RankList title="铁粉榜" @clear="clear" :cityObj="cityObj" listTitle="铁粉数" :rankData="rankData" @rankFans="rankFans"/>
</div>
<div class="force-slide" :style="`right:${$refs['user-rank-list'].offsetWidth+50}px;`" v-if="cityObj" @click="clear">
<img src="@/assets/img/fansleft.png" alt="">
返回
</div>
</div>
</template>
<script>
import ForceMap from './mapForce.vue'
import FansceMap from './mapFans.vue'
import RankList from "./rankList.vue";
import { getHardcoreFanInfo,getFanDistribution } from '@/server/screen-data'
export default {
data() {
return {
optionList:[],
rankData:[],
rankList:[],
cityObj:'',
dataList:[],
city:'全国',
mapStyle:{
width:'1000',
height:'900',
tooltip:true,
zoom:1.5
},
}
},
methods: {
setrankList(data){
this.rankData = data.list
this.city = data.city
},
getlist(){
getHardcoreFanInfo().then((res) => {
if (res.status == 200){
this.rankData = res.data.data.countryTop
this.rankList = res.data.data.countryTop
this.dataList = res.data.data.cityInfoList
this.optionList = res.data.data.cityInfoList
}
}).catch(() => {
})
},
clear() {
this.cityObj = null
this.optionList = this.dataList
this.rankData = this.rankList
},
rankFans(item) {
getFanDistribution(item.username).then(re=>{
if (re.status === 200){
this.optionList = re.data.data.map(it=>{
return {
name:it.city,
value:it.gps.concat(it.score),
uv:it.score
}
})
this.cityObj = item
}
})
}
},
mounted(){
this.getlist()
},
created(){
this.mapStyle.width = document.documentElement.clientWidth
this.mapStyle.height = document.documentElement.clientHeight-66
},
components: {
ForceMap,
RankList,
FansceMap
},
}
</script>
<style scoped lang="scss">
.force {
width: 100%;
display: flex;
justify-content: space-between;
position: relative;
.user-rank-list{
position: absolute;
top: 40px;
right: 40px;
}
.force-slide{
// padding: 10px;
width: 64px;
height: 40px;
background: #364366;
color:#78C1FF;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 50px;
// left: 20px;
font-size: 14px;
border: 2px solid #5f97d5;
cursor: pointer;
img{
width: 12px;
height: 16px;
}
}
// .user-rank-list {
// margin-top: 40px;
// margin-right: 72px;
// }
}
</style>
\ No newline at end of file
<template>
<!-- -->
<div class="force">
<div class="map">
<ForceMap :optionList="optionList" :mapStyle="mapStyle" title="原力值" @setrankList="setrankList"/>
</div>
<div class="user-rank-list">
<RankList title="用户原力榜" listTitle="原力值" :city="city" :rankData="rankData"/>
</div>
</div>
</template>
<script>
import ForceMap from './mapForce.vue'
import RankList from "./rankList.vue";
import { getForceInfo } from '@/server/screen-data'
export default {
data() {
return {
optionList: [],
rankData: [],
city:'全国',
mapStyle:{
width:'1000',
height:'900',
tooltip:true,
zoom:1.5
},
}
},
methods: {
getlist() {
getForceInfo().then((res) => {
if (res.status == 200) {
this.optionList = res.data.data.cityInfoList
this.rankData = res.data.data.countryTop
}
}).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="csdn-datav">
<div class="csdn-datav-header">
<div class="time">
<p>
{{ time.hour > 9 ? time.hour : "0" + time.hour }}:{{
time.min > 9 ? time.min : "0" + time.min
}}:{{ time.seconds > 9 ? time.seconds : "0" + time.seconds }}
</p>
<dl>
<dd>{{ time.days }}</dd>
<dt>
{{ time.year }}-{{
time.month > 9 ? time.month : "0" + time.month
}}-{{ time.day > 9 ? time.day : "0" + time.day }}
</dt>
</dl>
</div>
<div class="title">
<span>{{ this.$route.meta.name }}</span>
<el-popover
placement="bottom"
popper-class="map-popover-pc"
trigger="hover"
v-model="visible">
<div class="map-popover-box">
{{popoverItem.desc}}
<a :href="popoverItem.url" target="_blank">详情 ></a>
</div>
<img src="@/assets/img/question-pc.png" slot="reference" alt="">
</el-popover>
</div>
<div class="nav">
<div class="nav-box"><span class="nav-img"></span> 导航
<div class="nav-list">
<div v-for="(it, index) in routerList"
:key="index">
<router-link
v-if="it.meta"
tag="p"
:to="it.path"
>{{ it.meta.navTitle }}</router-link>
<a :href="it.url" v-else target="_blank">{{it.title}}</a>
</div>
</div>
</div>
</div>
</div>
<div class="csdn-datav-body">
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
data() {
return {
visible:false,
routerList: [],
popoverList:[
{
key:'/fans',
desc:'CSDN铁粉介绍以及说明',
url:'https://bbs.csdn.net/topics/606708603'
},
{
key:'/force',
desc:'原力分数的增减原则',
url:'https://bbs.csdn.net/topics/602534373'
}
],
popoverItem:'',
time: {
year: '',
month: '',
day: '',
hour: '',
min: '',
seconds: '',
days: '',
}
}
},
methods: {
getCurrentDate() {
var myDate = new Date();
var year = myDate.getFullYear(); //年
var month = myDate.getMonth() + 1; //月
var day = myDate.getDate(); //日
var hour = myDate.getHours(); //时
var min = myDate.getMinutes(); //分
var seconds = myDate.getSeconds(); //秒
var days = myDate.getDay();
switch (days) {
case 1:
days = '星期一';
break;
case 2:
days = '星期二';
break;
case 3:
days = '星期三';
break;
case 4:
days = '星期四';
break;
case 5:
days = '星期五';
break;
case 6:
days = '星期六';
break;
case 0:
days = '星期日';
break;
}
this.time = {
year,
month,
day,
hour,
min,
seconds,
days,
}
}
},
beforeUpdate(){
this.popoverItem = this.popoverList.find(it=>it.key === this.$route.path)
},
created(){
this.getCurrentDate()
},
mounted() {
this.popoverItem = this.popoverList.find(it=>it.key === this.$route.path)
this.routerList = this.$router.options.routes.find(it => it.name === 'pcmap').children.filter(it => it.meta)
this.routerList.push({
title:'参与项目',
url:'https://gitcode.net/csdn/csdn-datav'
})
setInterval(() => {
this.getCurrentDate()
}, 1000)
}
}
</script>
<style lang="scss">
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.map-popover-pc{
background:#364366 ;
border-color: #364366;
margin-top: 20px !important;
.popper__arrow,.popper__arrow::after{
border-bottom-color: #364366 !important;
// border-color: #364366 !important;
}
.map-popover-box{
color: #fff;
font-size: 14px;
a{
color: #77C0FF;
font-size: 14px;
}
}
}
html,
body,
.app {
width: 100%;
height: 100%;
}
.csdn-datav {
height: 100%;
display: flex;
flex-direction: column;
background: #0B1224;
background-image: url("@/assets/img/map-back.png");
background-size: cover;
.csdn-datav-header {
display: flex;
align-items: center;
justify-content: space-between;
color: #77c0ff;
// padding: 0 30px;
height: 64px;
.nav {
display: flex;
height: 100%;
background: #1E2F56;
min-width: 100px;
flex: 1;
align-items: center;
justify-content: end;
border-bottom: 1px solid #375A87;
padding-right: 40px;
.nav-list {
display: none;
position: absolute;
top: 62px;
width: 120px;
// height: 100px;
padding: 4px 0;
left: -34px;
background: #1b2b4f;
border: 2px solid #365a87;
z-index: 10;
p,a {
display: block;
font-size: 16px;
font-weight: 500;
color: #77c0ff;
height: 44px;
width: 100%;
line-height: 44px;
text-align: center;
&:hover,&.router-link-active {
background: #141f38;
}
}
}
.nav-box{
display: flex;
align-items: center;
position: relative;
height: 100%;
cursor: pointer;
&:hover {
.nav-list {
display: block;
}
span {
color: #77c0ff;
}
.nav-img {
background: url("@/assets/img/nav-hover.png");
background-size: cover;
}
}
}
.nav-img {
width: 14px;
height: 16px;
background: url("@/assets/img/nav.png");
background-size: cover;
margin-right: 4px;
}
}
.time {
padding-left: 40px;
min-width: 330px;
background: #1E2F56;
border-bottom: 1px solid #375A87;
flex: 1;
font-size: 36px;
font-weight: 500;
display: flex;
height: 100%;
align-items: center;
p {
width: 150px;
}
dl {
font-size: 14px;
font-weight: 400;
margin-left: 16px;
}
}
.title{
// flex: 1;
width:890px;
text-align: center;
background-image: url("@/assets/img/nav-bak.png");
background-size: cover;
height: 100%;
line-height: 64px;
display: flex;
align-items: center;
justify-content: center;
img{
width: 20px;
height: 20px;
margin-top: 26px;
margin-left: 4px;
}
}
}
.csdn-datav-body {
flex: 1;
// overflow: auto;
}
}
</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 * as echarts from "echarts";
// 引入中国,城市地图
import { chinaJson,cityArr } from "../../../public/echarts/city.js"
export default {
data() {
return {
cityData: cityArr,
flag: true,
initOptions: {
renderer: 'canvas'
},
pointArray: [],
GLOptions: null,
normalDistributionNumber: 150, // 正态分布扩散数量
dataArray: [],
geoGPS: {},
oldMapData: {},
isLoadFlights: true, // 是否设置echrts配置项
};
},
// props: ['optionList','cityObj'],
props:{
cityObj: {
type: Object,
default: () => {
return {}
}
},
type: {
type: String,
default: () => {
return ''
}
},
optionList: {
type: Array,
default: () => {
return []
}
},
mapStyle: {
type: Object,
default: () => {
return {
width:'',
height:'900',
tooltip:true,
zoom:1.2,
}
}
},
},
watch: {
optionList() {
this.setEchartOption();
},
},
methods: {
convertData(data) {
var res = []
if (this.optionList.length > 0) {
res = this.optionList.map(it => {
return [
{
coord: this.cityObj.gps,
},
{
coord: it.value,
name: it.city,
value: it.uv,
}
]
})
}
return res
},
setEchartOption() {
this.myEchart = echarts.init(document.getElementById("china_map"));
echarts.registerMap('chinas', chinaJson);
var series = [
{
type: 'scatter', //样试
coordinateSystem: 'geo', //该系列使用的坐标系
data: this.cityData,
label: {
formatter: "{b}",
position: "",
show: true,
},
//标记的大小,可以设置数组或者函数返回值的形式
symbolSize: function (val) {
return 0;
},
hoverAnimation: false, //鼠标移入放大圆
tooltip: {
show: false
},
encode: {
value: 10
},
showEffectOn: "render",
hoverAnimation: false, // 动画
label: {
formatter: "{b}",
position: "",
show: true,
textStyle: { //图例文字的样式
color: '#fff',
fontSize: 12
},
},
zlevel: 1
},
{
name: 'totip',
type: 'lines',
coordinateSystem: 'geo',
zlevel: 10,
effect: {
show: false,
period: 4, //箭头指向速度,值越小速度越快
trailLength: .2, //特效尾迹长度[0,1]值越大,尾迹越长重
symbol: 'arrow', //箭头图标
symbolSize: 0, //图标大小
color: '#FFB932', // 图标颜色
},
lineStyle: {
normal: {
show: true,
width: 1, //尾迹线条宽度
opacity: .8, //尾迹线条透明度
curveness: 0.2, //尾迹线条曲直度
color: new echarts.graphic.LinearGradient(0,1,1,0,
[
{
offset: 1,
color: "#FFB932",
},
{
offset: 0,
color: "#26FFF3",
},
],
),
// color: '#26FFF3', // 飞线颜色
},
},
data: this.convertData(),
},
{
type: 'effectScatter',
name: 'totip',
coordinateSystem: 'geo',
zlevel: 1,
rippleEffect: {
//涟漪特效
period: 4, //动画时间,值越小速度越快
brushType: 'fill', //波纹绘制方式 stroke, fill
scale: 6, //波纹圆环最大限制,值越大波纹越大
color: '#0CB5AD',
},
label: {
normal: {
show: false,
position: 'right', //显示位置
offset: [5, 0], //偏移设置
formatter: function (params) {
//圆环显示文字
return params.data.name
},
fontSize: 13,
},
emphasis: {
show: false,
},
},
symbol: 'circle',
symbolSize: function (val) {
// console.log(val,8888888)
if (val[2]<50){
return 4
}
if (val[2]<100){
return 5
}
if (val[2]<200){
return 6
}
if (val[2]<500){
return 7
}
if (val[2]>1000){
return 10
}
},
itemStyle: {
normal: {
show: false,
color: '#0CB5AD',
},
},
data: this.optionList,
},
//中心点
{
type: 'effectScatter',
coordinateSystem: 'geo',
zlevel: 15,
rippleEffect: {
period: 4,
brushType: 'fill',
scale: 4,
color: '#FFB932',//圆环颜色
},
label: {
normal: {
show: true,
position: 'bottom',
//offset:[5, 0],
color: '#FFB932',
formatter: '{b}',
textStyle: {
color: '#fff',
fontSize: 12,
},
},
// emphasis: {
// show: false,
// color: '#FFB932',
// },
},
symbol: 'circle',
symbolSize: 15,
itemStyle: {
color: '#FFB932',//中心点颜色
},
data:[
{
name: this.cityObj.city || '',
value: this.cityObj.gps || '',
},
] ,
}
]
var that = this
var option = {
geo: {
map: 'chinas',
zoom: this.type === 'wap' ? 4 :this.mapStyle.zoom, // 地图缩放率
roam: true,
center: this.type === 'wap' ?this.cityObj.gps||[116.40529, 39.904987]:[120.40529, 35.904987],
silent: 'move', // 拖拽以及缩放
itemStyle: {
normal: {
areaColor: '#1E2F56', // 地图颜色
borderColor: '#33547E', // 地图边界颜色
borderWidth: .5
}
},
},
scaleLimit:{ //所属组件的z分层,z值小的图形会被z值大的图形覆盖
min:0.8, //最小的缩放值
max:10, //最大的缩放值
},
series: series,
tooltip: {
trigger: "item",
borderColor: 'transparent',
show: this.mapStyle.tooltip,
transitionDuration: 0,
showContent: true,
enterable: true,
backgroundColor: 'transparent', // 提示框浮层的背景颜色。
axisPointer: { // 坐标轴指示器配置项。
type: 'shadow', // '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 obj = that.optionList[params.dataIndex]
var str = ''
console.log(params,9999)
// if (params.seriesName==='totip'){
if (params.name === that.cityObj.city){
str = `<a class="popover-title" href="https://blog.csdn.net/${that.cityObj.username}" target="_blank"> <img src="${that.cityObj.avatar}" alt=""> <span>${that.cityObj.nickname||that.cityObj.username}</span></a>
<p class="popover-city"><i></i><span class="city">${that.cityObj.city}</span> <span>铁粉数</span> <span class="num">${that.cityObj.score}</span></p>
`
}else{
str = `<p class="popover-title">地区</p>
<p class="popover-city"><i></i><span class="city">${obj.name}</span> <span>铁粉数</span> <span class="num">${obj.uv}</span></p>`
}
return (
`<div class="popover-fans">
<div class="popover-top">
${str}
</div>
</div>`
);
// }
},
},
};
this.myEchart.setOption(option);
// 自适应
window.addEventListener("resize", () => {
if (this.myEchart) {
this.myEchart.resize();
}
});
// 点击地图
},
},
beforeDestroy(){
this.myEchart && this.myEchart.clear();
},
created(){
},
mounted() {
// this.mapStyle.width = document.documentElement.clientWidth
// this.mapStyle.height = document.documentElement.clientHeight
this.setEchartOption();
this.myEchart.on('click', (params) => {
this.$emit('fansObj',{obj:this.optionList[params.dataIndex],flag:params.name === this.cityObj.city})
})
},
};
</script>
<style lang="scss">
html,
body {
width: 100%;
height: 100%;
}
.map-city {
width: 360px;
height: 258px;
}
// #china_map {
// width: 1200px;
// height: 900px;
// }
.popover-fans {
width: 250px;
height: 94px;
background-image: url("@/assets/img/popover-fans.png");
background-size: cover;
padding-top: 10px;
.popover-title {
font-size: 16px;
font-weight: 500;
color: #77c0ff;
height: 36px;
line-height: 36px;
padding: 0 16px;
display: flex;
align-items: center;
img{
height: 24px;
width: 24px;
border-radius: 50%;
margin-right: 8px;
}
span{
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
word-break: break-all;
}
}
.popover-city {
height: 44px;
line-height: 44px;
padding: 0 16px;
display: flex;
align-items: center;
i {
display: inline-block;
width: 16px;
height: 16px;
background-image: url("@/assets/img/place.png");
background-size: cover;
}
span {
font-size: 14px;
font-weight: 500;
color: #77c0ff;
&.city {
margin-right: 48px;
}
&.num {
color: #29ffa3;
}
}
}
}
.csdn-map {
// .popover {
// width: 360px;
// height: 258px;
// background-image: url("@/assets/img/popover.png");
// background-size: cover;
// padding-top: 10px;
// .popover-title {
// font-size: 16px;
// font-weight: 500;
// color: #77c0ff;
// height: 36px;
// line-height: 36px;
// padding: 0 16px;
// }
// .popover-city {
// height: 44px;
// line-height: 44px;
// padding: 0 16px;
// span {
// font-size: 14px;
// font-weight: 500;
// color: #77c0ff;
// &.city {
// margin-right: 48px;
// }
// &.num {
// color: #29ffa3;
// }
// }
// }
// .exhibition {
// height: 40px;
// display: flex;
// align-items: center;
// justify-content: space-between;
// padding: 0 16px;
// p {
// font-size: 14px;
// font-weight: 500;
// color: rgba(119, 192, 255, 0.7);
// }
// &.active {
// p {
// color: #77c0ff;
// }
// .num {
// color: #29ffa3;
// }
// &:hover {
// background: rgba(95, 151, 213, 0.3);
// cursor: pointer;
// }
// }
// }
// }
}
/* .wrapper{
background: #0f0;
} */
</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 * as echarts from "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 /3000
}else{
dataObj.value = resObj.score/40
}
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() {
this.myEchart = echarts.init(document.getElementById("china_map"));
echarts.registerMap('chinas', chinaJson);
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: {
normal: {
areaColor: '#1E2F56', // 地图颜色
borderColor: '#33547E', // 地图边界颜色
borderWidth: .5
}
},
scaleLimit:{ //所属组件的z分层,z值小的图形会被z值大的图形覆盖
min:0.8, //最小的缩放值
max:10, //最大的缩放值
}
},
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.username}" target="_blank" class="exhibition active"><p>${index+1}</p> <p class="img"> <img src="${item.avatar}" alt=""> <span>${item.nickname || item.username}</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">上榜用户</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',
hoverAnimation: true,
label: {
formatter: '{b}',
position: 'left',
show: true,
textStyle: { //图例文字的样式
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: 16px;
font-weight: 500;
color: #77c0ff;
height: 36px;
line-height: 36px;
padding: 0 16px;
}
.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.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{
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-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
<template>
<div class="rank-list" ref="rank-list">
<div class="title">
<span>{{ title }}</span>
<!-- <span class="clear" @click="clear" v-if="cityObj">回退</span> -->
<p v-if="city&&!userItem"><img src="@/assets/img/place-pc.png" alt="" /><span style="font-size: 16px;">{{city}}</span></p>
<p v-if="userItem"><img src="@/assets/img/user.png" alt="" /><a target="_blank" :href="`https://blog.csdn.net/${userItem.username}`">{{userItem.nickname||userItem.username}}</a></p>
</div>
<div class="rank-list-nav border">
<span class="mar">排名</span>
<p><span>明星成员</span></p>
<span>城市</span><span>{{listTitle}}</span>
</div>
<ul class="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>
<img :src="item.avatar" alt="" /><span
>{{item.nickname || item.username}}</span
>
</p>
<span>{{item.city}}</span><span>{{item.score}}</span>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
userItem:''
}
},
methods: {
clear () {
this.$emit('clear')
},
rankAdd(item){
if (this.title == '用户原力榜') {
window.open(`https://blog.csdn.net/`+item.username)
}else{
this.userItem = item
this.$emit('rankFans',item)
}
}
},
props: ['title','rankData','city','listTitle','cityObj']
}
</script>
<style scoped lang="scss">
.rank-list {
width: 400px;
height: 800px;
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;
}
p {
display: flex;
align-items: center;
max-width: 200px;
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:1500px){
.rank-list {
// width: 368px;
height: 645px;
// .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="rank-list">
<div class="title">
<span>用户原力榜</span>
<p><img src="" alt="" /><span>北京市</span></p>
</div>
</div>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
}
}
</script>
<style scoped lang="scss">
.rank-list {
width: 480px;
height: 870px;
background-image: url("@/assets/img/rightslide.png");
}
</style>
\ No newline at end of file
<template>
<!-- -->
<div class="force ranklist">
<div class="go back" @click="clear" v-if="cityObj">
<img src="@/assets/img/backoff.png" alt="">
</div>
<div class="map">
<FansceMap v-if="cityObj" type="wap" :mapStyle="mapStyle" :optionList="optionList" :cityObj="cityObj" @fansObj="fansObj"/>
<ForceMap v-else :optionList="optionList" type="wap" :mapStyle="mapStyle" :cityObj="cityObj" @setrankList="setrankList" title="铁粉数"/>
</div>
<div class="user-rank-list" ref="user-rank-list">
<RankList title="铁粉榜" :cityObj="cityObj" :city="city" listTitle="铁粉数" @listOpen="listOpen" :rankData="rankData" @rankFans="rankFans"/>
</div>
<van-popup v-model="Popup"
>
<div class="popover-fans">
<div class="popover-top">
<p class="popover-title" v-if="fansData.flag"> <a target="_blank" :href="`https://blog.csdn.net/${cityObj.username}`"><img :src="cityObj.avatar" alt=""> <span>{{cityObj.nickname||cityObj.username}}</span></a> </p>
<p class="popover-title" v-else>地区</p>
<p class="popover-city"><i></i><span class="city">{{fansData.obj.name}}</span> <span>铁粉数</span> <span class="num">{{fansData.obj.uv}}</span></p>
</div>
</div>
<div class="close" @click="Popup = false"><img src="@/assets/img/close-circle.png" alt=""></div>
</van-popup>
</div>
</template>
<script>
import ForceMap from '../pc/mapForce.vue'
import FansceMap from '../pc/mapFans.vue'
import RankList from "./rankList.vue";
import { getHardcoreFanInfo,getFanDistribution } from '@/server/screen-data'
export default {
data() {
return {
optionList:[],
rankData:[],
rankList:[],
Popup:false,
cityObj:'',
dataList:[],
show:true,
city:'全国',
listFlag:false,
mapStyle:{
width:'',
height:'',
tooltip:false,
zoom:1
},
listStyle:'',
positionY:'',
mapbottom:'',
fansData:{
obj:{}
}
}
},
methods: {
setrankList(data){
this.rankData = data.list
this.city = data.city
},
fansObj (obj) {
this.fansData = obj
setTimeout(()=>{
this.Popup = true
},100)
},
listOpen (val) {
var height = -(this.$refs['user-rank-list'].offsetHeight-300)
if (val){
height = 34
}
this.$refs['user-rank-list'].style=`bottom:${height}px;`
},
getlist(){
getHardcoreFanInfo().then((res) => {
if (res.status == 200){
this.rankData = res.data.data.countryTop
this.rankList = res.data.data.countryTop
this.dataList = res.data.data.cityInfoList
this.optionList = res.data.data.cityInfoList
}
}).catch(() => {
})
},
clear() {
this.cityObj = null
this.optionList = this.dataList
this.rankData = this.rankList
},
rankFans(item) {
getFanDistribution(item.username).then(re=>{
if (re.status === 200){
this.optionList = re.data.data.map(it=>{
return {
name:it.city,
value:it.gps.concat(it.score),
uv:it.score
}
})
this.Popup = false
this.cityObj = item
}
})
}
},
created () {
this.mapStyle.width = document.documentElement.clientWidth
this.mapStyle.height = document.documentElement.clientHeight
},
mounted(){
this.getlist()
this.listOpen(false)
},
components: {
ForceMap,
RankList,
FansceMap
},
}
</script>
<style scoped lang="scss">
.force{
position: relative;
overflow: hidden;
.popover-fans{
width: 275px !important;
height: 94px !important;
.popover-title a{
color: #77c0ff;
display: flex;
align-items: center;
}
}
}
</style>
\ No newline at end of file
<template>
<!-- -->
<div class="force ranklist">
<div class="map">
<ForceMap :optionList="optionList" type="wap" :mapStyle="mapStyle" title="原力值" @setrankList="setrankList"/>
</div>
<div class="user-rank-list" ref="user-rank-list">
<RankList title="用户原力榜" listTitle="原力值" :city="city" @listOpen="listOpen" :rankData="rankData"/>
</div>
</div>
</template>
<script>
import ForceMap from '../pc/mapForce.vue'
import RankList from "./rankList.vue";
import { getForceInfo } from '@/server/screen-data'
export default {
data() {
return {
optionList: [],
rankData: [],
Popup:false,
city:'全国',
mapStyle:{
width:'',
height:'',
tooltip:false,
zoom:1
}
}
},
methods: {
listOpen (val) {
var height = -(this.$refs['user-rank-list'].offsetHeight-300)
if (val){
height = 48
}
this.$refs['user-rank-list'].style=`bottom:${height}px;`
},
getlist() {
getForceInfo().then((res) => {
if (res.status == 200) {
this.optionList = res.data.data.cityInfoList
this.rankData = res.data.data.countryTop
}
}).catch(() => {
})
},
setrankList(data){
this.rankData = data.list
this.city = data.city
}
},
created () {
this.mapStyle.width = document.documentElement.clientWidth
this.mapStyle.height = document.documentElement.clientHeight
},
mounted() {
this.getlist()
this.listOpen(false)
},
components: {
ForceMap,
RankList
},
}
</script>
<style scoped lang="scss">
// .user-rank-list{
// position: fixed;
// }
.popup-btn{
position: absolute;
bottom: 20px;
background: #0f0;
}
</style>
\ No newline at end of file
<template>
<!-- -->
<div class="csdn-datav-m" @click="navAdd(true)">
<div class="nav back" @click.stop="navAdd(navFlag)" style="z-index:13;">
<div class="nav-box">
<img src="@/assets/img/menu-m.png" alt="">
<div class="nav-list" v-if="navFlag">
<div v-for="(it, index) in routerList"
:key="index">
<router-link
v-if="it.path"
tag="p"
:to="it.path"
>{{ it.meta.navTitle }}</router-link
>
<a :href="it.url" v-else target="_blank">{{it.title}}</a>
</div>
</div>
</div>
</div>
<div class="share back" @click="shareCopy" id="copy_title" :data-clipboard-text="clipbText">
<img src="@/assets/img/share.png" alt="">
</div>
<a v-if="popoverItem.url" :href="popoverItem.url" target="_blank" class="help back" >
<img src="@/assets/img/question-wap.png" alt="">
</a>
<router-view></router-view>
<!-- <van-popup v-model="Popup"
>
<div class="popup-content">
<div class="popover-top">
<p class="popover-title">{{popupObj.title}} </p>
<p class="popover-city"><i class="el-icon-location-outline"></i><span class="city">{{popupObj.obj.name}}</span> <span>{{popupObj.navname}}</span> <span class="num">{{popupObj.obj.score}}</span></p>
<div class="exhibition"><p>排名</p> <p class="img">用户</p> <p>{{popupObj.navname}}</p></div>
<div v-if="popupObj.obj.list&&popupObj.obj.list.length>0"><a :href="`https://blog.csdn.net/${item.username}`" v-for="(item,index) in popupObj.obj.list" :key="index" target="_blank" class="exhibition active"><p>{{index+1}}</p> <p class="img"> <img :src="item.avatar" alt=""> <span>{{item.nickname||item.username}}</span></p> <p class="num">{{item.score}}</p></a></div>
<div class="popover-empty" v-else>
<span>暂无上榜</span>
<a href="" target="_blank">前往查看热榜更新规则</a>
</div>
</div>
</div>
<div class="close" @click="Popup = false"><img src="@/assets/img/close-circle.png" alt=""></div>
</van-popup> -->
</div>
</template>
<script>
import Clipboard from 'clipboard'
export default {
data(){
return {
routerList:[],
navFlag:false,
Popup:false,
clipbText:'',
popoverList:[
{
key:'/m/fans',
desc:'',
url:'https://bbs.csdn.net/topics/606708603'
},
{
key:'/m/force',
desc:'',
url:'https://bbs.csdn.net/topics/602534373'
}
],
popoverItem:{},
popupObj:{
obj:{},
title:'',
navname:''
}
}
},
methods: {
shareCopy () {
var clipboard = new Clipboard("#copy_title");
clipboard.on("success", (e) => {
this.$toast.success('复制成功');
clipboard.destroy();
});
clipboard.on("error", (e) => {
this.$toast.fail('该浏览器不支持复制');
clipboard.destroy();
});
},
navAdd(flag){
this.navFlag = !flag
}
},
beforeUpdate(){
this.popoverItem = this.popoverList.find(it=>it.key === this.$route.path)
},
mounted (){
this.popoverItem = this.popoverList.find(it=>it.key === this.$route.path)
this.routerList = this.$router.options.routes.find(it => it.name === 'pcmap').children.filter(it => it.meta)
this.routerList.push({
title:'参与项目',
url:'https://gitcode.net/csdn/csdn-datav'
})
this.clipbText = window.location.href
}
}
</script>
<style lang="scss">
html,body,.app{
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
p{
margin: 0;
}
.ranklist{
position: relative;
.user-rank-list{
width: 100%;
position: absolute;
height: 80%;
}
}
.csdn-datav-m{
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
background: #0B1224;
background-image: url("@/assets/img/map-back.png");
background-size: cover;
.back{
width: 36px;
height: 36px;
background: #2C3547;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
img{
height: 24px;
}
position: absolute;
}
.help{
right: 10px;
top: 102px;
}
.go{
left: 10px;
top: 10px;
}
.nav-box{
height: 36px;
display: flex;
align-items: center;
position: relative;
.nav-list{
width: 100px;
// height: 200px;
position: absolute;
top: 45px;
background: #2C3547;
border-radius: 10px;
right: -7px;
padding: 4px 0;
z-index: 20;
p,a{
display: block;
font-size: 14px;
font-weight: 500;
color: #B2BCCF;
height:40px ;
line-height: 40px;
text-align: center;
&.router-link-active{
background: rgba(0,0,0,0.2000);
color: #FFFFFF;
}
}
}
}
.popup-box{
position: absolute;
bottom:20px;
background: #0d0;
width: 200px;
height: 40px;
left: 50%;
margin-left:-100px;
}
.van-popup{
background: none;
.close{
text-align: center;
// background: #fff;
img{
width: 28px;
height: 28px;
}
}
}
.popup-content {
width: 360px;
height: 250px;
background-image: url("@/assets/img/popover.png");
background-size: cover;
padding-top: 10px;
position: relative;
.close{
position: absolute;
width: 100px;
height:30px;
background: #fff;
}
.popover-title {
font-size: 16px;
font-weight: 500;
color: #77c0ff;
height: 36px;
line-height: 36px;
padding: 0 16px;
}
.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;
span {
font-size: 14px;
font-weight: 500;
color: #77c0ff;
&.city {
margin-right: 48px;
}
&.num {
color: #29ffa3;
}
}
}
.exhibition {
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
text-decoration:none;
padding: 0 16px;
p {
font-size: 14px;
font-weight: 500;
color: rgba(119, 192, 255, 0.7);
text-align: start;
&:nth-child(1){
width: 30px;
text-align: center;
}
&:nth-child(3){
width: 90px;
text-align: right;
}
&.img{
flex:1;
margin-left: 40px;
display: flex;
align-items: center;
span{
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-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: #29ffa3;
}
&:hover {
background: rgba(95, 151, 213, 0.3);
cursor: pointer;
}
}
}
}
.nav{
right: 10px;
top: 10px;
}
.share{
right: 10px;
top: 56px;
}
}
</style>
\ No newline at end of file
<template>
<div class="rank-box">
<div class="rank-list-open" @click="open(!listFlag)">
<p ><span>{{!listFlag?'点击展开':'点击收起'}}</span><i :class="listFlag?'active':''"></i></p>
</div>
<div class="rank-list">
<div class="title">
<span>{{ title }}</span>
<p v-if="city&&!userItem"><img src="@/assets/img/place.png" alt="" /><span>{{city}}</span></p>
<p v-if="userItem"><img src="@/assets/img/user-wap.png" alt="" /><a target="_blank" :href="`https://blog.csdn.net/${userItem.username}`">{{userItem.nickname||userItem.username}}</a></p>
</div>
<div class="rank-list-nav border">
<span class="mar">排名</span>
<p><span>明星成员</span></p>
<span>城市</span><span>{{listTitle}}</span>
</div>
<ul class="user-list">
<!-- <li class="border">
<span class="mar">排名</span>
<p><span>明星成员</span></p>
<span>城市</span><span>{{listTitle}}</span>
</li> -->
<li class="rank-list-nav active" v-for="(item,index) in rankData" :key="index" @click="rankAdd(item)">
<span class="mar">{{index+1}}</span>
<p>
<img :src="item.avatar" alt="" /><span
>{{item.nickname || item.username}}</span
>
</p>
<span>{{item.city}}</span><span>{{item.score}}</span>
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
data() {
return {
listFlag:false,
userItem:''
}
},
methods: {
open (val) {
this.listFlag = val
this.$emit('listOpen', this.listFlag)
},
clear () {
this.$emit('clear')
},
rankAdd(item){
if (this.title == '用户原力榜') {
window.open(`https://blog.csdn.net/`+item.username)
}else{
this.userItem = item
this.$emit('rankFans',item)
this.open(false)
}
}
},
props: ['title','rankData','city','listTitle','cityObj']
}
</script>
<style scoped lang="scss">
.rank-box{
height: 100%;
}
.rank-list-open{
height: 40px;
display: flex;
justify-content: center;
p{
display: flex;
align-items: center;
justify-content: center;
// padding: 0 10px;
height: 24px;
background: #2B364F;
border-radius: 16px;
border: 1px solid #5C6E8E;
width: 86px;
span{
font-size: 12px;
font-weight: 500;
color: #FFFFFF;
}
i{
width: 16px;
height: 16px;
background-image:url('@/assets/img/up.png') ;
background-size: cover;
&.active{
background-image:url('@/assets/img/down.png') ;
background-size: cover;
}
}
}
}
.rank-list {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background: linear-gradient(180deg, #416095 0%, #364362 100%);
border-radius: 20px 20px 0px 0px;
padding-bottom: 10px;
.title {
display: flex;
justify-content: space-between;
height: 48px;
align-items: center;
padding: 0px 16px 0px 16px;
padding-right: 26px;
&>span:nth-child(1){
color: #fff;
}
span {
font-size: 14px;
font-weight: 500;
color: #b2bccf;
}
.clear{
cursor: pointer;
}
p {
display: flex;
align-items: center;
max-width: 150px;
img {
width: 18px;
height: 18px;
}
a{
flex:1;
font-size: 14px;
color: #B2BCCF;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
word-break: break-all;
}
}
}
.rank-list-nav{
border-radius:10px 10px 0 0;
}
li,.rank-list-nav {
display: flex;
align-items: center;
height: 50px;
padding: 0 10px;
&.border {
border-bottom: 1px solid #2b4670;
background: #2B364F;
margin: 0 10px;
span{
color: #b2bccf;
}
}
// &.active:hover {
// background: #5f97d5;
// cursor: pointer;
// }
p {
width: 160px;
display: flex;
align-items: center;
img {
width: 28px;
height: 28px;
border-radius: 50%;
margin-right: 10px;
}
}
& > span:nth-child(1) {
margin-right: 20px;
width: 40px;
text-align: center;
}
& > span:nth-child(4){
width: 50px;
}
& > span:nth-child(3) {
margin-right: 10px;
width: 50px;
// text-align: center;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
word-break: break-all;
}
&.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: 14px;
font-weight: 500;
color: #fff;
}
}
.user-list {
// height: 100%;
margin: 0 10px;
flex: 1;
overflow: auto;
background: #2B364F;
// border-radius:0 0 10px 10px;
margin-top: 0px;
&::-webkit-scrollbar {
width: 4px;
height: 4px;
}
}
}
</style>
\ No newline at end of file
const { defineConfig } = require('@vue/cli-service')
const path = require('path');
function resolve(dir){
return path.join(__dirname,dir)//设置绝对路径
}
let publicPath = '/'
if (process.env.NODE_ENV === 'production') {
publicPath = 'https://csdn.gitcode.host/csdn-datav/'
}
module.exports = defineConfig({
assetsDir: 'csdn-datav',
transpileDependencies: true,
lintOnSave:false,
publicPath:publicPath,
devServer: {
host: 'loc-csdn-datav.csdn.net',
port: '80',
headers: {
'Access-Control-Allow-Origin': '*'
}
},
productionSourceMap: false,
chainWebpack: config => {
config.resolve.alias.set('@', resolve('src'))
}
})
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册