3 changed files with 166 additions and 0 deletions
@ -0,0 +1,42 @@ |
|||||
|
<script setup> |
||||
|
defineProps({ |
||||
|
title: { type: String, default: '' }, |
||||
|
hover: { type: Boolean, default: false } |
||||
|
}) |
||||
|
defineOptions({ name: 'SongCard' }) |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<div class="song-card" :class="{ 'song-card--hover': hover }"> |
||||
|
<div v-if="title || $slots.header" class="song-card__head"> |
||||
|
<slot name="header"> |
||||
|
<span class="h3">{{ title }}</span> |
||||
|
</slot> |
||||
|
</div> |
||||
|
<div class="song-card__body"> |
||||
|
<slot /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped> |
||||
|
.song-card { |
||||
|
background: var(--song-bg-elevated); |
||||
|
border: 1px solid var(--song-border); |
||||
|
border-radius: var(--song-radius-l); |
||||
|
box-shadow: var(--song-shadow-1); |
||||
|
transition: transform var(--song-dur-1) var(--song-ease), |
||||
|
box-shadow var(--song-dur-1) var(--song-ease); |
||||
|
} |
||||
|
.song-card--hover:hover { |
||||
|
transform: translateY(-2px); |
||||
|
box-shadow: var(--song-shadow-2); |
||||
|
} |
||||
|
.song-card__head { |
||||
|
padding: var(--song-space-4) var(--song-space-5); |
||||
|
border-bottom: 1px solid var(--song-border); |
||||
|
} |
||||
|
.song-card__body { |
||||
|
padding: var(--song-space-5); |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,92 @@ |
|||||
|
<script setup> |
||||
|
import { inject } from 'vue' |
||||
|
defineProps({ |
||||
|
items: { type: Array, default: () => [] }, |
||||
|
labelField: { type: String, default: 'label' } |
||||
|
}) |
||||
|
defineOptions({ name: 'SongNav' }) |
||||
|
|
||||
|
const theme = inject('songTheme') |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<nav class="song-nav"> |
||||
|
<a |
||||
|
v-for="(item, i) in items" |
||||
|
:key="i" |
||||
|
class="song-nav__item" |
||||
|
:class="{ 'song-nav__item--active': item.active }" |
||||
|
href="javascript:void 0" |
||||
|
@click="$emit('select', item)" |
||||
|
> |
||||
|
{{ item[labelField] }} |
||||
|
</a> |
||||
|
<button class="song-nav__theme" @click="theme?.toggle()" type="button"> |
||||
|
{{ theme?.isDark ? '砚夜' : '天青' }} |
||||
|
</button> |
||||
|
</nav> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped> |
||||
|
.song-nav { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: var(--song-space-6); |
||||
|
padding: 0 var(--song-space-5); |
||||
|
height: 56px; |
||||
|
border-bottom: 1px solid var(--song-border); |
||||
|
} |
||||
|
/* 直棂窗意象 §1.1:竖条分隔 */ |
||||
|
.song-nav__item { |
||||
|
position: relative; |
||||
|
color: var(--song-text-secondary); |
||||
|
font-size: 14px; |
||||
|
letter-spacing: 0.04em; |
||||
|
padding: 0 2px; |
||||
|
text-decoration: none; |
||||
|
transition: color var(--song-dur-1) var(--song-ease); |
||||
|
} |
||||
|
.song-nav__item + .song-nav__item::before { |
||||
|
content: ''; |
||||
|
position: absolute; |
||||
|
left: calc(-1 * var(--song-space-3)); |
||||
|
top: 25%; |
||||
|
height: 50%; |
||||
|
width: 1px; |
||||
|
background: var(--song-border); |
||||
|
} |
||||
|
.song-nav__item:hover { |
||||
|
color: var(--song-text); |
||||
|
text-decoration: none; |
||||
|
} |
||||
|
.song-nav__item--active { |
||||
|
color: var(--song-primary); |
||||
|
} |
||||
|
/* 主色墨线右滑 §7 */ |
||||
|
.song-nav__item--active::after { |
||||
|
content: ''; |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
bottom: -8px; |
||||
|
width: 100%; |
||||
|
height: 2px; |
||||
|
background: var(--song-primary); |
||||
|
} |
||||
|
.song-nav__theme { |
||||
|
margin-left: auto; |
||||
|
height: 28px; |
||||
|
padding: 0 12px; |
||||
|
font-family: var(--song-font-serif); |
||||
|
font-size: 12px; |
||||
|
color: var(--song-text-secondary); |
||||
|
background: transparent; |
||||
|
border: 1px solid var(--song-border); |
||||
|
border-radius: var(--song-radius-s); |
||||
|
cursor: pointer; |
||||
|
transition: color var(--song-dur-1), border-color var(--song-dur-1); |
||||
|
} |
||||
|
.song-nav__theme:hover { |
||||
|
color: var(--song-primary); |
||||
|
border-color: var(--song-primary); |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,32 @@ |
|||||
|
<script setup> |
||||
|
defineProps({ |
||||
|
type: { type: String, default: 'default' }, |
||||
|
closable: { type: Boolean, default: false } |
||||
|
}) |
||||
|
defineOptions({ name: 'SongTag' }) |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<span class="song-tag" :class="`song-tag--${type}`"> |
||||
|
<slot /> |
||||
|
</span> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped> |
||||
|
.song-tag { |
||||
|
display: inline-flex; |
||||
|
align-items: center; |
||||
|
height: 22px; |
||||
|
padding: 0 10px; |
||||
|
font-size: 12px; |
||||
|
line-height: 20px; |
||||
|
border: 1px solid var(--song-border); |
||||
|
border-radius: var(--song-radius-s); |
||||
|
color: var(--song-text-secondary); |
||||
|
background: transparent; |
||||
|
} |
||||
|
.song-tag--primary { color: var(--song-primary); border-color: var(--song-primary); background: var(--song-primary-bg); } |
||||
|
.song-tag--success { color: var(--song-success); border-color: var(--song-success); background: var(--song-success-bg); } |
||||
|
.song-tag--warning { color: var(--song-warning); border-color: var(--song-warning); background: var(--song-warning-bg); } |
||||
|
.song-tag--danger { color: var(--song-danger); border-color: var(--song-danger); background: var(--song-danger-bg); } |
||||
|
</style> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue