mirror of
https://github.com/simon987/music-graph-ui.git
synced 2025-04-10 05:56:42 +00:00
Bug fixes, labels, updated deps, tags in search bar
This commit is contained in:
parent
55b47450df
commit
f2760d8f3e
@ -3,10 +3,9 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>music_graph</title>
|
||||
<title>music-graph v1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
|
6
music_graph/package-lock.json
generated
6
music_graph/package-lock.json
generated
@ -3372,9 +3372,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"element-ui": {
|
||||
"version": "2.7.2",
|
||||
"resolved": "https://registry.npmjs.org/element-ui/-/element-ui-2.7.2.tgz",
|
||||
"integrity": "sha512-Exh9QTkm9gwMMPzg1TyaTlBKyr3k4K9XcC5vl0A/mneDvJX//RsURGuOWsCNDVQMdhh5h9e+W5icosh+pKfbCg==",
|
||||
"version": "2.9.2",
|
||||
"resolved": "https://registry.npmjs.org/element-ui/-/element-ui-2.9.2.tgz",
|
||||
"integrity": "sha512-HU5DDKivv2UFZghVWiP3I74IbTzSW8VXc070fM787i1X/u9f43olDuu3S6Pe9z87Z1oNjXt06ZmBlLYtySJMCw==",
|
||||
"requires": {
|
||||
"async-validator": "~1.8.1",
|
||||
"babel-helper-vue-jsx-merge-props": "^2.0.0",
|
||||
|
@ -14,7 +14,7 @@
|
||||
"d3": "^5.9.2",
|
||||
"d3-force": "^2.0.1",
|
||||
"d3-path": "^1.0.7",
|
||||
"element-ui": "^2.7.2",
|
||||
"element-ui": "^2.9.2",
|
||||
"lodash": "^4.17.11",
|
||||
"vue": "^2.6.10",
|
||||
"vue-resource": "^1.5.1",
|
||||
|
@ -33,12 +33,13 @@ export function MusicGraph(data) {
|
||||
this.simulation = d3.forceSimulation()
|
||||
.force('charge', d3.forceManyBody())
|
||||
.force('collide', d3.forceCollide()
|
||||
.radius(40)
|
||||
.radius(35)
|
||||
.strength(1))
|
||||
.force('center', d3.forceCenter(width / 2, height / 2))
|
||||
|
||||
this.zoomed = () => {
|
||||
this.container.attr('transform', d3.event.transform)
|
||||
this.dismiss()
|
||||
}
|
||||
|
||||
this.dismiss = () => {
|
||||
@ -157,7 +158,7 @@ export function MusicGraph(data) {
|
||||
this.makeMenu = function (d) {
|
||||
let items = []
|
||||
let i = 0
|
||||
if (d.type === 'Group' && !d.membersExpanded) {
|
||||
if ((d.type === 'Group' || d.type === 'Artist')) {
|
||||
items.push({
|
||||
idx: i++,
|
||||
icon: icons.guitar,
|
||||
@ -165,13 +166,12 @@ export function MusicGraph(data) {
|
||||
fn: (d) => {
|
||||
this.api.getGroupMembers(d.mbid, d.id)
|
||||
.then(data => {
|
||||
d.membersExpanded = true
|
||||
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({
|
||||
idx: i++,
|
||||
icon: icons.expand,
|
||||
@ -183,34 +183,43 @@ export function MusicGraph(data) {
|
||||
this.expandedNodes.add(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({
|
||||
idx: i++,
|
||||
icon: icons.release,
|
||||
title: 'Releases',
|
||||
icon: icons.label,
|
||||
title: 'Related',
|
||||
fn: (d) => {
|
||||
this.api.getArtistReleases(d.mbid, d.id)
|
||||
this.api.getRelatedTags(d.id)
|
||||
.then(data => {
|
||||
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') &&
|
||||
!d.tagsExpanded) {
|
||||
if ((d.type === 'Artist' || d.type === 'Group')) {
|
||||
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
|
||||
if (d.type === 'Group' || d.type === 'Artist') {
|
||||
fn = (d) => {
|
||||
this.api.getArtistTags(d.mbid, d.id)
|
||||
.then(data => {
|
||||
this.addNodes(data.newNodes, data.relations, d.id)
|
||||
d.tagsExpanded = true
|
||||
})
|
||||
}
|
||||
} 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)
|
||||
.then(data => {
|
||||
this.addNodes(data.newNodes, data.relations, d.id)
|
||||
d.tagsExpanded = true
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -230,12 +238,28 @@ export function MusicGraph(data) {
|
||||
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({
|
||||
idx: i,
|
||||
icon: icons.delete,
|
||||
title: 'Remove from graph',
|
||||
fn: (d) => {
|
||||
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) => {
|
||||
if (d.type === 'Group' || d.type === 'Artist') {
|
||||
// Toggle artistInfo
|
||||
this.nodes.forEach(x => {
|
||||
x.hover = false
|
||||
})
|
||||
if (this._data.hoverArtist === d) {
|
||||
this._data.hoverArtist = undefined
|
||||
} else {
|
||||
this._data.hoverArtist = d
|
||||
d.hover = true
|
||||
}
|
||||
this._update()
|
||||
}
|
||||
}
|
||||
|
||||
this.addNode = function (newNode, relations) {
|
||||
// Convert {id, id} relation to {node, node}
|
||||
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
|
||||
}
|
||||
this.nodeById.set(newNode.id, newNode)
|
||||
@ -358,11 +394,13 @@ export function MusicGraph(data) {
|
||||
})
|
||||
|
||||
// Convert {id, id} relation to {node, node}
|
||||
let linksToAdd = relations.map(({weight, source, target}) => ({
|
||||
source: this.nodeById.get(source),
|
||||
target: this.nodeById.get(target),
|
||||
weight: weight
|
||||
}))
|
||||
let linksToAdd = relations
|
||||
.filter(rel => this.nodeById.has(rel.source) && this.nodeById.has(rel.target))
|
||||
.map(({weight, source, target}) => ({
|
||||
source: this.nodeById.get(source),
|
||||
target: this.nodeById.get(target),
|
||||
weight: weight
|
||||
}))
|
||||
|
||||
// Update source/targetLinks, avoid bidirectional links
|
||||
for (const {source, target} of linksToAdd) {
|
||||
@ -430,7 +468,7 @@ export function MusicGraph(data) {
|
||||
.force('link', d3.forceLink(this.links)
|
||||
.id(d => d.id)
|
||||
.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()
|
||||
@ -456,6 +494,7 @@ export function MusicGraph(data) {
|
||||
.enter()
|
||||
.append('circle')
|
||||
.merge(this.node)
|
||||
this.node
|
||||
.classed('node', true)
|
||||
.attr('r', d => d.radius)
|
||||
.attr('stroke', d => this._getNodeColor(d))
|
||||
@ -485,11 +524,13 @@ export function MusicGraph(data) {
|
||||
|
||||
this.setupKeyBindings = function () {
|
||||
document.body.onkeydown = (e) => {
|
||||
let isPanMode = this.svg.classed('pan-mode')
|
||||
if (e.ctrlKey) {
|
||||
this.svg.classed('pan-mode', true)
|
||||
}
|
||||
}
|
||||
|
||||
if (e.key === 'q') {
|
||||
this.svg.classed('pan-mode', !isPanMode)
|
||||
} else if (e.key === 'Escape') {
|
||||
document.body.onkeyup = (e) => {
|
||||
if (e.key === 'Control') {
|
||||
this.svg.classed('pan-mode', false)
|
||||
}
|
||||
}
|
||||
@ -511,6 +552,9 @@ export function MusicGraph(data) {
|
||||
}
|
||||
|
||||
this._getNodeColor = function (node) {
|
||||
if (node.hover) {
|
||||
return '#FF0000'
|
||||
}
|
||||
if (this.expandedNodes.has(node.id)) {
|
||||
return '#1cb3c8'
|
||||
}
|
||||
@ -524,7 +568,7 @@ export function MusicGraph(data) {
|
||||
})
|
||||
}
|
||||
|
||||
this.addTagById = function(tagid) {
|
||||
this.addTagById = function (tagid) {
|
||||
if (this.nodeById.has(tagid)) {
|
||||
return
|
||||
}
|
||||
@ -546,7 +590,7 @@ export function MusicGraph(data) {
|
||||
.attr('cx', d => d.x)
|
||||
.attr('cy', d => d.y)
|
||||
this.label
|
||||
.attr('x', d => Math.round(d.node.x))
|
||||
.attr('x', d => d.node.x)
|
||||
.attr('y', d => d.node.y + d.baseline)
|
||||
})
|
||||
|
||||
|
@ -63,6 +63,10 @@ export function MusicGraphApi() {
|
||||
return d3.json(this.url + '/artist/details/' + mbid)
|
||||
}
|
||||
|
||||
/**
|
||||
* Works in both directions
|
||||
* @returns {Promise<{newNodes: *, relations: *} | never>}
|
||||
*/
|
||||
this.getGroupMembers = function (mbid, originId) {
|
||||
return d3.json(this.url + '/artist/members/' + mbid)
|
||||
.then((r) => {
|
||||
@ -70,8 +74,8 @@ export function MusicGraphApi() {
|
||||
newNodes: r.artists.map(nodeUtils.fromRawDict),
|
||||
relations: r.artists.map(a => {
|
||||
return {
|
||||
source: a.id,
|
||||
target: originId,
|
||||
source: originId,
|
||||
target: a.id,
|
||||
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) {
|
||||
if (ONLY_GENRE_TAGS) {
|
||||
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) {
|
||||
return d3.json(this.url + '/artist/related/' + mbid)
|
||||
.then((r) => {
|
||||
@ -168,7 +219,14 @@ export function MusicGraphApi() {
|
||||
name: r.tag.name
|
||||
}),
|
||||
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(/ /g, '+')
|
||||
|
||||
return d3.json(this.url + '/artist/autocomplete/' + prefix)
|
||||
return d3.json(this.url + '/autocomplete/' + prefix)
|
||||
}
|
||||
}
|
||||
|
25
music_graph/src/components/AboutPage.vue
Normal file
25
music_graph/src/components/AboutPage.vue
Normal 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>
|
@ -7,14 +7,18 @@
|
||||
style="float: right"
|
||||
>
|
||||
<figure>
|
||||
<img
|
||||
<el-image
|
||||
alt=""
|
||||
style="height: 128px"
|
||||
width="128"
|
||||
height="128"
|
||||
class="block"
|
||||
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>
|
||||
</figure>
|
||||
</div>
|
||||
@ -62,12 +66,25 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style>
|
||||
|
||||
figure {
|
||||
text-align: center;
|
||||
margin: 0 20px 3em 20px;
|
||||
width: 128px;
|
||||
}
|
||||
|
||||
.el-image {
|
||||
width: 128px;
|
||||
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>
|
||||
|
@ -58,15 +58,16 @@ export default {
|
||||
this.api.getArtistDetails(artist.mbid)
|
||||
.then(info => {
|
||||
this.artistInfo = info
|
||||
this.artistInfo.releases = this.artistInfo.releases.filter(r =>
|
||||
r.labels.indexOf('Album') !== -1 || r.labels.indexOf('EP') !== -1)
|
||||
this.artistInfo.releases = this.artistInfo.releases
|
||||
.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 => {
|
||||
t.type = genres.has(t.name) ? '' : 'info'
|
||||
return t
|
||||
})
|
||||
})
|
||||
},
|
||||
onTagClick: function(tag) {
|
||||
onTagClick: function (tag) {
|
||||
this.$emit('addTag', tag)
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,19 @@
|
||||
<template>
|
||||
<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
|
||||
v-bind:artist="hoverArtist"
|
||||
v-on:addTag="onAddTag($event)"
|
||||
/>
|
||||
<canvas id="textMeasurementCanvas"></canvas>
|
||||
<Watermark text="music-graph v1.0"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ArtistInfo from './ArtistInfo'
|
||||
import Watermark from './Watermark'
|
||||
import {MusicGraph} from '../MusicGraph'
|
||||
import InputBar from './InputBar'
|
||||
|
||||
@ -21,12 +23,12 @@ let data = {
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {InputBar, ArtistInfo},
|
||||
components: {InputBar, ArtistInfo, Watermark},
|
||||
data() {
|
||||
return data
|
||||
},
|
||||
methods: {
|
||||
onQuery: function (e) {
|
||||
onAddArtist: function (e) {
|
||||
this.mm.addArtistByMbid(e)
|
||||
},
|
||||
onAddTag: function(e) {
|
||||
@ -35,6 +37,13 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
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>
|
||||
@ -63,7 +72,7 @@ export default {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
svg.pan-mode .pan-rect {
|
||||
.pan-rect {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
@ -71,11 +80,6 @@ export default {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
svg.pan-mode {
|
||||
box-sizing: border-box;
|
||||
border: 5px red solid;
|
||||
}
|
||||
|
||||
/* Link */
|
||||
svg .link.selected {
|
||||
stroke-width: 2;
|
||||
@ -123,7 +127,7 @@ export default {
|
||||
}
|
||||
|
||||
svg .label.tag {
|
||||
fill: darkgrey;
|
||||
fill: #409EFF;
|
||||
}
|
||||
|
||||
svg.hover .label:not(.selected) {
|
||||
|
@ -9,8 +9,10 @@
|
||||
@select="onSubmit"
|
||||
>
|
||||
<template slot-scope="{ item }">
|
||||
<div class="value" >{{ item.value }} <span class="year"
|
||||
v-if="item.year !== 0">[{{item.year}}]</span></div>
|
||||
<div class="value" v-bind:class="{tag: item.type === 'tag'}">{{ item.value }} <span class="year"
|
||||
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>
|
||||
</template>
|
||||
</el-autocomplete>
|
||||
@ -18,7 +20,6 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as _ from 'lodash'
|
||||
import {MusicGraphApi} from '../MusicGraphApi'
|
||||
|
||||
export default {
|
||||
@ -29,28 +30,34 @@ export default {
|
||||
api: new MusicGraphApi()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
query: _.debounce(function () {
|
||||
if (this.query.length >= 3) {
|
||||
this.api.autoComplete(this.query)
|
||||
}
|
||||
}, 500)
|
||||
},
|
||||
methods: {
|
||||
onSubmit: function (artist) {
|
||||
this.$emit('query', artist.mbid)
|
||||
onSubmit: function (line) {
|
||||
if (line.type === 'artist') {
|
||||
this.$emit('addArtist', line.mbid)
|
||||
} else if (line.type === 'tag') {
|
||||
this.$emit('addTag', line.id)
|
||||
}
|
||||
this.query = ''
|
||||
},
|
||||
fetchSuggestions: function (query, callback) {
|
||||
if (this.query.length >= 3) {
|
||||
if (this.query.length >= 1) {
|
||||
this.api.autoComplete(query)
|
||||
.then(data => {
|
||||
callback(data.artists.map(a => {
|
||||
return {
|
||||
'value': a.name,
|
||||
'year': a.year,
|
||||
'comment': a.comment,
|
||||
'mbid': a.mbid
|
||||
callback(data.lines.map(line => {
|
||||
if (line.type === 'artist') {
|
||||
return {
|
||||
'value': line.name,
|
||||
'year': line.year,
|
||||
'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;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.tag {
|
||||
color: #409EFF;
|
||||
}
|
||||
</style>
|
||||
|
23
music_graph/src/components/Watermark.vue
Normal file
23
music_graph/src/components/Watermark.vue
Normal 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>
|
@ -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' +
|
||||
'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' +
|
||||
'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>'
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,22 @@
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import HelloWorld from '@/components/HelloWorld'
|
||||
import HelloWorld from '../components/HelloWorld'
|
||||
import AboutPage from '../components/AboutPage'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
export default new Router({
|
||||
mode: 'history',
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'HelloWorld',
|
||||
component: HelloWorld
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'AboutPage',
|
||||
component: AboutPage
|
||||
}
|
||||
]
|
||||
})
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.0 KiB |
BIN
music_graph/static/diagram.png
Normal file
BIN
music_graph/static/diagram.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
Loading…
x
Reference in New Issue
Block a user