Bug fixes, labels, updated deps, tags in search bar

This commit is contained in:
simon 2019-06-22 17:08:09 -04:00
parent 55b47450df
commit f2760d8f3e
15 changed files with 271 additions and 74 deletions

View File

@ -3,10 +3,9 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>music_graph</title> <title>music-graph v1.0</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<!-- built files will be auto injected -->
</body> </body>
</html> </html>

View File

@ -3372,9 +3372,9 @@
"dev": true "dev": true
}, },
"element-ui": { "element-ui": {
"version": "2.7.2", "version": "2.9.2",
"resolved": "https://registry.npmjs.org/element-ui/-/element-ui-2.7.2.tgz", "resolved": "https://registry.npmjs.org/element-ui/-/element-ui-2.9.2.tgz",
"integrity": "sha512-Exh9QTkm9gwMMPzg1TyaTlBKyr3k4K9XcC5vl0A/mneDvJX//RsURGuOWsCNDVQMdhh5h9e+W5icosh+pKfbCg==", "integrity": "sha512-HU5DDKivv2UFZghVWiP3I74IbTzSW8VXc070fM787i1X/u9f43olDuu3S6Pe9z87Z1oNjXt06ZmBlLYtySJMCw==",
"requires": { "requires": {
"async-validator": "~1.8.1", "async-validator": "~1.8.1",
"babel-helper-vue-jsx-merge-props": "^2.0.0", "babel-helper-vue-jsx-merge-props": "^2.0.0",

View File

@ -14,7 +14,7 @@
"d3": "^5.9.2", "d3": "^5.9.2",
"d3-force": "^2.0.1", "d3-force": "^2.0.1",
"d3-path": "^1.0.7", "d3-path": "^1.0.7",
"element-ui": "^2.7.2", "element-ui": "^2.9.2",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"vue": "^2.6.10", "vue": "^2.6.10",
"vue-resource": "^1.5.1", "vue-resource": "^1.5.1",

View File

@ -33,12 +33,13 @@ export function MusicGraph(data) {
this.simulation = d3.forceSimulation() this.simulation = d3.forceSimulation()
.force('charge', d3.forceManyBody()) .force('charge', d3.forceManyBody())
.force('collide', d3.forceCollide() .force('collide', d3.forceCollide()
.radius(40) .radius(35)
.strength(1)) .strength(1))
.force('center', d3.forceCenter(width / 2, height / 2)) .force('center', d3.forceCenter(width / 2, height / 2))
this.zoomed = () => { this.zoomed = () => {
this.container.attr('transform', d3.event.transform) this.container.attr('transform', d3.event.transform)
this.dismiss()
} }
this.dismiss = () => { this.dismiss = () => {
@ -157,7 +158,7 @@ export function MusicGraph(data) {
this.makeMenu = function (d) { this.makeMenu = function (d) {
let items = [] let items = []
let i = 0 let i = 0
if (d.type === 'Group' && !d.membersExpanded) { if ((d.type === 'Group' || d.type === 'Artist')) {
items.push({ items.push({
idx: i++, idx: i++,
icon: icons.guitar, icon: icons.guitar,
@ -165,13 +166,12 @@ export function MusicGraph(data) {
fn: (d) => { fn: (d) => {
this.api.getGroupMembers(d.mbid, d.id) this.api.getGroupMembers(d.mbid, d.id)
.then(data => { .then(data => {
d.membersExpanded = true
this.addNodes(data.newNodes, data.relations, d.id) this.addNodes(data.newNodes, data.relations, d.id)
}) })
} }
}) })
} }
if ((d.type === 'Group' || d.type === 'Artist') && !d.relatedExpanded) { if ((d.type === 'Group' || d.type === 'Artist')) {
items.push({ items.push({
idx: i++, idx: i++,
icon: icons.expand, icon: icons.expand,
@ -183,34 +183,43 @@ export function MusicGraph(data) {
this.expandedNodes.add(d.id) this.expandedNodes.add(d.id)
this.addNodes(data.newNodes, data.relations, d.id) this.addNodes(data.newNodes, data.relations, d.id)
} }
d.relatedExpanded = true
}) })
} }
}) })
} }
if ((d.type === 'Artist' || d.type === 'Group') && !d.releasesExpanded) { if (d.type === 'Tag') {
items.push({ items.push({
idx: i++, idx: i++,
icon: icons.release, icon: icons.label,
title: 'Releases', title: 'Related',
fn: (d) => { fn: (d) => {
this.api.getArtistReleases(d.mbid, d.id) this.api.getRelatedTags(d.id)
.then(data => { .then(data => {
this.addNodes(data.newNodes, data.relations, d.id) this.addNodes(data.newNodes, data.relations, d.id)
d.releasesExpanded = true
}) })
} }
}) })
} }
if ((d.type === 'Album' || d.type === 'EP' || d.type === 'Single' || d.type === 'Group' || d.type === 'Artist') && if ((d.type === 'Artist' || d.type === 'Group')) {
!d.tagsExpanded) { items.push({
idx: i++,
icon: icons.label,
title: 'Label',
fn: (d) => {
this.api.getArtistLabels(d.mbid, d.id)
.then(data => {
this.addNodes(data.newNodes, data.relations, d.id)
})
}
})
}
if (d.type === 'Album' || d.type === 'EP' || d.type === 'Single' || d.type === 'Group' || d.type === 'Artist') {
let fn let fn
if (d.type === 'Group' || d.type === 'Artist') { if (d.type === 'Group' || d.type === 'Artist') {
fn = (d) => { fn = (d) => {
this.api.getArtistTags(d.mbid, d.id) this.api.getArtistTags(d.mbid, d.id)
.then(data => { .then(data => {
this.addNodes(data.newNodes, data.relations, d.id) this.addNodes(data.newNodes, data.relations, d.id)
d.tagsExpanded = true
}) })
} }
} else if (d.type === 'Album' || d.type === 'EP' || d.type === 'Single') { } else if (d.type === 'Album' || d.type === 'EP' || d.type === 'Single') {
@ -218,7 +227,6 @@ export function MusicGraph(data) {
this.api.getReleaseDetails(d.mbid, d.id) this.api.getReleaseDetails(d.mbid, d.id)
.then(data => { .then(data => {
this.addNodes(data.newNodes, data.relations, d.id) this.addNodes(data.newNodes, data.relations, d.id)
d.tagsExpanded = true
}) })
} }
} }
@ -230,12 +238,28 @@ export function MusicGraph(data) {
fn: fn fn: fn
}) })
} }
if (d.type === 'Tag') {
items.push({
idx: i++,
icon: icons.expand,
title: 'Related',
fn: (d) => {
this.api.getRelatedByTag(d.id)
.then(data => {
this.addNodes(data.newNodes, data.relations, d.id)
})
}
})
}
items.push({ items.push({
idx: i, idx: i,
icon: icons.delete, icon: icons.delete,
title: 'Remove from graph', title: 'Remove from graph',
fn: (d) => { fn: (d) => {
this.removeNodes([d.id]) this.removeNodes([d.id])
if (this._data.hoverArtist && d.id === this._data.hoverArtist.id) {
this._data.hoverArtist = undefined
}
} }
}) })
@ -295,17 +319,29 @@ export function MusicGraph(data) {
this.nodeClick = (d) => { this.nodeClick = (d) => {
if (d.type === 'Group' || d.type === 'Artist') { if (d.type === 'Group' || d.type === 'Artist') {
// Toggle artistInfo // Toggle artistInfo
this.nodes.forEach(x => {
x.hover = false
})
if (this._data.hoverArtist === d) { if (this._data.hoverArtist === d) {
this._data.hoverArtist = undefined this._data.hoverArtist = undefined
} else { } else {
this._data.hoverArtist = d this._data.hoverArtist = d
d.hover = true
} }
this._update()
} }
} }
this.addNode = function (newNode, relations) { this.addNode = function (newNode, relations) {
// Convert {id, id} relation to {node, node} // Convert {id, id} relation to {node, node}
if (this.nodeById.has(newNode.id)) { if (this.nodeById.has(newNode.id)) {
// Node already exists, select it
this.nodes.forEach(x => {
x.hover = false
})
this._data.hoverArtist = this.nodeById.get(newNode.id)
this._data.hoverArtist.hover = true
this._update()
return return
} }
this.nodeById.set(newNode.id, newNode) this.nodeById.set(newNode.id, newNode)
@ -358,11 +394,13 @@ export function MusicGraph(data) {
}) })
// Convert {id, id} relation to {node, node} // Convert {id, id} relation to {node, node}
let linksToAdd = relations.map(({weight, source, target}) => ({ let linksToAdd = relations
source: this.nodeById.get(source), .filter(rel => this.nodeById.has(rel.source) && this.nodeById.has(rel.target))
target: this.nodeById.get(target), .map(({weight, source, target}) => ({
weight: weight source: this.nodeById.get(source),
})) target: this.nodeById.get(target),
weight: weight
}))
// Update source/targetLinks, avoid bidirectional links // Update source/targetLinks, avoid bidirectional links
for (const {source, target} of linksToAdd) { for (const {source, target} of linksToAdd) {
@ -430,7 +468,7 @@ export function MusicGraph(data) {
.force('link', d3.forceLink(this.links) .force('link', d3.forceLink(this.links)
.id(d => d.id) .id(d => d.id)
.strength(l => l.weight) .strength(l => l.weight)
.distance(d => (1.15 / d.weight) * (82 * (this.expandedNodes.size + 1))) .distance(d => (1.12 / d.weight) * 80 * (this.expandedNodes.size + 1))
) )
this.simulation.alphaTarget(0.03).restart() this.simulation.alphaTarget(0.03).restart()
@ -456,6 +494,7 @@ export function MusicGraph(data) {
.enter() .enter()
.append('circle') .append('circle')
.merge(this.node) .merge(this.node)
this.node
.classed('node', true) .classed('node', true)
.attr('r', d => d.radius) .attr('r', d => d.radius)
.attr('stroke', d => this._getNodeColor(d)) .attr('stroke', d => this._getNodeColor(d))
@ -485,11 +524,13 @@ export function MusicGraph(data) {
this.setupKeyBindings = function () { this.setupKeyBindings = function () {
document.body.onkeydown = (e) => { document.body.onkeydown = (e) => {
let isPanMode = this.svg.classed('pan-mode') if (e.ctrlKey) {
this.svg.classed('pan-mode', true)
}
}
if (e.key === 'q') { document.body.onkeyup = (e) => {
this.svg.classed('pan-mode', !isPanMode) if (e.key === 'Control') {
} else if (e.key === 'Escape') {
this.svg.classed('pan-mode', false) this.svg.classed('pan-mode', false)
} }
} }
@ -511,6 +552,9 @@ export function MusicGraph(data) {
} }
this._getNodeColor = function (node) { this._getNodeColor = function (node) {
if (node.hover) {
return '#FF0000'
}
if (this.expandedNodes.has(node.id)) { if (this.expandedNodes.has(node.id)) {
return '#1cb3c8' return '#1cb3c8'
} }
@ -524,7 +568,7 @@ export function MusicGraph(data) {
}) })
} }
this.addTagById = function(tagid) { this.addTagById = function (tagid) {
if (this.nodeById.has(tagid)) { if (this.nodeById.has(tagid)) {
return return
} }
@ -546,7 +590,7 @@ export function MusicGraph(data) {
.attr('cx', d => d.x) .attr('cx', d => d.x)
.attr('cy', d => d.y) .attr('cy', d => d.y)
this.label this.label
.attr('x', d => Math.round(d.node.x)) .attr('x', d => d.node.x)
.attr('y', d => d.node.y + d.baseline) .attr('y', d => d.node.y + d.baseline)
}) })

View File

@ -63,6 +63,10 @@ export function MusicGraphApi() {
return d3.json(this.url + '/artist/details/' + mbid) return d3.json(this.url + '/artist/details/' + mbid)
} }
/**
* Works in both directions
* @returns {Promise<{newNodes: *, relations: *} | never>}
*/
this.getGroupMembers = function (mbid, originId) { this.getGroupMembers = function (mbid, originId) {
return d3.json(this.url + '/artist/members/' + mbid) return d3.json(this.url + '/artist/members/' + mbid)
.then((r) => { .then((r) => {
@ -70,8 +74,8 @@ export function MusicGraphApi() {
newNodes: r.artists.map(nodeUtils.fromRawDict), newNodes: r.artists.map(nodeUtils.fromRawDict),
relations: r.artists.map(a => { relations: r.artists.map(a => {
return { return {
source: a.id, source: originId,
target: originId, target: a.id,
weight: 0.8 weight: 0.8
} }
}) })
@ -99,6 +103,29 @@ export function MusicGraphApi() {
}) })
} }
this.getArtistLabels = function (mbid, originId) {
return d3.json(this.url + '/artist/details/' + mbid)
.then((r) => {
const newNodes = r.labels
.map(l => {
l.labels = ['Label']
return l
})
.map(nodeUtils.fromRawDict)
return {
newNodes: newNodes,
relations: newNodes.map(t => {
return {
source: originId,
target: t.id,
weight: 0.8
}
})
}
})
}
this._filterTags = function (tags) { this._filterTags = function (tags) {
if (ONLY_GENRE_TAGS) { if (ONLY_GENRE_TAGS) {
return tags.filter(tag => genres.has(tag.name)) return tags.filter(tag => genres.has(tag.name))
@ -133,6 +160,30 @@ export function MusicGraphApi() {
}) })
} }
this.getRelatedTags = function (tagId) {
return d3.json(this.url + '/tag/tag/' + tagId)
.then((r) => {
const tags = this._filterTags(r.tags)
let directedRelations = r.relations.map(rel => {
// Make new nodes children of the expanded nodes, no matter the original direction
if (rel.source === tagId) {
return rel
} else {
return {
source: rel.target,
target: rel.source,
weight: rel.weight
}
}
})
return {
newNodes: this._addTagLabel(tags).map(nodeUtils.fromRawDict),
relations: directedRelations
}
})
}
this.getRelatedByMbid = function (mbid) { this.getRelatedByMbid = function (mbid) {
return d3.json(this.url + '/artist/related/' + mbid) return d3.json(this.url + '/artist/related/' + mbid)
.then((r) => { .then((r) => {
@ -168,7 +219,14 @@ export function MusicGraphApi() {
name: r.tag.name name: r.tag.name
}), }),
newNodes: r.artists.map(nodeUtils.fromRawDict), newNodes: r.artists.map(nodeUtils.fromRawDict),
relations: r.relations relations: r.relations.map(rel => {
// Invert relation direction
return {
source: rel.target,
target: rel.source,
weight: rel.weight
}
})
} }
}) })
} }
@ -195,6 +253,6 @@ export function MusicGraphApi() {
prefix = prefix.replace(/[^\w.\-!?& ]/g, '_').toUpperCase() prefix = prefix.replace(/[^\w.\-!?& ]/g, '_').toUpperCase()
prefix = prefix.replace(/ /g, '+') prefix = prefix.replace(/ /g, '+')
return d3.json(this.url + '/artist/autocomplete/' + prefix) return d3.json(this.url + '/autocomplete/' + prefix)
} }
} }

View File

@ -0,0 +1,25 @@
<template>
<el-main>
<span>This is the about page</span>
<object data="/static/diagram.svg" type="image/svg+xml"></object>
</el-main>
</template>
<script>
export default {
name: 'AboutPage'
}
</script>
<style scoped>
#watermark {
position: fixed;
top: calc(100% - 30px);
left: 1%;
pointer-events: none;
color: rgba(0,0,0,0.67);
font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
}
</style>

View File

@ -7,14 +7,18 @@
style="float: right" style="float: right"
> >
<figure> <figure>
<img <el-image
alt="" alt=""
style="height: 128px" style="height: 128px"
width="128" width="128"
height="128" height="128"
class="block"
v-bind:src="api.resolveCoverUrl(release.mbid)" v-bind:src="api.resolveCoverUrl(release.mbid)"
onerror="this.src='/static/album.png'"
> >
<div slot="error" class="image-slot">
<i class="el-icon-full-screen"></i>
</div>
</el-image>
<figcaption>{{release.name}} ({{release.year}})</figcaption> <figcaption>{{release.name}} ({{release.year}})</figcaption>
</figure> </figure>
</div> </div>
@ -62,12 +66,25 @@ export default {
} }
</script> </script>
<style scoped> <style>
figure { figure {
text-align: center; text-align: center;
margin: 0 20px 3em 20px;
width: 128px;
}
.el-image {
width: 128px; width: 128px;
height: 180px; height: 180px;
margin: 0 20px 3em 20px; }
.image-slot {
font-size: 30px;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
background: #f5f7fa;
color: #909399;
} }
</style> </style>

