✌️
This commit is contained in:
		
							parent
							
								
									2ec25a7729
								
							
						
					
					
						commit
						7d7193cb63
					
				
					 4 changed files with 119 additions and 31 deletions
				
			
		
							
								
								
									
										78
									
								
								src/client/app/common/views/widgets/hashtags.chart.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/client/app/common/views/widgets/hashtags.chart.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,78 @@
 | 
			
		|||
<template>
 | 
			
		||||
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
 | 
			
		||||
	<defs>
 | 
			
		||||
		<linearGradient :id="gradientId" x1="0" x2="0" y1="1" y2="0">
 | 
			
		||||
			<stop offset="0%" stop-color="hsl(200, 80%, 70%)"></stop>
 | 
			
		||||
			<stop offset="100%" stop-color="hsl(90, 80%, 70%)"></stop>
 | 
			
		||||
		</linearGradient>
 | 
			
		||||
		<mask :id="maskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY">
 | 
			
		||||
			<polygon
 | 
			
		||||
				:points="polygonPoints"
 | 
			
		||||
				fill="#fff"
 | 
			
		||||
				fill-opacity="0.5"/>
 | 
			
		||||
			<polyline
 | 
			
		||||
				:points="polylinePoints"
 | 
			
		||||
				fill="none"
 | 
			
		||||
				stroke="#fff"
 | 
			
		||||
				stroke-width="0.7"/>
 | 
			
		||||
			<circle
 | 
			
		||||
				:cx="headX"
 | 
			
		||||
				:cy="headY"
 | 
			
		||||
				r="1.2"
 | 
			
		||||
				fill="#fff"/>
 | 
			
		||||
		</mask>
 | 
			
		||||
	</defs>
 | 
			
		||||
	<rect
 | 
			
		||||
		x="-2" y="-2"
 | 
			
		||||
		:width="viewBoxX + 4" :height="viewBoxY + 4"
 | 
			
		||||
		:style="`stroke: none; fill: url(#${ gradientId }); mask: url(#${ maskId })`"/>
 | 
			
		||||
</svg>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import * as uuid from 'uuid';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: {
 | 
			
		||||
		src: {
 | 
			
		||||
			type: Array,
 | 
			
		||||
			required: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			viewBoxX: 50,
 | 
			
		||||
			viewBoxY: 30,
 | 
			
		||||
			gradientId: uuid(),
 | 
			
		||||
			maskId: uuid(),
 | 
			
		||||
			polylinePoints: '',
 | 
			
		||||
			polygonPoints: '',
 | 
			
		||||
			headX: null,
 | 
			
		||||
			headY: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	watch: {
 | 
			
		||||
		src() {
 | 
			
		||||
			this.draw();
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	created() {
 | 
			
		||||
		this.draw();
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		draw() {
 | 
			
		||||
			const stats = this.src.slice().reverse();
 | 
			
		||||
			const peak = Math.max.apply(null, stats) || 1;
 | 
			
		||||
 | 
			
		||||
			const polylinePoints = stats.map((x, i) => [this.viewBoxX - ((stats.length - 1) - i), (1 - (x / peak)) * this.viewBoxY]);
 | 
			
		||||
			this.polylinePoints = polylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
 | 
			
		||||
 | 
			
		||||
			this.polygonPoints = `${this.viewBoxX - (stats.length - 1)},${ this.viewBoxY } ${ this.polylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
 | 
			
		||||
 | 
			
		||||
			this.headX = polylinePoints[polylinePoints.length - 1][0];
 | 
			
		||||
			this.headY = polylinePoints[polylinePoints.length - 1][1];
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -6,7 +6,12 @@
 | 
			
		|||
		<div class="mkw-hashtags--body" :data-mobile="platform == 'mobile'">
 | 
			
		||||
			<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
			<div v-else>
 | 
			
		||||
				<router-link v-for="stat in stats" :key="stat.tag" :to="`/tags/${ stat.tag }`">{{ stat.tag }}</router-link>
 | 
			
		||||
				<div v-for="stat in stats" :key="stat.tag">
 | 
			
		||||
					<div class="tag">
 | 
			
		||||
						<router-link :to="`/tags/${ stat.tag }`">#{{ stat.tag }}</router-link>
 | 
			
		||||
					</div>
 | 
			
		||||
					<x-chart class="chart" :src="stat.chart"/>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</mk-widget-container>
 | 
			
		||||
| 
						 | 
				
			
			@ -15,12 +20,17 @@
 | 
			
		|||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import define from '../../../common/define-widget';
 | 
			
		||||
import XChart from './hashtags.chart.vue';
 | 
			
		||||
 | 
			
		||||
export default define({
 | 
			
		||||
	name: 'hashtags',
 | 
			
		||||
	props: () => ({
 | 
			
		||||
		compact: false
 | 
			
		||||
	})
 | 
			
		||||
}).extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XChart
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			stats: [],
 | 
			
		||||
| 
						 | 
				
			
			@ -52,21 +62,8 @@ export default define({
 | 
			
		|||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
root(isDark)
 | 
			
		||||
	.mkw-rss--body
 | 
			
		||||
		.feed
 | 
			
		||||
			padding 12px 16px
 | 
			
		||||
			font-size 0.9em
 | 
			
		||||
 | 
			
		||||
			> a
 | 
			
		||||
				display block
 | 
			
		||||
				padding 4px 0
 | 
			
		||||
				color isDark ? #9aa4b3 : #666
 | 
			
		||||
				border-bottom dashed 1px isDark ? #1c2023 : #eee
 | 
			
		||||
 | 
			
		||||
				&:last-child
 | 
			
		||||
					border-bottom none
 | 
			
		||||
 | 
			
		||||
		.fetching
 | 
			
		||||
	.mkw-hashtags--body
 | 
			
		||||
		> .fetching
 | 
			
		||||
			margin 0
 | 
			
		||||
			padding 16px
 | 
			
		||||
			text-align center
 | 
			
		||||
| 
						 | 
				
			
			@ -75,23 +72,29 @@ root(isDark)
 | 
			
		|||
			> [data-fa]
 | 
			
		||||
				margin-right 4px
 | 
			
		||||
 | 
			
		||||
		&[data-mobile]
 | 
			
		||||
			background isDark ? #21242f : #f3f3f3
 | 
			
		||||
		> div
 | 
			
		||||
			> div
 | 
			
		||||
				display flex
 | 
			
		||||
				align-items center
 | 
			
		||||
				padding 16px
 | 
			
		||||
 | 
			
		||||
			.feed
 | 
			
		||||
				padding 0
 | 
			
		||||
				&:not(:last-child)
 | 
			
		||||
					border-bottom solid 1px #393f4f
 | 
			
		||||
 | 
			
		||||
				> a
 | 
			
		||||
					padding 8px 16px
 | 
			
		||||
					border-bottom none
 | 
			
		||||
				> .tag
 | 
			
		||||
					flex 1
 | 
			
		||||
 | 
			
		||||
					&:nth-child(even)
 | 
			
		||||
						background isDark ? rgba(#000, 0.05) : rgba(#fff, 0.7)
 | 
			
		||||
					> a
 | 
			
		||||
						color #9baec8
 | 
			
		||||
 | 
			
		||||
.mkw-rss[data-darkmode]
 | 
			
		||||
				> .chart
 | 
			
		||||
					width 50px
 | 
			
		||||
					height 30px
 | 
			
		||||
 | 
			
		||||
.mkw-hashtags[data-darkmode]
 | 
			
		||||
	root(true)
 | 
			
		||||
 | 
			
		||||
.mkw-rss:not([data-darkmode])
 | 
			
		||||
.mkw-hashtags:not([data-darkmode])
 | 
			
		||||
	root(false)
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,13 +4,10 @@ import Note from '../../../../models/note';
 | 
			
		|||
 * Get trends of hashtags
 | 
			
		||||
 */
 | 
			
		||||
module.exports = (params, user) => new Promise(async (res, rej) => {
 | 
			
		||||
	// 10分
 | 
			
		||||
	const interval = 1000 * 60 * 10;
 | 
			
		||||
 | 
			
		||||
	const data = await Note.aggregate([{
 | 
			
		||||
		$match: {
 | 
			
		||||
			createdAt: {
 | 
			
		||||
				$gt: new Date(Date.now() - interval)
 | 
			
		||||
				$gt: new Date(Date.now() - 1000 * 60 * 60)
 | 
			
		||||
			},
 | 
			
		||||
			tags: {
 | 
			
		||||
				$exists: true,
 | 
			
		||||
| 
						 | 
				
			
			@ -48,6 +45,10 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 | 
			
		|||
		}>
 | 
			
		||||
	}>;
 | 
			
		||||
 | 
			
		||||
	if (data.length == 0) {
 | 
			
		||||
		return res([]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const hots = data[0].tags
 | 
			
		||||
		.sort((a, b) => a.count - b.count)
 | 
			
		||||
		.map(tag => tag.tag)
 | 
			
		||||
| 
						 | 
				
			
			@ -56,6 +57,9 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 | 
			
		|||
	const countPromises: Array<Promise<number[]>> = [];
 | 
			
		||||
 | 
			
		||||
	for (let i = 0; i < 10; i++) {
 | 
			
		||||
		// 10分
 | 
			
		||||
		const interval = 1000 * 60 * 10;
 | 
			
		||||
 | 
			
		||||
		countPromises.push(Promise.all(hots.map(tag => Note.count({
 | 
			
		||||
			tags: tag,
 | 
			
		||||
			createdAt: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue