vue 音乐播放器页面(前后端版)

效果图

在歌曲宝里面下载音乐文件和歌词

https://www.gequbao.com/

增加了上一曲,播放,下一曲的功能,之前只能通过搜索来进行换曲,还增加了背景图片,可以在添加歌曲时上传我们喜欢的图片,这样在不同的音乐界面背景图也不一样,末尾我又修改了一下可以自动播放,添加了播放列表,音量调节

第一步

在自己的vue项目里面创建一个页面,并在路由里面配置路由(要发送请求和用到element-ui)所以vue项目要安装router,axios和element-ui(vue2)或者element-plus(vue3)

npm install axios
npm install router
//vue为2版本
npm install element-ui
//vue为3版本
npm install element-plus

第二步

MusicView.vue页面

<script>
import {ref, onMounted, watch, reactive, onUnmounted} from 'vue';
import axios from "axios";

const name = ref('')
export default {
  setup() {
    const lrcData = ref([]);
    const audio = ref(null);
    const ul = ref(null);
    const container = ref(null);
    const musicName = ref('')
    const dialogFormVisible = ref(false)
    const isPlay = ref(false)
    const selectedFile = ref(null);
    const originalTitle = ref(''); // 原始标题
    const title = ref(originalTitle);
    const musicList = ref([])
    const imgUrl = ref("");
    let conm = 0;
    const form = reactive({
      musicName: '',
      text: '',
      file: '',
      imgUrl: ''
    })
    // form 三个属性分别为 歌曲名称,歌词,mp3文件

    //添加对话框状态
    const isDialog = () => {
      dialogFormVisible.value = true
    }
    //上一曲
    const previousSong = () => {
      conm = (conm - 1 + musicList.value.length) % musicList.value.length;
      name.value = musicList.value[conm]
      fetchLyrics();
      name.value="";
      isPlay.value=false
      conm--;
    }
    //下一曲
    const nextSong = () => {
      conm = (conm + 1) % musicList.value.length;
      name.value = musicList.value[conm]
      fetchLyrics();
      name.value="";
      isPlay.value=false
      conm++;
    }

    //播放与暂停
    const Play = () => {
      if (isPlay.value){
        audio.value.pause();
      }else {
        audio.value.play();
      }
      isPlay.value=!isPlay.value
      console.log(isPlay.value)
    }
    // 更新播放状态
    const updatePlayStatus = (event) => {
      isPlay.value = event.type === 'play'; // 根据事件类型更新 isPlay
    };
    const musicLists =async () => {
      await axios.get("http://localhost:8080/user/musicList").then(res=>{
        musicList.value = res.data.data
      }).catch(res=>{
        console.log(res.error)
      })
    }
    const musicAdd =async () => {
      console.log(form)
      if (form.musicName!==null && form.text!==null){
        await axios.post("http://localhost:8080/user/musicAdd",{musicName: form.musicName,text: form.text,file: form.file,imgUrl: form.imgUrl}).then(res=>{
          if (res.data.code===0){
            form.file = '';
            form.musicName = '';
            form.text = '';
            form.imgUrl = ""
            dialogFormVisible.value = false;
          }
        }).catch(res=>{
          console.log(res.error)
        })
      }
    }
    const uploadFile = () => {
    }
    const handleFileChange = async (event) => {
      // 获取用户选择的文件
      const file = event.raw;
      if (file && file.type === 'audio/mpeg') {
        selectedFile.value = file;
      }
      if (selectedFile.value===null){
        return;
      }
      let formData = new FormData;
      formData.append('file', selectedFile.value)
      await axios.post('http://localhost:8080/user/musicFileUpload', formData, {
        headers: {'Content-Type': 'multipart/form-data'}
      }).then(res => {
        if (res.data.code === 0) {
          form.file = res.data.data;
        }
      }).catch(res => {
        console.log(res.error)
      });

    };
    const handleChange = async (event) => {
      // 获取用户选择的文件
      const file = event.raw;
      if (selectedFile.value===null){
        return;
      }
      let formData = new FormData;
      formData.append('file', file)
      await axios.post('http://localhost:8080/user/musicFileUpload', formData, {
        headers: {'Content-Type': 'multipart/form-data'}
      }).then(res => {
        if (res.data.code === 0) {
          form.imgUrl = res.data.data;
        }
      }).catch(res => {
        console.log(res.error)
      });

    };
    const findIndex = () => {
      const curTime = audio.value.currentTime;
      for (let i = 0; i < lrcData.value.length; i++) {
        if (curTime < lrcData.value[i].time) {
          return i > 0 ? i - 1 : -1; // 确保索引不会小于0
        }
      }
      return lrcData.value.length - 1; // 播放到最后一句
    };
    // 创建歌词 DOM 元素
    const createLrcElements = () => {
      // 清空原有歌词元素
      ul.value.innerHTML = '';
      lrcData.value.forEach(item => {
        const li = document.createElement('li');
        li.textContent = item.text;
        ul.value.appendChild(li);
      });
    };
    // 设置歌词滚动效果
    const setOffset = () => {
      const index = findIndex();
      const containerHeight = container.value.clientHeight;
      const liHeight = ul.value.children[0]?.clientHeight || 0; // 使用可选链防止报错
      const maxOffset = Math.max(ul.value.clientHeight - containerHeight, 0);
      let offset = liHeight * index + liHeight / 2 - containerHeight / 2;
      offset = Math.max(0, Math.min(offset, maxOffset)); // 确保 offset 在合法范围内
      ul.value.style.transform = `translateY(-${offset}px)`;
      // 设置高亮当前歌词
      const activeLi = ul.value.querySelector('.active');
      if (activeLi) {
        activeLi.classList.remove('active');
      }

      const li = ul.value.children[index];
      if (li) {
        li.classList.add('active');
      }
    };
    let con = 1; // 控制移动字符的索引
    let intervalId; // 用于存储setInterval的ID
    const titleButton = () => {
      // 清除之前的定时器
      clearInterval(intervalId);
      // 每2秒更新一次标题
      intervalId = setInterval(() => {
        // 逐个字符移动
        title.value = originalTitle.value.substring(con) + originalTitle.value.substring(0, con);
        con = (con + 1) % originalTitle.value.length;
      }, 1200);
    };
    // 创建一个获取歌词的异步函数
    const fetchLyrics = async () => {
      try {
        console.log(name.value)
        // 修改为你的 API 地址
        const data = await axios.post('http://localhost:8080/user/song', {name: name.value});
        const lyri = data.data.data
        musicName.value = lyri.songUrl
        originalTitle.value = lyri.title+" - "+lyri.artist+"  ";
        console.log(title.value)
        if (lyri.imgUrl){
          imgUrl.value = lyri.imgUrl
        }
        // 假设你的歌词是一个包含时间戳和歌词的数组
        // 示例:[{ time: 0, text: '第一行歌词' }, { time: 5, text: '第二行歌词' }]
        lrcData.value = lyri.list.map((lyric) => ({
          time: lyric.time,
          text: lyric.text,
        }));
        createLrcElements(); // 更新 DOM 元素
        setOffset();
      } catch (error) {
        console.error("获取歌词失败:", error); // 处理错误情况
      }
    };
    onMounted(() => {
      fetchLyrics();
      titleButton();
      musicLists();
      audio.value.addEventListener('timeupdate', setOffset);
      audio.value.addEventListener('play', updatePlayStatus);
      audio.value.addEventListener('pause', updatePlayStatus);
    });
    onUnmounted(() => {
      if (intervalId) {
        clearInterval(intervalId);
      }
      audio.value.removeEventListener('play', updatePlayStatus);
      audio.value.removeEventListener('pause', updatePlayStatus);
    });
    watch(musicName, () => {
      if (audio.value) {
        audio.value.load(); // 确保加载新音频源
      }
    });
    return {
      audio,
      ul,
      container,
      name,
      dialogFormVisible,
      musicName,
      fetchLyrics,
      musicLists,
      isDialog,
      titleButton,
      form,
      title,
      imgUrl,
      isPlay,
      Play,
      previousSong,
      nextSong,
      musicAdd,
      handleFileChange,
      uploadFile,
      handleChange
    };
  }
}

</script>

<template>
  <div class="body" :style="{ background: `url(${imgUrl})`,backgroundSize: 'cover'}">
    <br>
    <div class="button">
      <el-input class="el-input" @keyup.enter="fetchLyrics" placeholder="输入歌曲名称" v-model="name" size="small"
                style="width: 200px"/>
      <el-button type="primary" style="margin-left: 10px" size="mini" icon="el-icon-search" @click="fetchLyrics" round>
        搜索
      </el-button>
      <el-button type="success" style="margin-left: 10px" icon="el-icon-plus" size="mini" @click="isDialog" round>添加
      </el-button>
    </div>
    <el-dialog title="添加歌曲" :visible.sync="dialogFormVisible">
      <el-form :model="form">
        <el-form-item label="歌曲名称" label-width="120px">
          <el-input v-model="form.musicName" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="歌词" label-width="120px">
          <el-input type="textarea" rows="10" show-word-limit maxlength="5000" v-model="form.text"
                    autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="mp3文件" label-width="120px">
          <el-upload
                     class="upload-demo custom-upload"
                     drag
                     :http-request="uploadFile"
                     limit="1"
                     accept=".mp3"
                     multiple
                     :on-change="handleFileChange">
            <div class="el-upload__text">
              选择文件或 <em>拖拽上传</em>
            </div>
          </el-upload>
        </el-form-item>
        <el-form-item label="歌曲背景" label-width="120px">
          <el-upload
                     class="upload-demo custom-upload"
                     drag
                     :http-request="uploadFile"
                     limit="1"
                     accept=".jpg,.png"
                     multiple
                     :on-change="handleChange">
            <div class="el-upload__text">
              选择文件或 <em>拖拽上传</em>
            </div>
          </el-upload>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="musicAdd">确 定</el-button>
      </div>
    </el-dialog>
    <div class="text-title">{{ title }}</div>
    <div class="container" ref="container">
      <ul class="lrc-list" ref="ul"></ul>
    </div>
    <div class="button">
      <audio ref="audio" :src="musicName" type="audio/mpeg" controls/>
      <button @click="previousSong" class="buttons"><i class="el-icon-arrow-left"></i>上一曲</button>
      <button v-if="isPlay" @click="Play" class="buttonss"><i class="el-icon-video-pause"></i></button>
      <button v-else @click="Play" class="buttonss"><i class="el-icon-video-play"></i></button>
      <button @click="nextSong" class="buttons">下一曲<i class="el-icon-arrow-right"></i></button>
    </div>

  </div>
</template>

<style>
* {
  margin: 0;
  padding: 0;
}

.button {
  display: flex;
  justify-content: center;
}
.buttons{
  width: 60px;
  height: 25px;
  background-color: white;
  border-radius: 12px;
  border: none;
  font-size: 11px;
  align-items: center;
  color: darkslategrey;
  margin-top: 42px;
  margin-left: 10px;
}
.buttonss{
  width: 25px;
  height: 25px;
  background-color: white;
  border-radius: 50%;
  border: none;
  font-size: 20px;
  align-items: center;
  color: darkslategrey;
  margin-top: 42px;
  margin-left: 10px;
}


.body {
  color: #666;
  height: 100vh;
  text-align: center;
}
.text-title{
  color: white;
  font-size: 20px;
  padding-top: 20px;
}
audio {
  width: 450px;
  margin: 30px 0;
}

.container {
  margin-top: 2%;
  height: 420px;
  overflow: hidden;
}

.container ul {
  transition: 0.6s;
  list-style: none;
}

.container li {
  height: 30px;
  line-height: 30px;
  transition: 0.2s;
}

.container li.active {
  color: sandybrown;
  transform: scale(1.2);
}
</style>

第三步

后端代码

记得将后端代码中resources文件夹里面的sql文件在Navicat上运行

管华尔/vue音乐播放器页面后端 - 码云 - 开源中国 (gitee.com)

第四步

数据库表样式

最后的版本

<script>
import {ref, onMounted, watch, reactive, onUnmounted} from 'vue';
import axios from "axios";

const name = ref('')
export default {
  setup() {
    const lrcData = ref([]);
    const audio = ref(null);
    const ul = ref(null);
    const container = ref(null);
    const musicName = ref('')
    const dialogFormVisible = ref(false)
    const isVolume = ref(false)
    const isPlay = ref(false)
    const selectedFile = ref(null);
    const originalTitle = ref(''); // 原始标题
    const title = ref(originalTitle);
    const musicList = ref([])
    const volumeSize = ref(50)

    const imgUrl = ref("");
    let conm = 0;
    const form = reactive({
      musicName: '',
      text: '',
      file: '',
      imgUrl: ''
    })
    // form 三个属性分别为 歌曲名称,歌词,mp3文件
    //添加对话框状态
    const isDialog = () => {
      dialogFormVisible.value = true
    }
    const tablePaly = (data) => {
      name.value = data.name
      fetchLyrics();
      name.value="";
      isPlay.value = false
      setTimeout(()=>{
        Play();
      },100)

    }
    //音量调节
    const musicVolume = (val) => {
      audio.value.volume = val/100;
      return val+"%";
    }
    //上一曲
    const previousSong = () => {
      conm = (conm - 1 + musicList.value.length) % musicList.value.length;
      name.value = musicList.value[conm].name
      fetchLyrics();
      name.value = "";
      isPlay.value = false
      setTimeout(()=>{
        Play();
      },100)
      conm--;
    }
    //下一曲
    const nextSong = () => {
      console.log("dsfdfsd")
      conm = (conm + 1) % musicList.value.length;
      name.value = musicList.value[conm].name
      fetchLyrics();
      name.value = "";
      isPlay.value = false
      setTimeout(()=>{
        Play();
      },100)
      conm++;
    }

    //播放与暂停
    const Play = () => {
      if (isPlay.value) {
        audio.value.pause();
      } else {
        audio.value.play();
      }
      isPlay.value = !isPlay.value
      console.log(isPlay.value)
    }
    // 更新播放状态
    const updatePlayStatus = (event) => {
      isPlay.value = event.type === 'play'; // 根据事件类型更新 isPlay
    };
    const musicLists = async () => {
      await axios.get("http://localhost:8080/user/musicList").then(res => {
        musicList.value = res.data.data
      }).catch(res => {
        console.log(res.error)
      })
    }
    const musicAdd = async () => {
      console.log(form)
      if (form.musicName !== null && form.text !== null) {
        await axios.post("http://localhost:8080/user/musicAdd", {
          musicName: form.musicName,
          text: form.text,
          file: form.file,
          imgUrl: form.imgUrl
        }).then(res => {
          if (res.data.code === 0) {
            form.file = '';
            form.musicName = '';
            form.text = '';
            form.imgUrl = ""
            dialogFormVisible.value = false;
            musicLists();
          }
        }).catch(res => {
          console.log(res.error)
        })
      }
    }
    const uploadFile = () => {
    }
    const handleFileChange = async (event) => {
      // 获取用户选择的文件
      const file = event.raw;
      if (file && file.type === 'audio/mpeg') {
        selectedFile.value = file;
      }
      if (selectedFile.value === null) {
        return;
      }
      let formData = new FormData;
      formData.append('file', selectedFile.value)
      await axios.post('http://localhost:8080/user/musicFileUpload', formData, {
        headers: {'Content-Type': 'multipart/form-data'}
      }).then(res => {
        if (res.data.code === 0) {
          form.file = res.data.data;
        }
      }).catch(res => {
        console.log(res.error)
      });

    };
    const handleChange = async (event) => {
      // 获取用户选择的文件
      const file = event.raw;
      if (selectedFile.value === null) {
        return;
      }
      let formData = new FormData;
      formData.append('file', file)
      await axios.post('http://localhost:8080/user/musicFileUpload', formData, {
        headers: {'Content-Type': 'multipart/form-data'}
      }).then(res => {
        if (res.data.code === 0) {
          form.imgUrl = res.data.data;
        }
      }).catch(res => {
        console.log(res.error)
      });

    };
    const findIndex = () => {
      const curTime = audio.value.currentTime;
      for (let i = 0; i < lrcData.value.length; i++) {
        if (curTime < lrcData.value[i].time) {
          return i > 0 ? i - 1 : -1; // 确保索引不会小于0
        }
      }
      return lrcData.value.length - 1; // 播放到最后一句
    };
    // 创建歌词 DOM 元素
    const createLrcElements = () => {
      // 清空原有歌词元素
      ul.value.innerHTML = '';
      lrcData.value.forEach(item => {
        const li = document.createElement('li');
        li.textContent = item.text;
        ul.value.appendChild(li);
      });
    };
    // 设置歌词滚动效果
    const setOffset = () => {
      const index = findIndex();
      const containerHeight = container.value.clientHeight;
      const liHeight = ul.value.children[0]?.clientHeight || 0; // 使用可选链防止报错
      const maxOffset = Math.max(ul.value.clientHeight - containerHeight, 0);
      let offset = liHeight * index + liHeight / 2 - containerHeight / 2;
      offset = Math.max(0, Math.min(offset, maxOffset)); // 确保 offset 在合法范围内
      ul.value.style.transform = `translateY(-${offset}px)`;
      // 设置高亮当前歌词
      const activeLi = ul.value.querySelector('.active');
      if (activeLi) {
        activeLi.classList.remove('active');
      }
      const li = ul.value.children[index];
      if (li) {
        li.classList.add('active');
      }
    };
    let con = 1; // 控制移动字符的索引
    let intervalId; // 用于存储setInterval的ID
    const titleButton = () => {
      // 清除之前的定时器
      clearInterval(intervalId);
      // 每2秒更新一次标题
      intervalId = setInterval(() => {
        // 逐个字符移动
        title.value = originalTitle.value.substring(con) + originalTitle.value.substring(0, con);
        con = (con + 1) % originalTitle.value.length;
      }, 1200);
    };
    // 创建一个获取歌词的异步函数
    const fetchLyrics = async () => {
      try {
        console.log(name.value)
        // 修改为你的 API 地址
        const data = await axios.post('http://localhost:8080/user/song', {name: name.value});
        const lyri = data.data.data
        musicName.value = lyri.songUrl
        originalTitle.value = lyri.title + " - " + lyri.artist + "  ";
        if (lyri.imgUrl) {
          imgUrl.value = lyri.imgUrl
        }
        // 假设你的歌词是一个包含时间戳和歌词的数组
        // 示例:[{ time: 0, text: '第一行歌词' }, { time: 5, text: '第二行歌词' }]
        lrcData.value = lyri.list.map((lyric) => ({
          time: lyric.time,
          text: lyric.text,
        }));
        createLrcElements(); // 更新 DOM 元素
        setOffset();
      } catch (error) {
        console.error("获取歌词失败:", error); // 处理错误情况
      }
    };
    onMounted(() => {
      fetchLyrics();
      titleButton();
      musicLists();
      audio.value.addEventListener('timeupdate', setOffset);
      audio.value.addEventListener('play', updatePlayStatus);
      audio.value.addEventListener('pause', updatePlayStatus);
      audio.value.addEventListener('ended', nextSong);
    });
    onUnmounted(() => {
      if (intervalId) {
        clearInterval(intervalId);
      }
      audio.value.removeEventListener('play', updatePlayStatus);
      audio.value.removeEventListener('pause', updatePlayStatus);
      audio.value.removeEventListener('ended', nextSong); // 移除这个事件监听器
    });
    watch(musicName, () => {
      if (audio.value) {
        audio.value.load(); // 确保加载新音频源
      }
    });
    return {
      audio,
      ul,
      container,
      name,
      dialogFormVisible,
      musicName,
      fetchLyrics,
      musicLists,
      isDialog,
      titleButton,
      form,
      title,
      imgUrl,
      isPlay,
      musicList,
      Play,
      previousSong,
      nextSong,
      musicAdd,
      tablePaly,
      volumeSize,
      isVolume,
      musicVolume,
      handleFileChange,
      uploadFile,
      handleChange
    };
  }
}

</script>

<template>
  <div class="body" :style="{ background: `url(${imgUrl})`,backgroundSize: 'cover'}">
    <br>
    <div class="button">
      <el-input class="el-input" @keyup.enter="fetchLyrics" placeholder="输入歌曲名称" v-model="name" size="small"
                style="width: 200px"/>
      <el-button type="primary" style="margin-left: 10px" size="mini" icon="el-icon-search" @click="fetchLyrics" round>
        搜索
      </el-button>
      <el-button type="success" style="margin-left: 10px" icon="el-icon-plus" size="mini" @click="isDialog" round>添加
      </el-button>
    </div>
    <el-dialog title="添加歌曲" :visible.sync="dialogFormVisible">
      <el-form :model="form">
        <el-form-item label="歌曲名称" label-width="120px">
          <el-input v-model="form.musicName" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="歌词" label-width="120px">
          <el-input type="textarea" rows="10" show-word-limit maxlength="5000" v-model="form.text"
                    autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="mp3文件" label-width="120px">
          <el-upload
              class="upload-demo custom-upload"
              drag
              :http-request="uploadFile"
              limit="1"
              accept=".mp3"
              multiple
              :on-change="handleFileChange">
            <div class="el-upload__text">
              选择文件或 <em>拖拽上传</em>
            </div>
          </el-upload>
        </el-form-item>
        <el-form-item label="歌曲背景" label-width="120px">
          <el-upload
              class="upload-demo custom-upload"
              drag
              :http-request="uploadFile"
              limit="1"
              accept=".jpg,.png"
              multiple
              :on-change="handleChange">
            <div class="el-upload__text">
              选择文件或 <em>拖拽上传</em>
            </div>
          </el-upload>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="musicAdd">确 定</el-button>
      </div>
    </el-dialog>
    <div class="text-title">{{ title }}</div>
    <div class="container" ref="container">
      <ul class="lrc-list" ref="ul"></ul>
    </div>
    <div class="button">
      <audio ref="audio" :src="musicName" type="audio/mpeg" controls/>
      <button @click="previousSong" class="buttons"><i class="el-icon-arrow-left"></i>上一曲</button>
      <button v-if="isPlay" @click="Play" class="buttonss"><i class="el-icon-video-pause"></i></button>
      <button v-else @click="Play" class="buttonss"><i class="el-icon-video-play"></i></button>
      <button @click="nextSong" class="buttons">下一曲<i class="el-icon-arrow-right"></i></button>
      <el-tooltip effect="light" placement="top">
        <div slot="content">
          <el-slider
              v-if="isVolume"
              v-model="volumeSize"
              vertical
              :format-tooltip="musicVolume"
              height="200px"
          >
          </el-slider>
        </div>
        <button @click="isVolume=!isVolume" class="buttonss">
          <svg t="1727351771803" style="width: 25px;height: 25px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4273" width="200" height="200"><path d="M128 420.576v200.864h149.12l175.456 140.064V284.288l-169.792 136.288H128z m132.256-64l204.288-163.968a32 32 0 0 1 52.032 24.96v610.432a32 32 0 0 1-51.968 24.992l-209.92-167.552H96a32 32 0 0 1-32-32v-264.864a32 32 0 0 1 32-32h164.256zM670.784 720.128a32 32 0 0 1-44.832-45.664 214.08 214.08 0 0 0 64.32-153.312 213.92 213.92 0 0 0-55.776-144.448 32 32 0 1 1 47.36-43.04 277.92 277.92 0 0 1 72.416 187.488 278.08 278.08 0 0 1-83.488 198.976zM822.912 858.88a32 32 0 1 1-45.888-44.608A419.008 419.008 0 0 0 896 521.152c0-108.704-41.376-210.848-114.432-288.384a32 32 0 0 1 46.592-43.872c84.16 89.28 131.84 207.04 131.84 332.256 0 127.84-49.76 247.904-137.088 337.728z" fill="#2c2c2c" p-id="4274"></path></svg>
        </button>
      </el-tooltip>
      <el-popover
          placement="top"
          width="400"
          class="dialog"
          trigger="click">
        <el-table
            :data="musicList"
            :show-header="false"
            height="400px"
            :border="false"
            style="width: 100%">
          <el-table-column
              prop="id"
              label="id"
              width="60">
          </el-table-column>
          <el-table-column prop="name" label="歌曲名" width="300">
            <template slot-scope="scope">
              <el-button @click="tablePaly(scope.row)" :plain="true" class="no-click-effect" >{{scope.row.name}}</el-button>
            </template>
          </el-table-column>
        </el-table>
        <button slot="reference" class="buttonss"><i class="el-icon-s-unfold"></i></button>
      </el-popover>
    </div>

  </div>
</template>

<style>
* {
  margin: 0;
  padding: 0;
}
.no-click-effect {
  border: none !important; /* 去掉边框 */
  outline: none !important; /* 去掉焦点样式 */
}

/* 去掉点击时的阴影效果 */
.no-click-effect:hover,
.no-click-effect:focus {
  box-shadow: none !important;
}

/* 去掉点击后背景颜色变化 */
.no-click-effect:active {
  background-color: transparent !important; /* 设为透明或想要的背景颜色 */
}
.dialog .el-popover{
  background-color: #f2f2f2;
  border-top-left-radius: 15px; /* 左上角圆角 */
  border-top-right-radius: 15px; /* 右上角圆角 */
}

.button {
  display: flex;
  margin-top: 80px;
  justify-content: center;
}

.buttons {
  width: 60px;
  height: 25px;
  background-color: white;
  border-radius: 12px;
  border: none;
  font-size: 11px;
  align-items: center;
  color: darkslategrey;
  margin-top: 42px;
  margin-left: 10px;
}

.buttonss {
  width: 25px;
  height: 25px;
  border-radius: 50%;
  background: white;
  border: none;
  font-size: 20px;
  align-items: center;
  color: darkslategrey;
  margin-top: 42px;
  margin-left: 10px;
}


.body {
  color: #666;
  height: 100vh;
  text-align: center;
}

.text-title {
  color: white;
  font-size: 20px;
  padding-top: 20px;
}

audio {
  width: 450px;
  margin: 30px 0;
}

.container {
  margin-top: 2%;
  height: 420px;
  overflow: hidden;
}

.container ul {
  transition: 0.6s;
  list-style: none;
}

.container li {
  height: 30px;
  line-height: 30px;
  transition: 0.2s;
}

.container li.active {
  color: sandybrown;
  transform: scale(1.2);
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值