View File

@ -58,15 +58,16 @@ export default {
this.api.getArtistDetails(artist.mbid) this.api.getArtistDetails(artist.mbid)
.then(info => { .then(info => {
this.artistInfo = info this.artistInfo = info
this.artistInfo.releases = this.artistInfo.releases.filter(r => this.artistInfo.releases = this.artistInfo.releases
r.labels.indexOf('Album') !== -1 || r.labels.indexOf('EP') !== -1) .sort((a, b) => a.year - b.year)
.filter(r => r.labels.indexOf('Album') !== -1 || r.labels.indexOf('EP') !== -1)
this.artistInfo.tags = info.tags.sort((a, b) => b.weight - a.weight).splice(0, 6).map(t => { this.artistInfo.tags = info.tags.sort((a, b) => b.weight - a.weight).splice(0, 6).map(t => {
t.type = genres.has(t.name) ? '' : 'info' t.type = genres.has(t.name) ? '' : 'info'
return t return t
}) })
}) })
}, },
onTagClick: function(tag) { onTagClick: function (tag) {
this.$emit('addTag', tag) this.$emit('addTag', tag)
} }
} }

View File

@ -1,17 +1,19 @@
<template> <template>
<div> <div>
<div id="mm"></div> <div id="mm"></div>
<InputBar v-on:query="onQuery($event)"></InputBar> <InputBar v-on:addArtist="onAddArtist($event)" v-on:addTag="onAddTag($event)"></InputBar>
<ArtistInfo <ArtistInfo
v-bind:artist="hoverArtist" v-bind:artist="hoverArtist"
v-on:addTag="onAddTag($event)" v-on:addTag="onAddTag($event)"
/> />
<canvas id="textMeasurementCanvas"></canvas> <canvas id="textMeasurementCanvas"></canvas>
<Watermark text="music-graph v1.0"/>
</div> </div>
</template> </template>
<script> <script>
import ArtistInfo from './ArtistInfo' import ArtistInfo from './ArtistInfo'
import Watermark from './Watermark'
import {MusicGraph} from '../MusicGraph' import {MusicGraph} from '../MusicGraph'
import InputBar from './InputBar' import InputBar from './InputBar'
@ -21,12 +23,12 @@ let data = {
} }
export default { export default {
components: {InputBar, ArtistInfo}, components: {InputBar, ArtistInfo, Watermark},
data() { data() {
return data return data
}, },
methods: { methods: {
onQuery: function (e) { onAddArtist: function (e) {
this.mm.addArtistByMbid(e) this.mm.addArtistByMbid(e)
}, },
onAddTag: function(e) { onAddTag: function(e) {
@ -35,6 +37,13 @@ export default {
}, },
mounted() { mounted() {
this.mm = new MusicGraph(data) this.mm = new MusicGraph(data)
this.$notify({
title: 'Welcome!',
message: 'Use the search bar to add nodes. Right click nodes for more options',
type: 'info',
duration: 15 * 1000
})
} }
} }
</script> </script>
@ -63,7 +72,7 @@ export default {
cursor: move; cursor: move;
} }
svg.pan-mode .pan-rect { .pan-rect {
pointer-events: all; pointer-events: all;
} }
@ -71,11 +80,6 @@ export default {
pointer-events: all; pointer-events: all;
} }
svg.pan-mode {
box-sizing: border-box;
border: 5px red solid;
}
/* Link */ /* Link */
svg .link.selected { svg .link.selected {
stroke-width: 2; stroke-width: 2;
@ -123,7 +127,7 @@ export default {
} }
svg .label.tag { svg .label.tag {
fill: darkgrey; fill: #409EFF;
} }
svg.hover .label:not(.selected) { svg.hover .label:not(.selected) {

View File

@ -9,8 +9,10 @@
@select="onSubmit" @select="onSubmit"
> >
<template slot-scope="{ item }"> <template slot-scope="{ item }">
<div class="value" >{{ item.value }} <span class="year" <div class="value" v-bind:class="{tag: item.type === 'tag'}">{{ item.value }} <span class="year"
v-if="item.year !== 0">[{{item.year}}]</span></div> v-if="item.year">[{{item.year}}]</span>
<span v-if="item.type === 'tag'" class="year">[tag]</span>
</div>
<span class="comment" v-if="item.comment">{{ item.comment }}</span> <span class="comment" v-if="item.comment">{{ item.comment }}</span>
</template> </template>
</el-autocomplete> </el-autocomplete>
@ -18,7 +20,6 @@
</template> </template>
<script> <script>
import * as _ from 'lodash'
import {MusicGraphApi} from '../MusicGraphApi' import {MusicGraphApi} from '../MusicGraphApi'
export default { export default {
@ -29,28 +30,34 @@ export default {
api: new MusicGraphApi() api: new MusicGraphApi()
} }
}, },
watch: {
query: _.debounce(function () {
if (this.query.length >= 3) {
this.api.autoComplete(this.query)
}
}, 500)
},
methods: { methods: {
onSubmit: function (artist) { onSubmit: function (line) {
this.$emit('query', artist.mbid) if (line.type === 'artist') {
this.$emit('addArtist', line.mbid)
} else if (line.type === 'tag') {
this.$emit('addTag', line.id)
}
this.query = '' this.query = ''
}, },
fetchSuggestions: function (query, callback) { fetchSuggestions: function (query, callback) {
if (this.query.length >= 3) { if (this.query.length >= 1) {
this.api.autoComplete(query) this.api.autoComplete(query)
.then(data => { .then(data => {
callback(data.artists.map(a => { callback(data.lines.map(line => {
return { if (line.type === 'artist') {
'value': a.name, return {
'year': a.year, 'value': line.name,
'comment': a.comment, 'year': line.year,
'mbid': a.mbid 'comment': line.comment,
'type': line.type,
'mbid': line.id
}
} else if (line.type === 'tag') {
return {
'value': line.name,
'type': line.type,
'id': line.id
}
} }
})) }))
}) })
@ -83,4 +90,8 @@ export default {
margin-left: 0.1em; margin-left: 0.1em;
vertical-align: top; vertical-align: top;
} }
.tag {
color: #409EFF;
}
</style> </style>

View File

@ -0,0 +1,23 @@
<template>
<span id="watermark">{{text}}</span>
</template>
<script>
export default {
name: 'Watermark',
props: ['text']
}
</script>
<style scoped>
#watermark {
position: fixed;
top: calc(100% - 30px);
left: 1%;
pointer-events: none;
color: rgba(0,0,0,0.67);
font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
}
</style>

View File

@ -35,7 +35,15 @@ const icons = {
'h58.649l-6.904,52.791H7.804v47.289h45.559l-8.784,67.066h47.687l8.787-67.066h60.083l-8.786,67.066h47.691l8.783-67.066h64.836\n' + 'h58.649l-6.904,52.791H7.804v47.289h45.559l-8.784,67.066h47.687l8.787-67.066h60.083l-8.786,67.066h47.691l8.783-67.066h64.836\n' +
'v-47.289h-58.647l6.901-52.791H273.661z M167.326,167.109h-60.084l6.9-52.791h60.082L167.326,167.109z"/></svg>', 'v-47.289h-58.647l6.901-52.791H273.661z M167.326,167.109h-60.084l6.9-52.791h60.082L167.326,167.109z"/></svg>',
delete: '<svg width="28px" height="28px" viewBox="0 0 510 510" ><path d="M255,0C114.75,0,0,114.75,0,255s114.75,255,255,255s255-114.75,255-255S395.25,0,255,0z M382.5,346.8l-35.7,35.7\n' + delete: '<svg width="28px" height="28px" viewBox="0 0 510 510" ><path d="M255,0C114.75,0,0,114.75,0,255s114.75,255,255,255s255-114.75,255-255S395.25,0,255,0z M382.5,346.8l-35.7,35.7\n' +
'L255,290.7l-91.8,91.8l-35.7-35.7l91.8-91.8l-91.8-91.8l35.7-35.7l91.8,91.8l91.8-91.8l35.7,35.7L290.7,255L382.5,346.8z"/></svg>' 'L255,290.7l-91.8,91.8l-35.7-35.7l91.8-91.8l-91.8-91.8l35.7-35.7l91.8,91.8l91.8-91.8l35.7,35.7L290.7,255L382.5,346.8z"/></svg>',
label: '<svg height="29px" width="29px" viewBox="1 0 511.999 511"><path d="m224.828125.5-10.148437 1.675781c-59.347657 9.816407-113.6875' +
' 40.507813-153.007813 86.417969-39.769531 46.429688-61.671875 105.707031-61.671875 166.910156 0 68.59375 26.722656 133.085938 75.246094' +
' 181.585938 48.519531 48.5 113.03125 75.210937 181.652344 75.210937 61.222656 0 120.519531-21.890625 166.964843-61.640625 45.929688-39.304687' +
' 76.632813-93.625 86.453125-152.953125l1.683594-10.15625zm9 65.621094 211.324219 211.234375c-115.246094-2.863281-208.464844-96.039063-211.324219-211.234375zm23.070313' +
' 406.136718c-119.574219 0-216.851563-97.238281-216.851563-216.753906 0-97.101562 63.300781-180.390625 153.996094-207.527344-.210938 4.234376-.328125 8.472657-.328125 12.695313' +
' 0 40.6875 9.402344 79.925781 27.175781 115.242187h-62.707031v40.046876h87.792968c7.007813 9.191406 14.667969 17.984374 22.984376 26.296874 4.4375 4.433594 9.007812 8.683594' +
' 13.703124 12.75h-167.53125v40.042969h230.199219c32.671875 14.683594 68.378907 22.417969 105.277344 22.417969 4.75 0 9.515625-.144531 14.28125-.40625-26.75 91.328125-110.402344' +
' 155.195312-207.992187 155.195312zm-98.714844-137.160156h197.226562v40.046875h-197.226562zm0 0"/></svg>'
} }

View File

@ -1,15 +1,22 @@
import Vue from 'vue' import Vue from 'vue'
import Router from 'vue-router' import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld' import HelloWorld from '../components/HelloWorld'
import AboutPage from '../components/AboutPage'
Vue.use(Router) Vue.use(Router)
export default new Router({ export default new Router({
mode: 'history',
routes: [ routes: [
{ {
path: '/', path: '/',
name: 'HelloWorld', name: 'HelloWorld',
component: HelloWorld component: HelloWorld
},
{
path: '/about',
name: 'AboutPage',
component: AboutPage
} }
] ]
}) })

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB