This commit is contained in:
Megghy
2023-06-05 15:31:37 +08:00
parent 4dedacf449
commit 981d873225
30 changed files with 16609 additions and 5653 deletions

View File

@@ -1,65 +0,0 @@
<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-pwa" target="_blank" rel="noopener">pwa</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</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>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript" target="_blank" rel="noopener">typescript</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 lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'HelloWorld',
props: {
msg: String,
},
})
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="stylus">
h3
margin 40px 0 0
ul
list-style-type none
padding 0
li
display inline-block
margin 0 10px
a
color #42b983
</style>

View File

@@ -0,0 +1,109 @@
<script setup lang="ts">
import { FormInst, FormItemInst, FormItemRule, FormRules, NButton, NCard, NForm, NFormItem, NInput, NSpace } from 'naive-ui'
import { ref } from 'vue'
interface RegisterModel {
username: string
email: string
password: string
reenteredPassword: string
}
interface LoginModel {
account: string
password: string
}
const isRegister = ref(false)
const registerModel = ref<RegisterModel>({} as RegisterModel)
const loginModel = ref<LoginModel>({} as LoginModel)
const formRef = ref<FormInst | null>(null)
const rPasswordFormItemRef = ref<FormItemInst | null>(null)
const rules: FormRules = {
account: [
{
required: true,
message: '请输入用户名或邮箱',
},
],
password: [
{
required: true,
message: '请输入密码',
},
],
reenteredPassword: [
{
required: true,
message: '请再次输入密码',
trigger: ['input', 'blur'],
},
{
validator: validatePasswordStartWith,
message: '两次密码输入不一致',
trigger: 'input',
},
{
validator: validatePasswordSame,
message: '两次密码输入不一致',
trigger: ['blur', 'password-input'],
},
],
}
function validatePasswordStartWith(rule: FormItemRule, value: string): boolean {
return !!registerModel.value.password && registerModel.value.password.startsWith(value) && registerModel.value.password.length >= value.length
}
function validatePasswordSame(rule: FormItemRule, value: string): boolean {
return value === registerModel.value.password
}
function onPasswordInput() {
if (registerModel.value.reenteredPassword) {
rPasswordFormItemRef.value?.validate({ trigger: 'password-input' })
}
}
</script>
<template>
<NCard embedded>
<template #header>
<Transition name="fade" mode="out-in">
<span v-if="isRegister"> 注册 </span>
<span v-else> 登陆 </span>
</Transition>
</template>
<Transition name="scale" mode="out-in">
<div v-if="isRegister">
<NForm ref="formRef" :rules="rules" :model="registerModel">
<NFormItem path="username" label="用户名">
<NInput v-model:value="registerModel.username" />
</NFormItem>
<NFormItem path="email" label="邮箱">
<NInput v-model:value="registerModel.email" />
</NFormItem>
<NFormItem path="password" label="密码">
<NInput v-model:value="registerModel.password" type="password" @input="onPasswordInput" @keydown.enter.prevent />
</NFormItem>
<NFormItem ref="rPasswordFormItemRef" first path="reenteredPassword" label="重复密码">
<NInput v-model:value="registerModel.reenteredPassword" :disabled="!registerModel.password" type="password" @keydown.enter.prevent />
</NFormItem>
</NForm>
<NButton @click="isRegister = false"> 或者现在去登陆 </NButton>
</div>
<div v-else>
<NForm ref="formRef" :rules="rules" :model="registerModel">
<NFormItem path="account" label="用户名或邮箱">
<NInput v-model:value="loginModel.account" />
</NFormItem>
<NFormItem path="password" label="密码">
<NInput v-model:value="loginModel.password" type="password" @input="onPasswordInput" @keydown.enter.prevent />
</NFormItem>
</NForm>
<NSpace vertical justify="center" align="center">
<NButton type="primary" size="large"> 登陆 </NButton>
<NButton @click="isRegister = true" size="small" text> 或者现在去注册 </NButton>
</NSpace>
</div>
</Transition>
</NCard>
</template>

132
src/components/SongList.vue Normal file
View File

@@ -0,0 +1,132 @@
<script setup lang="ts">
import { SongsInfo } from '@/api/api-models'
import { DataTableColumns, NAvatar, NButton, NDataTable, NInput, NList, NListItem, NSpace } from 'naive-ui'
import { onMounted, h, ref } from 'vue'
import APlayer from 'vue3-aplayer'
const props = defineProps<{
songs: SongsInfo[]
canEdit?: boolean
}>()
const songsInternal = ref<{ [id: string]: SongsInfo }>({})
const columns = ref<DataTableColumns<SongsInfo>>()
const aplayerMusic = ref<{
title: string
artist: string
src: string
pic: string
}>()
const createColumns = (): DataTableColumns<SongsInfo> => [
{
title: '',
key: 'cover',
resizable: false,
width: 50,
render(data) {
return h(NAvatar, {
src: data.cover,
imgProps: {
}
})
},
},
{
title: '名称',
key: 'name',
resizable: true,
minWidth: 100,
render(data) {
return props.canEdit
? h(NInput, {
value: data.name,
onUpdateValue(v) {
songsInternal.value[data.id].name = v
},
})
: h('span', data.name)
},
},
{
title: '作者',
key: 'author',
resizable: true,
render(data) {
return props.canEdit
? h(NInput, {
value: data.author,
onUpdateValue(v) {
songsInternal.value[data.id].author = v
},
})
: h('span', data.author)
},
},
{
title: '描述',
key: 'description',
resizable: true,
minWidth: 75,
render(data) {
return props.canEdit
? h(NInput, {
value: data.desc,
onUpdateValue(v) {
songsInternal.value[data.id].desc = v
},
})
: h('span', data.desc)
},
},
{
title: '操作',
key: 'manage',
minWidth: 75,
render(data) {
return h(NSpace, [
h(
NButton,
{
onClick: () => console.log(1),
},
{
default: () => '保存',
}
),
h(
NButton,
{
type: 'primary',
onClick: () => {
aplayerMusic.value = {
title: data.name,
artist: data.author,
src: data.url,
pic: data.cover,
}
},
},
{
default: () => '播放',
}
),
])
},
},
]
onMounted(() => {
props.songs.forEach((song) => {
songsInternal.value[song.id] = song
})
columns.value = createColumns()
})
</script>
<template>
歌单 {{ songs.length }}
<Transition>
<APlayer v-if="aplayerMusic" :music="aplayerMusic" />
</Transition>
<NDataTable :columns="columns" :data="songs"> </NDataTable>
</template>