【学生专区】python flask 轻量级项目实战,前后端分离音乐后台管理系统及前端排行榜。

一.需知:本次实战教程由本人边学边制作实现,项目安全度不高,仅可用于练手和作业!

1.项目需求:

这是供python基础生联系的,全项目采用python 的flask项目进行开发,对于大学生来说是个不错的练手项目,项目整体属于轻量型,并未进行网页加密,项目开源,但不建议商用,人家窃取信息非常简单。项目文件放在顶部资源,懒得动手的大学生就自己下载用吧。希望对各位有所帮助,关注一手,后面还会出其它框架的项目实战!!!

        1.2024版本起步的python编译工具,并配置好编译环境

        2.数据库可视化工具:navicat,17版本免费可去大佬:         https://blog.csdn.net/qq_66600223/article/details/144310474https://blog.csdn.net/qq_66600223/article/details/144310474

        3.MySQL环境5.7–9.6版本

        4.前端工具:HbuilderX,或webstrom

        5.python环境包

pip install flask 
pip install pymysql
pip install captcha
pip install CORS
pip install flask-cors
pip install twisted

2.项目效果图:

二.数据库设计

1.初步设计,概念设计

2.用户管理表

/*
 Navicat Premium Dump SQL

 Source Server         : kuwo
 Source Server Type    : MySQL
 Source Server Version : 90100 (9.1.0)
 Source Host           : localhost:3306
 Source Schema         : kuwo

 Target Server Type    : MySQL
 Target Server Version : 90100 (9.1.0)
 File Encoding         : 65001

 Date: 02/02/2025 17:54:18
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for userinfos
-- ----------------------------
DROP TABLE IF EXISTS `userinfos`;
CREATE TABLE `userinfos`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  `password` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  `level` int NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = DYNAMIC;

SET FOREIGN_KEY_CHECKS = 1;

3.排行管理表

/*
 Navicat Premium Dump SQL

 Source Server         : kuwo
 Source Server Type    : MySQL
 Source Server Version : 90100 (9.1.0)
 Source Host           : localhost:3306
 Source Schema         : kuwo

 Target Server Type    : MySQL
 Target Server Version : 90100 (9.1.0)
 File Encoding         : 65001

 Date: 02/02/2025 17:54:31
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for rankinfo
-- ----------------------------
DROP TABLE IF EXISTS `rankinfo`;
CREATE TABLE `rankinfo`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(40) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 59706 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC;

SET FOREIGN_KEY_CHECKS = 1;

4.歌手管理表

/*
 Navicat Premium Dump SQL

 Source Server         : kuwo
 Source Server Type    : MySQL
 Source Server Version : 90100 (9.1.0)
 Source Host           : localhost:3306
 Source Schema         : kuwo

 Target Server Type    : MySQL
 Target Server Version : 90100 (9.1.0)
 File Encoding         : 65001

 Date: 02/02/2025 17:54:49
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for singerinfo
-- ----------------------------
DROP TABLE IF EXISTS `singerinfo`;
CREATE TABLE `singerinfo`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `singer` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
  `singerimg` varchar(600) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 14289021 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC;

SET FOREIGN_KEY_CHECKS = 1;

5.歌曲管理表

/*
 Navicat Premium Dump SQL

 Source Server         : kuwo
 Source Server Type    : MySQL
 Source Server Version : 90100 (9.1.0)
 Source Host           : localhost:3306
 Source Schema         : kuwo

 Target Server Type    : MySQL
 Target Server Version : 90100 (9.1.0)
 File Encoding         : 65001

 Date: 02/02/2025 17:54:42
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for singinginfo
-- ----------------------------
DROP TABLE IF EXISTS `singinginfo`;
CREATE TABLE `singinginfo`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `song` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
  `album` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `singing` varchar(400) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
  `img` varchar(600) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
  `singerid` int NOT NULL,
  `intro` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL,
  `rankid` int NOT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `singer`(`singerid` ASC) USING BTREE,
  INDEX `rankid`(`rankid` ASC) USING BTREE,
  CONSTRAINT `rankid` FOREIGN KEY (`rankid`) REFERENCES `rankinfo` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `singer` FOREIGN KEY (`singerid`) REFERENCES `singerinfo` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 710 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC;

SET FOREIGN_KEY_CHECKS = 1;

完成以上数据库设计呢,我们就可以使用爬虫,对自己想要的歌曲进行爬取。

三.爬虫部分

1.须知 !!!:

       免责声明,内容只供学习!!       

        由于爬虫的内容是未经允许的,为避免版权及内容追究,在这里我们不展示完整代码,有需要可自行去网络上学习爬虫相关内容,这里展示部分理念和无关网站解密的数据库存入代码。有且只能用来学习!!!!

2.爬虫理念:

        通过selenium和请求头模块进行网页请求模拟,进行爬取酷狗音乐的音乐,并通过sq语句进行判断数据库表与表之间,,字段间存在的外键进行对数据库的添加。Selenium 和请求头在网页数据采集和自动化操作中都具有重要的地位。Selenium 提供了强大的浏览器自动化功能,但需要注意性能和资源消耗问题;请求头则是一种简单而有效的伪装和信息传递手段,可以帮助我们更好地应对网站的反爬机制。在实际应用中,将两者结合起来,并根据具体情况灵活运用各种技巧和策略,能够提高爬虫的效率、稳定性和隐蔽性,从而更好地完成数据采集任务。同时,也要遵守网站的使用规则和法律法规,确保爬虫行为的合法性和道德性。

3.软件包

这里的酷狗音乐是目录名,dbcome是自己写的本地包

4.dbcome

        用来对数据库进行连接,从而使其实现增删改查

dbcome.py

import pymysql


class MysqlUtil:
    def __init__(self):
        self.con = pymysql.connect(host="127.0.0.1", port=3306, db="your db", user="root", passwd="123456",
                                   charset="utf8")
        self.cursor = self.con.cursor(pymysql.cursors.DictCursor)

    # 数据添加、删除、修改
    def adddeledit(self, sql):
        self.cursor.execute(sql)
        i = self.cursor.rowcount
        self.con.commit()
        self.con.close()
        return i

    # 查询一条数据
    def get_one(self, sql):
        self.cursor.execute(sql)
        row = self.cursor.fetchone()
        self.con.close()
        return row

    # 查询所有数据
    def get_list(self, sql):
        self.cursor.execute(sql)
        rows = self.cursor.fetchall()
        self.con.close()
        return rows

db处应修改为你自己的数据库名称,这里原给的是kuwo,port为你的端口号,user用户,passward密码,utf8支持中文字符集

5.部分理念截图

数据爬入后,数据库应为

歌曲MP3文件和封面歌手图片文件先存入本地image和music文件夹内,稍后会用上

四.服务器端(及python flask内容)

1.整体文件结构

html文件放入\kuwo\manage\templates,css,js放入对应的文件夹里,py路由文件放在\kuwo\manage,前端路由跨域模块放入userweb

2.实现登录页面及表单验证

首先我们把登录页面的html元素写出来,两个文本框和一个密码框,还有背景视频(自己可以替换成图片)script内容为自动播放视频背景。给html内容放入\kuwo\manage\templates\login.html

login.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
    <!-- 引入登录页面的样式表 -->
    <link rel="stylesheet" href="/static/css/login.css">
    <!-- 引入jQuery库 -->
    <script src="/static/js/jquery-3.0.0.js"></script>
    <!-- 引入登录页面的脚本 -->
    <script src="/static/js/login.js"></script>
</head>
<body>
<!-- 视频标签,用于播放登录页面的背景视频 -->
<video id="vi"  autoplay muted aria-controls="false" >

<source src="/static/images/login.mp4" type="video/mp4">

</video>
{#<img src="/static/images/1.webp" id="im">#}
<!-- 登录页面的标题 -->
<div class="title"><h1></h1></div>
<!-- 登录表单 -->
<form action=""id="loginform">
    <!-- 用户名输入框 -->
    <div>
        <label for="username">用户名:</label>
        <input type="text" name="username" id="username" placeholder="请输入用户名">
    </div>
    <!-- 密码输入框 -->
    <div>
        <label for="password">密&empty;码:</label>
        <input type="password" name="password" id="password" placeholder="请输入密码">
    </div>
    <!-- 验证码输入框 -->
    <div>
        <label for="code">验证码:</label>
        <input type="text" name="code" id="code" placeholder="请输入验证码">
        <!-- 验证码图片 -->
        <img src="/code" alt="" id="recode">
    </div>
    <!-- 登录按钮 -->
    <button type="button" id="btnlogin">登录</button>
</form>

<script>
    // 获取视频元素
    var myVideo = document.getElementById("vi");

    // 隐藏视频控制条
    function hideControls() {
        myVideo.removeAttribute("controls");
    }

    // 显示视频控制条
    function showControls() {
        myVideo.setAttribute("controls", "true");
    }

    // 调用隐藏控制条的函数
    hideControls(); // 调用该函数即可隐藏控制条
</script>
</body>
</html>

为了在页面保持美观,我们给login.html写上样式,目录位置:\kuwo\static\css\login.css

login.css
* {
    margin: 0;
    padding: 0;
    list-style: none;
    font-family: "微软雅黑", serif;


}

a {
    text-decoration: none;
}

body {
    width: 100%;
    height: 100%;
}

#im {
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: -1;
}

form {
    width: 400px;
    height: 250px;
    background: rgba(144,0,0,0.2);
    position: absolute;
    top: 50%;
    left: 50%;
    margin-left: -200px;
    margin-top: -150px;
    border: 4px solid #fff;
    border-radius: 10px;
}

form div {
    height: 60px;
    line-height: 50px;
}

form div label {
    display: block;
    width:20%;
    float: left;
    text-align: right;
    color: white;
}

form div input {
    width: 68%;
    height: 30px;
    padding-left: 10px;
}

form div:nth-child(4) {
    width: 30%;
}

form button {
    display: block;
    border: none;
    border-radius: 3px;
    margin: 3px auto;
    padding: 10px 20px;
    color: blue;
    background: gold;
    cursor: pointer;
}
#code{
    width:45%;
}
#recode{
    width: 20%;
    height: 15%;
    position: absolute;
    z-index: 10;
    padding-left:2% ;
    padding-top: 1%;
    cursor: pointer;
}
h3{
    text-align: center;
}
.title{

}
#vi {
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: -1;
    object-fit: fill;


}

再给登录页面写入交互功能,文件目录位置:\kuwo\static\js\login.js

login.js
// 页面加载事件
$(function (){
    ref_code(); // 刷新验证码
    submit_form(); // 登录按钮单击事件
})

// 刷新验证码
function ref_code(){
    $("#recode").on("click",function (){
        $(this).attr("src","/code?time="+new Date().getMilliseconds())
    })
}

// 登录按钮单击事件
function submit_form(){
    $("#btnlogin").on("click",function (){
        if($("#username").val()=="" || $("#password").val()=="" || $("#code").val()==""){
            alert("请把登录信息填写完整!");
            return null;
        }
        else {
              // 使用ajax提交表单数据
            $.ajax({
                url:"../manage/login", // 提交的路径
                type:"post",// 提交的方式
                data:new FormData($("#loginform")[0]), // 提交的数据
                cache:false,
                processData: false,
                contentType:false,
                dataType:"json",// 接收的数据类型
                success:function (result){ // result:自定义的接收返回数据的变量名
                    if(result.success){
                        window.location.href=result.url;
                    }
                    else{
                        alert(result.msg);
                    }
                },
                error:function (){
                    alert("内部错误,请联系管理员!")
                }
            })
        }
    })
}

还有flask路由位置别忘了,我们把登录页面和用户的写在一个文件,写入登录判断,还有等会用的欢迎界面及用户界面的路由。

user.py
from flask import Blueprint, render_template, session, jsonify, request, redirect

from dbcomn import MysqlUtil

# 注册蓝图
user = Blueprint("user", __name__, url_prefix="/manage", template_folder="templates")


# 登录界面
@user.route("/")
def login():
    return render_template("login.html")


# 主界面
@user.route("/admin")
def admin():
    #判断是否登录
    if session.get("user") is None:
        return  redirect("/manage")
    return render_template("admin.html")


# 欢迎界面
@user.route("/welcome")
def welcome():
    return render_template("welcome.html")


# 用户管理界面
@user.route("/user_list")
def user_list(name=""):
    sql = "select * from userinfos"
    if name != "":
        sql += f" WHERE username LIKE '%{name}%'"
    mysqlutil = MysqlUtil()
    lis = mysqlutil.get_list(sql)
    data = {
        "lis": lis,
        "name": name
    }
    return render_template("user.html", data=data)



# 登录判断 methods :提交方式
@user.route("/login", methods=["post"])
def judge_login():
    if request.method == "POST":
        username = request.form["username"]
        password = request.form["password"]
        code = request.form["code"]
        # 判断验证码是否输入正确
        if str(session.get("code")).upper() == code.upper():
            sql = f"select * from userinfos where username='{username}'"
            mysqlutil = MysqlUtil()
            lit = mysqlutil.get_one(sql)
            if lit is not None:
                if password == lit["password"]:
                    session["user"] = lit
                    return jsonify({"success": True, "url": "/manage/admin"})
                else:
                    return jsonify({"success": False, "msg": "密码输入错误"})
            else:
                return jsonify({"success": False, "msg": "当前用户不存在"})
        else:
            return jsonify({"success": False, "msg": "验证码错误"})


# 根据条件查询用户
@user.route("/find_user", methods=["get"])
def find_user():
    name = request.args.get("name")
    return user_list(name)

此时就可以看见页面样子

接着,把数据库内容连上,也就是上文爬虫里的dbcome一样,直接拿过来用4.dbcome,目录位置为:\kuwo\dbcomn.py,这样即可在点击登陆时判断数据库中是否有该用户来实现登录。

现在,你可以直接在网页地址栏那直接跳过登录页面,这样就没有登陆的必要,所以我们要写一个重定向,让它默认页面为登陆页面,且不通过登录,无法实现地址栏直接跳转,由于不想拆开弄,直接连后面的所有(包括添加蓝图,生成验证码,和导入的模块)内容都放进去。

app.py (也是项目启动的源文件)  文件目录地址:\kuwo\app.py
from flask import Flask, make_response, session, redirect

from manage.qin import gangqin
from manage.ranking import ranking
from manage.singer import singer
from manage.singing import singing
from manage.user import user
from captcha.image import ImageCaptcha
from random import randint, choice

from manage.xiaoyx import xyx, xiaoyouxi
from userweb.content import content
from flask_cors import CORS
from waitress import serve
app = Flask(__name__)
CORS(app)
# 设置密钥 ,启用session
app.secret_key = "jin"

app.register_blueprint(user)
app.register_blueprint(ranking)
app.register_blueprint(singer)
app.register_blueprint(singing)
app.register_blueprint(xiaoyouxi)
app.register_blueprint(gangqin)
app.register_blueprint(content)

# 重定向到登陆页面
@app.route("/")
def index():
    return  redirect("/manage")

# 生成验证码
@app.route("/code")
def get_code():
    code = ""  # 存验证码
    for i in range(4):
        code += choice((chr(randint(65, 90)), str(randint(0, 9))))  # 从26个字母和数字0-9之间随机抽取一个
        # 存储验证码的值到session
        session["code"] = code
    image = ImageCaptcha().generate_image(code)
    # 存储验证码图片
    image.save("static/images/code.png")
    # 打开验证码图片,转换为字节流
    with open("static/images/code.png", "rb") as f:
        image_data = f.read()
    response = make_response(image_data)
    response.headers["Content-Type"] = "images/png"
    return response


if __name__ == '__main__':

    app.run(debug=True)

现在实现了登录页面和表单验证,点击登录以后应该要有一个欢迎页面,和一个导航栏,所以

3.欢迎页面及滑动导航栏

admin.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Lei的音乐管理系统</title>
     <link rel="icon" href="/static/images/logo.jpg" type="image/x-icon">
    <link rel="stylesheet" href="/static/css/admin.css">
</head>
<script src="/static/js/admin.js"></script>
<body>
    <div class="top">
        <h2>====Lei的音乐后台管理</h2>
    </div>
     <div class="shell">
        <ul class="nav">
            <li class="active" id="logo">
                <a href="#">
                    <div class="icon">
                        <div class="imageBox">
                            <img src="/static/images/微信图片_20241129160757.jpg" alt="">
                        </div>
                    </div>
                    <div class="text">Lei的音乐管理后台</div>
                </a>
            </li>
            <li>
                <a href="/manage/user_list" target="mainFrame">
                    <div class="icon">
                        <i class="iconfont icon-cangku"></i>
                    </div>
                    <div class="text">用户管理</div>
                </a>
            </li>
            <li>
                <a href="/ranking/ranking_list" target="mainFrame">
                    <div class="icon">
                        <i class="iconfont icon-zhuti_tiaosepan"></i>
                    </div>
                    <div class="text">排行管理</div>
                </a>
            </li>
            <li>
                <a href="/singer/singer_list" target="mainFrame">
                    <div class="icon">
                        <i class="iconfont icon-qianbao"></i>
                    </div>
                    <div class="text">歌手管理</div>
                </a>
            </li>
            <li>
                <a href="/singing/singing_list" target="mainFrame">
                    <div class="icon">
                        <i class="iconfont icon-tupian"></i>
                    </div>
                    <div class="text">歌曲管理</div>
                </a>
            </li>
            <li>
                <a href="/xiaoyouxi/xyx" target="mainFrame">
                    <div class="icon">
                        <i class="iconfont icon-erweima"></i>
                    </div>
                    <div class="text">小游戏</div>
                </a>
            </li>
            <li>
                <a href="/gangqin/gangqin_list" target="mainFrame">
                    <div class="icon">
                        <i class="iconfont icon-dunpaibaoxianrenzheng"></i>
                    </div>
                    <div class="text">钢琴</div>
                </a>
            </li>
            <li>
                <a href="/static/images/二维码.jpg" target="mainFrame">
                    <div class="icon">
                        <div class="imageBox">
                            <img src="/static/images/微信图片_20241129160757.jpg" alt="">
                        </div>
                    </div>
                    <div class="text">ME</div>
                </a>
            </li>
        </ul>
    </div>

{#    <section id="">用户管理</section>#}
{#    <section id="theme">Theme</section>#}
{#    <section id="wallet">Wallet</section>#}
{#    <section id="picture">Picture</section>#}
{#    <section id="code">QR code</section>#}
{#    <section id="authentication">authentication</section>#}
{#    <section id="me">ME</section>#}
{#     主内容区域#}
    <div class="content">
        <iframe src="/manage/welcome" frameborder="0" name="mainFrame" id="mainFrame"></iframe>
    </div>
</body>
</html>
welcome.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>欢迎界面</title>
</head>
    <script src="/static/js/jquery-3.0.0.js"></script>
    <script src="/static/js/login.js"></script>
<style>
    *{
        padding: 0;
        margin: 0;
    }
    body{
        width: 100%;
        height: 100%;

    }
    #vi{
        width: 100%;
        height: 100%;
        position: absolute;
        object-fit: fill;
        z-index: -1;

    }
    /* 隐藏整个进度条 */
/* 全屏按钮 */
video::-webkit-media-controls-fullscreen-button {
  display: none;
}
/* 播放按钮 */
video::-webkit-media-controls-play-button {
  display: none;
}
/* 进度条 */
video::-webkit-media-controls-timeline {
  display: none;
}
/* 观看的当前时间 */
video::-webkit-media-controls-current-time-display {
  display: none;
}
/* 剩余时间 */
video::-webkit-media-controls-time-remaining-display {
  display: none;
}
/* 音量按钮 */
video::-webkit-media-controls-mute-button {
  display: none;
}
video::-webkit-media-controls-toggle-closed-captions-button {
  display: none;
}
/* 音量的控制条 */
video::-webkit-media-controls-volume-slider {
  display: none;
}
/* 所有控件 */
video::-webkit-media-controls-enclosure {
  display: none;
}


    {#video{#}
    {#    width: 100%;#}
    {#    height: 100%;#}
    {#    position: absolute;#}
    {#    z-index: -1;#}
    {#}#}
</style>
<body>
<video controls id="vi" loop autoplay muted aria-controls="false" >

<source src="/static/images/welcome.mp4" type="video/mp4" >



</video>
{#<img src="/static/images/2.webp" alt="">#}
{#<video src="/static/images/合12sd成%201%20(1).mp4"></video>#}
</body>
</html>

图片视频src路径记得自己更换一下

接下来是样式

admin.css

/* 头部样式*/
div.top{
    width: 100%;
    height: 60px;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    background: #212020;
}
div.top h2{
    height: 60px;
    line-height: 60px;
    margin-left: 40px;
    color: #fff;
    font-weight: 600;
    font-size: 22px;
}
/*侧边导航样式*/
div.sidebar{
    width: 160px;
    position: fixed;
    top: 60px;
    left: 0;
    right: 0;
    bottom: 0;
    background: #3e2b2b;
}

* {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            list-style: none;
            text-decoration: none;
        }

        body {
            background: #e4e9f5;
        }

        section {
            position: relative;
            width: 100%;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            font: 900 100px '';
            color: #ffa21759;
            background: #e4e9f5;
        }

        .shell {
            position: relative;
            width: 84px;
            height: 100%;
            background: #000000;
            z-index: 9999;
            transition: width 0.5s;
            padding-left: 10px;
            overflow: hidden;
        }

        .shell:hover {
            width: 300px;
        }

        .imageBox {
            position: relative;
            width: 50px;
            height: 50px;
            border-radius: 50%;
            overflow: hidden;
        }

        .imageBox img {
            width: 100%;
            height: 100%;
            object-fit: cover;
        }

        .shell ul {
            position: relative;
            height: 100vh;
        }

        .shell ul li {
            position: relative;
            padding: 5px;
        }

        .active {
            background: #e4e9f5;
            border-top-left-radius: 50px;
            border-bottom-left-radius: 50px;
        }

        .active::before {
            content: "";
            position: absolute;
            top: -30px;
            right: 0;
            width: 30px;
            height: 30px;
            border-bottom-right-radius: 25px;
            box-shadow: 5px 5px 0 5px #e4e9f5;
            background: transparent;
        }

        .active::after {
            content: "";
            position: absolute;
            bottom: -30px;
            right: 0;
            width: 30px;
            height: 30px;
            border-top-right-radius: 25px;
            box-shadow: 5px -5px 0 5px #e4e9f5;
            background: transparent;
        }

        #logo {
            margin: 40px 0 100px 0;
        }

        .shell ul li a {
            position: relative;
            display: flex;
            white-space: nowrap;
        }

        .icon {
            position: relative;
            display: flex;
            justify-content: center;
            align-items: center;
            min-width: 60px;
            padding-left: 10px;
            height: 70px;
            color: #333;
            transition: 0.5s;
            color: rgb(255, 255, 255);
        }

        .icon i {
            font-size: 30px;
            z-index: 999;
        }

        .text {
            position: relative;
            height: 70px;
            display: flex;
            align-items: center;
            font-size: 20px;
            color: #ffad32c1;
            padding-left: 15px;
            text-transform: uppercase;
            letter-spacing: 2px;
            transition: 0.5s;
        }

        .shell ul li:hover a .icon,
        .shell ul li:hover a .text {
            color: #ffa117;
        }

        .active a .icon::before {
            content: "";
            position: absolute;
            inset: 5px;
            width: 60px;
            background: #000000;
            border-radius: 50%;
            transition: 0.5s;
            border: 7px solid #ffa117;
            box-sizing: border-box;
        }
/*div.sidebar nav ul li{*/
/*    height: 40px;*/
/*    line-height: 40px;*/
/*    border-bottom: 1px solid #d1cece;*/
/*    list-style-type: none;*/
/*    flex-direction: column;*/
/*    justify-content: center;*/
/*    align-items: center;*/
/*}*/
/*div.sidebar nav ul li a{*/
/*    display: block;*/
/*    width: 100%;*/
/*    text-align: center;*/
/*    color: #fff;*/
/*    background: #3e2b2b;*/
/*    transition: 0.3s;*/
/*}*/
/*div.sidebar nav ul li a:hover{*/
/*    background: #212020;*/
/*}*/
/*div.sidebar nav ul li a:active{*/
/*    background: #212020;*/
/*}*/

/*主内容区域样式*/
div.content{
    position: fixed;
    top: 60px;
    left: 85px;
    right: 0;
    bottom: 0;
}
div.content iframe{
    width: 100%;
    height: 100%;
}






点击排行榜交互功能

admin.js
$(function (){
    nav_click()
});

//导航栏单击事件
function  nav_click(){
    $("div.sidebar nav ul li").each(function (e,i){
        $(i).find("a").on("click",function (){
            $(this).css({"background":"#000000"}).parent().siblings().find("a").css({"background":"#0b0b0b"})
        })
    })
    let nav = document.querySelectorAll(".nav li");
    function activeLink() {
        nav.forEach((item) => item.classList.remove("active"));
        this.classList.add("active");
    }
    nav.forEach((item) => item.addEventListener("click", activeLink));
}

实现完导航栏点击事件后,就到用户管理模块了,上面已经给出flask路由了user.py,所以实现

4.用户管理页面的内容

user.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<link rel="stylesheet" href="/static/css/user.css">
<body>
<video controls id="vi"  autoplay muted aria-controls="false" >

<source src="/static/images/welcome.mp4" type="video/mp4" >

</video>

    <form action="/manage/find_user" method="get">
        <input type="text" name="name" value="{
  
  { data.name }}" placeholder="请输入用户名">
        <input type="submit" value="搜索">
    </form>
    <table>
        <thead>
            <tr>
                <td class="yhm">用户名</td>
                <td class="mm">密码</td>
                <td class="id">身份</td>
            </tr>
        </thead>
        <tbody>
            {% for item in data.lis %}
                <tr>
                    <td>{
  
  { item["username"] }}</td>
                    <td>{
  
  { item["password"] }}</td>
                    {% if item["level"]==0 %}
                        <td>管理员</td>
                    {% else %}
                        <td>普通用户</td>
                    {%  endif %}
                </tr>
            {% endfor %}
        </tbody>
    </table>

<script>
    var myVideo = document.getElementById("vi");

    function hideControls() {
        myVideo.removeAttribute("controls");
    }

    function showControls() {
        myVideo.setAttribute("controls", "true");
    }

    hideControls(); // 调用该函数即可隐藏控制条
</script>
</body>
</html>

user.css
/*table tr{*/
/*    width: 1800px;*/
/*    height: 50px;*/
/*    border: 2px solid white;*/
/*}*/
/*table td{*/
/*    color: #51ef00;*/
/*    width: 1800px;*/
/*    height: 50px;*/
/*    border: 2px solid white;*/
/*    text-align: center;*/
/*}*/
/*.yhm{*/
/*    background: rgba(152, 230, 11, 0.2);*/

/*}*/
/*.mm{*/
/*    background: rgba(5, 62, 248, 0.2);*/

/*}*/
/*.id{*/
/*    background: rgba(255, 255, 255, 0.2);*/
/*}*/
/*form {*/
/*    line-height: 50px;*/
/*    background-image: url("/static/images/260.jpg");*/
/*}*/
/* *{*/
/*        padding: 0;*/
/*        margin: 0;*/
/*    }*/
/*    body{*/
/*        width: 100%;*/
/*        height: 100%;*/
/*    }*/
/*    img{*/
/*        width: 100%;*/
/*        height: 100%;*/
/*        position: absolute;*/
/*        z-index: -1;*/
/*    }*/
    #vi{
        width: 100%;
        height: 100%;
        position: absolute;
        object-fit: fill;
        z-index: -1;
    }

    body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f4;
    margin: 0;
    padding: 0;
}



form {
    margin: 20px;
}

input[type="text"] {
    padding: 10px;
    font-size: 16px;
    border: 1px solid #ccc;
    border-radius: 5px;
    width: 200px;
}

input[type="submit"] {
    padding: 10px 20px;
    font-size: 16px;
    background-color: #4CAF50;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}

table {
    width: 80%;
    margin: 20px auto;
    border-collapse: collapse;
}

th, td {
    padding: 15px;
    text-align: left;
    border-bottom: 1px solid #ddd;
}

th {
    background-color: #4CAF50;
    color: white;
}

.yhm {
    background-color: #f9f9f9;
}

.mm {
    background-color: #e9e9e9;
}

.id {
    background-color: #f9f9f9;
}

用户部分到这结束,还剩下三个模块。

5.排行管理页面内容

排行管理里包含上面一样的操作外,还增加了“增删改的功能”和页面,一个一个来实现把。

ranking.py
from flask import Blueprint, render_template, session, jsonify, request

from dbcomn import MysqlUtil

# 注册蓝图
ranking = Blueprint("ranking", __name__, url_prefix="/ranking", template_folder="templates")


# 用户管理界面
@ranking.route("/ranking_list")
def ranking_list(name=""):
    sql = "SELECT * FROM rankinfo"
    if name != "":
        sql += f" WHERE name LIKE '%{name}%'"
    mysqlutil = MysqlUtil()
    lis = mysqlutil.get_list(sql)
    data = {
        "lis": lis,
        "name": name
    }
    return render_template("ranking.html", data=data)


# 根据条件查询用户
@ranking.route("/find_ranking", methods=["get"])
def find_ranking():
    name = request.args.get("name")
    return ranking_list(name)


# 新增界面
@ranking.route("/ranking_add")
def ranking_add():
    return render_template("ranking_add.html")


# 新增
@ranking.route("/add", methods=["post"])
def add():
    if request.method == "POST":
        name = request.form["name"]
        sql = f"SELECT * FROM rankinfo WHERE `name`='{name}'"
        mysqlutil = MysqlUtil()
        row = mysqlutil.get_one(sql)
        if row is None:
            sql3 = f"INSERT INTO rankinfo(`name`) VALUES ('{name}') "
            mysqlutil2 = MysqlUtil()
            i = mysqlutil2.adddeledit(sql3)
            if i > 0:
                return jsonify({"success": True, "msg": "保存成功!"})
            else:
                return jsonify({"success": False, "msg": "保存失败!"})
        else:
            return jsonify({"success": False, "msg": "该类别已存在!"})



# 修改界面
@ranking.route("/ranking_edit/<id>")
def ranking_edit(id):
    sql = f"SELECT * FROM rankinfo WHERE id={id}"
    mysqlutil = MysqlUtil()
    row = mysqlutil.get_one(sql)
    return render_template("ranking_edit.html", data=row)


# 修改
@ranking.route("/edit", methods=["post"])
def edit():
    if request.method == "POST":
        id = request.form["id"]
        name = request.form["name"]
        sql = f"UPDATE rankinfo SET `name`='{name}' WHERE id={id}"
        mysqlutil = MysqlUtil()
        i = mysqlutil.adddeledit(sql)
        if i > 0:
            return jsonify({"success": True, "msg": "修改成功!"})
        else:
            return jsonify({"success": False, "msg": "修改失败!"})


# 删除
@ranking.route("/del", methods=["post"])
def delete():
    if request.method == "POST":
        id = request.form["id"]
        # sql = f"SELECT * FROM rankinfo WHERE `id`='{id}'"
        # mysqlutil = MysqlUtil()
        # row = mysqlutil.get_one(sql)
        # if row is None:
        sql2 = f"DELETE FROM rankinfo WHERE `id`='{id}' "
        mysqlutil2 = MysqlUtil()
        i = mysqlutil2.adddeledit(sql2)
        if i > 0:
            return jsonify({"success": True, "msg": "删除成功!"})
        else:
            return jsonify({"success": False, "msg": "删除失败!"})
    else:
            return jsonify({"success": False, "msg": "该类别无法删除!"})
ranking.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<link rel="stylesheet" href="/static/css/ranking.css">
<link rel="stylesheet" href="/static/js/layer/layer/theme/default/layer.css">
<script src="/static/js/jquery-3.0.0.js"></script>
<script src="/static/js/layer/layer/layer.js"></script>
<script src="/static/js/ranking.js"></script>
<body>

{#<video controls="controls" id="vi" loop autoplay muted aria-controls="false" >#}
{##}
{#<source src="/static/images/welcome.mp4" type="video/mp4" >#}
{##}
{##}
{##}
{#</video>#}
{#    <img src="/static/images/3.webp" alt="">#}
    <form action="/ranking/find_ranking" method="get">
        <input type="text" name="name" value="{
  
  { data.name }}" placeholder="请输入排行榜名称">
        <input type="submit" value="搜索">
    </form>
        <button type="button" class="bth_add">新增</button>
        <table>
            <thead>
                <tr>
                    <td>排名</td>
                    <td>名字</td>
                    <td>操作</td>
                </tr>
            </thead>
            <tbody>
                    {% for item in data.lis %}
                        <tr>
                            <td>{
  
  { item["id"] }}</td>
                            <td>{
  
  { item["name"] }}</td>
                            <td class="bth">
                                <p data-id="{
  
  { item["id"] }}">修改</p>
                                <p data-id="{
  
  { item["id"] }}" data-name="{
  
  { item["name"] }}">删除</p>
                            </td>
                        </tr>
                {% endfor %}
            </tbody>
        </table>


</body>
</html>
ranking.css
body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f4;
    margin: 0;
    padding: 0;

}

video {
        width: 100%;
        height: 100%;
        position: absolute;
        object-fit: fill;
        z-index: -1;
        background-attachment: scroll;
}
 img{
        width: 100%;
        height: 100%;
        position: absolute;
        object-fit: fill;
        z-index: -1;
         background-attachment: fixed;
    }

form {
    margin: 20px;
}

input[type="text"] {
    padding: 10px;
    font-size: 16px;
    border: 1px solid #ccc;
    border-radius: 5px;
    width: 200px;
}

input[type="submit"] {
    padding: 10px 20px;
    font-size: 16px;
    background-color: #4CAF50;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}

table {
    width: 80%;
    margin: 20px auto;
    border-collapse: collapse;
}

th, td {
    padding: 15px;
    text-align: left;
    border-bottom: 1px solid #ddd;
}

th {
    background-color: #4CAF50;
    color: white;
}

.bth_add {
    padding: 10px 20px;
    font-size: 16px;
    background-color: #4CAF50;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    margin: 20px;
}

.bth {
    display: flex;
    justify-content: space-around;
}

.bth p {
    cursor: pointer;
    color: #4CAF50;
}


/* 全屏按钮 */
video::-webkit-media-controls-fullscreen-button {
  display: none;
}
/* 播放按钮 */
video::-webkit-media-controls-play-button {
  display: none;
}
/* 进度条 */
video::-webkit-media-controls-timeline {
  display: none;
}
/* 观看的当前时间 */
video::-webkit-media-controls-current-time-display {
  display: none;
}
/* 剩余时间 */
video::-webkit-media-controls-time-remaining-display {
  display: none;
}
/* 音量按钮 */
video::-webkit-media-controls-mute-button {
  display: none;
}
video::-webkit-media-controls-toggle-closed-captions-button {
  display: none;
}
/* 音量的控制条 */
video::-webkit-media-controls-volume-slider {
  display: none;
}
/* 所有控件 */
video::-webkit-media-controls-enclosure {
  display: none;
}
ranking_add.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>排行新增</title>
    <link rel="stylesheet" href="/static/css/ranking_add.css">
    <link rel="stylesheet" href="/static/js/layer/layer/theme/default/layer.css">
    <script src="/static/js/jquery-3.0.0.js"></script>
    <script src="/static/js/layer/layer/layer.js"></script>
    <script src="/static/js/ranking.js"></script>
</head>
<body>
     <form action="" id="addForm">
        <h2>信息新增</h2>
        <div>
            <label for="name">排行名称:</label>
            <input type="text" name="name" id="name">
        </div>
        <button type="button" id="btn_add">保存</button>
    </form>


    <div class="eyes">
      <div class="eye">
        <div class="ball"></div>
      </div>

      <div class="eye">
        <div class="ball"></div>
      </div>
    </div>

<script type="text/javascript">
  var balls = document.getElementsByClassName("ball");
  document.onmousemove = function(){
    var x = event.clientX * 100 / window.innerWidth + "%";
    var y = event.clientY * 100 / window.innerHeight + "%";

    for(var i=0;i<2;i++){
      balls[i].style.left = x;
      balls[i].style.top = y;
      balls[i].style.transform = "translate(-" + x +",-" + y +")";
    }
  }
</script>

</body>
</html>
ranking_add.css
body {
    font-family: Arial, sans-serif;
    background-color: #f2f2f2;
    margin: 0;
    padding: 0;
}

form {
    width: 80%;
    margin: 20px auto;
    background-color: #fff;
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

h2 {
    color: #4CAF50;
    text-align: center;
}

label {
    color: #4CAF50;
    font-weight: bold;
}

input[type="text"], input[type="file"] {
    width: 70%;
    padding: 8px;
    margin-right: 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
}

input[type="text"]:focus, input[type="file"]:focus {
    outline: none;
    border: 1px solid #4CAF50;
}

button {
    padding: 8px 16px;
    background-color: #4CAF50;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

button:hover {
    background-color: #45a049;
}

button#btn_add {
    margin-right: 10px;
}

button#btn_res {
    background-color: #f44336;
}

button#btn_res:hover {
    background-color: #da190b;
}


@keyframes particleAnimation {
    0% {
        transform: translateX(0) translateY(0);
    }
    100% {
        transform: translateX(100vw) translateY(100vh);
    }
}

body{
  margin: 0;
  padding: 0;
  background: #34495e;
}

.eyes{
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 100%;
  text-align: center;
}

.eye{
  width: 240px;
  height: 120px;
  background: #fff;
  display: inline-block;
  margin: 40px;
  border-radius: 50%;
  position: relative;
  overflow: hidden;
}

.ball{
  width: 40px;
  height: 40px;
  background: #000;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%,-50%);
  border-radius: 50%;
  border: 15px solid #333;
}
ranking_edit.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>排行修改</title>
    <link rel="stylesheet" href="/static/css/ranking_add.css">
    <link rel="stylesheet" href="/static/js/layer/layer/theme/default/layer.css">
    <script src="/static/js/jquery-3.0.0.js"></script>
    <script src="/static/js/layer/layer/layer.js"></script>
    <script src="/static/js/ranking.js"></script>
</head>
<body>
     <form action="" id="editForm">
         <input type="hidden" name="id" value="{
  
  { data["id"] }}">
        <h2>排行修改</h2>
        <div>
            <label for="name">排行名称:</label>
            <input type="text" name="name" id="edit_name" value="{
  
  { data["name"] }}">
        </div>
        <button type="button" id="btn_edit">保存</button>
    </form>

     <div class="eyes">
      <div class="eye">
        <div class="ball"></div>
      </div>

      <div class="eye">
        <div class="ball"></div>
      </div>
    </div>

<script type="text/javascript">
  var balls = document.getElementsByClassName("ball");
  document.onmousemove = function(){
    var x = event.clientX * 100 / window.innerWidth + "%";
    var y = event.clientY * 100 / window.innerHeight + "%";

    for(var i=0;i<2;i++){
      balls[i].style.left = x;
      balls[i].style.top = y;
      balls[i].style.transform = "translate(-" + x +",-" + y +")";
    }
  }
</script>
</body>
</html>
ranking.js
$(function (){
    basics_bth();// 基础按钮单击事件
    add_form_submit();// 新增表单提交事件
    edit_form_submit();//修改
});

// 基础按钮单击事件
function basics_bth(){
    //新增按钮单击事件
    $(".bth_add").on("click",function (){
        //打开新增界面
        window.parent.frames["mainFrame"].location.href="/ranking/ranking_add";
    })

    // 点击新增按钮弹出对话框
    $(".bth_add").click(function() {
        layer.open({
            type: 2,
            title: '新增',
            shadeClose: true,
            shade: 0.8,
            area: ['400px', '300px'],
            content: '/ranking_add' // iframe的url
        });
    });

    // 点击修改按钮弹出对话框
    $(".bth p:first-child").click(function() {
        var id = $(this).data("id");
        layer.open({
            type: 2,
            title: '修改',
            shadeClose: true,
            shade: 0.8,
            area: ['400px', '300px'],
            content: '/ranking_edit?id=' + id // iframe的url
        });
    });

    //删除/修改按钮单击事件
    $(".bth p").each(function (e,i){
        $(i).on("click",function (){
            if($(this).text()=="修改"){
                var id = $(this).attr("data-id")
                window.parent.frames["mainFrame"].location.href="/ranking/ranking_edit/"+id;
            }
            else {
                var name=$(this).attr("data-name");
                var id = $(this).attr("data-id")
                layer.confirm("是否确认删除类别名称为"+name+"的数据?",{
                    btn:["确认","取消"]
                },function (){
                    // 使用ajax提交表单数据
                $.ajax({
                    url:"/ranking/del",//提交的路径
                    type:"post",//提交的方法
                    data:{
                        "id":id
                    },//提交的数据
                    dataType:"json",//接收的数据类型
                    success:function (result){// result:自定义的接受返回数据的变量名
                        if(result.success){
                            window.parent.frames["mainFrame"].location.reload();
                        }
                        else {
                            layer.msg(result.msg,{icon: 2});
                        }
                    },
                    error:function (){
                        alert("内部错误,请联系管理员")
                    }

                })
                })



            }
        })
    })
}

//新增表单提交事件
function add_form_submit(){
    $("#btn_add").on("click",function (){
       if($("#name").val()=="") {
           layer.msg("请把信息填写完整",{icon:2});
           return null;
       }
       else {
            // 使用ajax提交表单数据
            $.ajax({
                url:"/ranking/add",//提交的路径
                type:"post",//提交的方法
                data:new FormData($("#addForm")[0]),//提交的数据
                cache:false,
                processData: false,
                contentType:false,
                dataType:"json",//接收的数据类型
                success:function (result){// result:自定义的接受返回数据的变量名
                    if(result.success){
                        layer.msg(result.msg,{icon: 1});
                        $("#name").val("");
                    }
                    else {
                        layer.msg(result.msg,{icon: 2});
                    }
                },
                error:function (){
                    alert("内部错误,请联系管理员")
                }

            })
       }
    })
}

//修改
function edit_form_submit(){
    $("#btn_edit").on("click",function (){
       if($("#edit_name").val()=="") {
           layer.msg("请把信息填写完整",{icon:2});
           return null;
       }
       else {

            // 使用ajax提交表单数据
            $.ajax({
                url:"/ranking/edit",//提交的路径
                type:"post",//提交的方法
                data:new FormData($("#editForm")[0]),//提交的数据
                cache:false,
                processData: false,
                contentType:false,
                dataType:"json",//接收的数据类型
                success:function (result){// result:自定义的接受返回数据的变量名
                    if(result.success){
                        layer.msg(result.msg,{icon: 1});
                    }
                    else {
                        layer.msg(result.msg,{icon: 2});
                    }
                },
                error:function (){
                    alert("内部错误,请联系管理员")
                }

            })
       }
    })
}

//



下面两项内容也是一样的和排行管理所以就不讲解了,不懂得上面有注释。

6.歌手管理页面内容

singer.py
import os

from flask import Blueprint, render_template, session, jsonify, request
from twisted.application import app

from dbcomn import MysqlUtil

# 注册蓝图
singer = Blueprint("singer", __name__, url_prefix="/singer", template_folder="templates")


@singer.route("/singer_list")
def singer_list(singer="", page=1, rows=5):
    # 查询歌手信息
    sql = """select * from singerinfo"""
    # 查询歌手信息总数
    sql2 = """SELECT COUNT(*) as count FROM singerinfo"""
    if singer != "":
        # 如果有查询条件,则添加查询条件
        sql += f" WHERE singer LIKE '%{singer}%'"
    # sql += f"ORDER BY id DESC LIMIT {(page - 1) * rows}, {rows}"
    mysqlutil = MysqlUtil()
    # 查询歌手信息列表
    lis = mysqlutil.get_list(sql)
    mysqlutil2 = MysqlUtil()
    # 查询歌手信息总数
    lis2 = mysqlutil2.get_one(sql2)
    count = lis2["count"]
    # 计算总页数
    if count % rows == 0:
        total = count // rows
    else:
        total = count // rows + 1
    # 每页显示的行数列表
    rows_lis = [5, 10, 20, 30, 40, 50]
    # 返回数据
    data = {
        "lis": lis,
        "singer": singer,
        "page": page,
        "total": total,
        "rows_lis": rows_lis,
        "rows": rows
    }
    return render_template("singer.html", data=data)


#翻页
@singer.route("/page_data", methods=["get"])
def page_data():
    # 获取页码和每页显示的行数
    page = request.args.get("page")
    rows = request.args.get("rows")
    # 调用singer_list函数,传入页码和每页显示的行数
    return singer_list(page=int(page), rows=int(rows))


# 根据提交查询
@singer.route("/find_singer", methods=["get"])
def find_singer():
    # 获取查询条件
    singer = request.args.get("singer")
    # 调用singer_list函数,传入查询条件
    return singer_list(singer=singer)


# 新增界面
@singer.route("/singer_add")
def singer_add():
    # 返回新增界面
    return render_template("singer_add.html")


# 新增
@singer.route("/add", methods=["post"])
def add():
    # 判断请求方法
    if request.method == "POST":
        # 获取表单数据
        singer = request.form["singer"]
        id = request.form["id"]
        singerimg = request.files["singerimg"]
        # 查询歌手信息
        sql = f"SELECT * FROM singerinfo WHERE `singer`='{singer}'"
        mysqlutil = MysqlUtil()
        row = mysqlutil.get_one(sql)
        # 判断歌手信息是否存在
        if row is None:
            # 保存图片
            singerimg = request.files["singerimg"]
            singerimg_path = os.path.join(os.getcwd() + "/static/images", singerimg.filename)
            singerimg.save(singerimg_path)
            singerimg_path = "/static/images/" + singerimg.filename
            # 插入歌手信息
            sql3 = f"INSERT INTO singerinfo(`singer`,`id`,`singerimg`) VALUES ('{singer}','{id}','{singerimg_path}') "
            mysqlutil2 = MysqlUtil()
            i = mysqlutil2.adddeledit(sql3)
            # 判断插入是否成功
            if i > 0:
                return jsonify({"success": True, "msg": "保存成功!"})
            else:
                return jsonify({"success": False, "msg": "保存失败!"})
        else:
            return jsonify({"success": False, "msg": "该类别已存在!"})

#修改

@singer.route("/singer_edit/<id>")
def singer_edit(id):
    # 根据id查询歌手信息
    sql = f"SELECT * FROM singerinfo WHERE id={id}"
    mysqlutil = MysqlUtil()
    row = mysqlutil.get_one(sql)
    # 返回修改界面
    return render_template("singer_edit.html", data=row)




@singer.route("/edit", methods=["post"])
def edit():
    # 判断请求方法
    if request.method == "POST":
        # 获取表单数据
        id = request.form["id"]
        singer = request.form["singer"]
        singerimg = request.files["singerimg"]

        # 保存图片
        singerimg_path = os.path.join(os.getcwd() + "/static/images", singerimg.filename)
        singerimg.save(singerimg_path)
        singerimg_path = "/static/images/" + singerimg.filename
        # 更新歌手信息
        sql = f"UPDATE singerinfo SET `singer`='{singer}',`id`='{id}',`singerimg`='{singerimg_path}' WHERE id={id}"
        mysqlutil = MysqlUtil()
        i = mysqlutil.adddeledit(sql)
        # 判断更新是否成功
        if i > 0:
            return jsonify({"success": True, "msg": "修改成功!"})
        else:
            return jsonify({"success": False, "msg": "修改失败!"})


# 删除
@singer.route("/del", methods=["post"])
def delete():
    # 判断请求方法
    if request.method == "POST":
        # 获取表单数据
        id = request.form["id"]
        # 查询歌手信息
        sql = f"SELECT * FROM singinginfo WHERE `id`='{id}'"
        mysqlutil = MysqlUtil()
        row = mysqlutil.get_one(sql)
        # 判断歌手信息是否存在
        if row is None:
            # 删除歌手信息
            sql2 = f"DELETE FROM singerinfo WHERE `id`='{id}'"
            mysqlutil2 = MysqlUtil()
            try:
                i = mysqlutil2.adddeledit(sql2)
                # 判断删除是否成功
                if i > 0:
                    return jsonify({"success": True, "msg": "删除成功!"})
                else:
                    return jsonify({"success": False, "msg": "删除失败!"})
            except Exception as e:
                # 记录详细的错误信息到日志文件
                app.logger.error(f"删除操作出错: {str(e)}")
                return jsonify({"success": False, "msg": "删除操作出错,请联系管理员!"})
        else:
                return jsonify({"success": False, "msg": "该类别无法删除!"})
singer.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <!-- 设置字符编码为UTF-8 -->
    <meta charset="UTF-8">
    <!-- 设置网页标题为歌手管理 -->
    <title>歌手管理</title>
    <!-- 引入歌手管理页面的CSS样式 -->
    <link rel="stylesheet" href="/static/css/singer.css">
    <!-- 引入layer插件的CSS样式 -->
    <link rel="stylesheet" href="/static/js/layer/layer/theme/default/layer.css">
    <!-- 引入jQuery库 -->
    <script src="/static/js/jquery-3.0.0.js"></script>
    <!-- 引入layer插件 -->
    <script src="/static/js/layer/layer/layer.js"></script>
    <!-- 引入歌手管理页面的JS脚本 -->
    <script src="/static/js/singer.js"></script>
</head>

<body>

    <!-- 搜索表单 -->
    <form action="/singer/find_singer" method="get">
        <!-- 输入框,用于输入歌手名称 -->
        <input type="text" name="singer" value="{
  
  { data.singer }}" placeholder="请输入歌手名称">
        <!-- 提交按钮 -->
        <input type="submit" value="搜索">
    </form>
    <!-- 新增按钮 -->
        <button type="button" class="bth_add">新增</button>
    <!-- 用于显示时间的div -->
    <div id="clock" style="position: fixed; z-index: -1;"></div>
    <!-- 歌手列表表格 -->
    <table>
        <thead>
            <tr>
                <!-- 表头,歌手 -->
                <td>歌手</td>
                <!-- 表头,歌手id -->
                <td>歌手id</td>
                <!-- 表头,歌手图片 -->
                <td>歌手图片</td>
                <!-- 表头,操作 -->
                <td>操作</td>
            </tr>
        </thead>

        <tbody>
            <!-- 遍历data.lis中的每一项 -->
            {% for item in data.lis %}
                <tr>
                    <!-- 显示歌手名称 -->
                    <td>{
  
  { item["singer"] }}</td>
                    <!-- 显示歌手id -->
                    <td>{
  
  { item["id"] }}</td>
                    <!-- 显示歌手图片 -->
                    <td><img src="{
  
  { item["singerimg"] }}" alt="" id="img"></td>
                    <!-- 操作按钮 -->
                    <td class="bth">
                        <!-- 修改按钮,data-id用于存储歌手id -->
                        <p data-id="{
  
  { item["id"] }}">修改</p>
                        <!-- 删除按钮,data-id用于存储歌手id,data-singer用于存储歌手名称 -->
                        <p data-id="{
  
  { item["id"] }}" data-singer="{
  
  { item["singer"] }}">删除</p>
                    </td>
                </tr>
            {% endfor %}
        </tbody>
    </table>




</body>
</html>
singer.css
body {
    font-family: Arial, sans-serif;
    background-color: #f2f2f2;
    margin: 0;
    padding: 0;
}

table {
    width: 80%;
    margin: 20px auto;
    border-collapse: collapse;
}

table thead tr {
    background-color: #4CAF50;
    color: white;
}

table tbody tr {
    background-color: #f2f2f2;
}

table tbody tr:nth-child(even) {
    background-color: #ddd;
}

table td, table th {
    padding: 8px;
    text-align: left;
}

table td img {
    width: 100px;
    height: 100px;
}

form {
    width: 80%;
    margin: 20px auto;
}

form input[type="text"] {
    width: 70%;
    padding: 8px;
    margin-right: 10px;
}

form input[type="submit"] {
    padding: 8px 16px;
    background-color: #4CAF50;
    color: white;
    border: none;
    cursor: pointer;
}

form select {
    padding: 8px;
    margin-right: 10px;
}

form input[type="text"]:focus, form select:focus {
    outline: none;
    border: 1px solid #4CAF50;
}

button {
    padding: 8px 16px;
    background-color: #4CAF50;
    color: white;
    border: none;
    cursor: pointer;
}

button:hover {
    background-color: #45a049;
}

button.bth_add {
    margin-bottom: 20px;
}

button.bth_add:hover {
    background-color: #45a049;
}

button.bth {
    padding: 0;
    background-color: transparent;
    border: none;
    cursor: pointer;
}

button.bth p {
    display: inline-block;
    padding: 8px 16px;
    background-color: #4CAF50;
    color: white;
    margin-right: 10px;
}

button.bth p:hover {
    background-color: #45a049;
}

button.bth p:last-child {
    margin-right: 0;
}
singer_add.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <!-- 设置字符编码为UTF-8 -->
    <meta charset="UTF-8">
    <!-- 设置网页标题 -->
    <title>排行新增</title>
    <!-- 引入CSS样式表 -->
    <link rel="stylesheet" href="/static/css/ranking_add.css">
    <!-- 引入layer样式表 -->
    <link rel="stylesheet" href="/static/js/layer/layer/theme/default/layer.css">
    <!-- 引入jQuery库 -->
    <script src="/static/js/jquery-3.0.0.js"></script>
    <!-- 引入layer库 -->
    <script src="/static/js/layer/layer/layer.js"></script>
    <!-- 引入singer.js库 -->
    <script src="/static/js/singer.js"></script>
</head>
<body>
     <!-- 表单 -->
     <form action="" id="addForm">
        <!-- 表单标题 -->
        <h2>信息新增</h2>
        <!-- 歌手名称输入框 -->
        <div>
            <label for="singer">歌手名称:</label>
            <input type="text" name="singer" id="singer">
        </div>
         <!-- 歌手id输入框 -->
         <div>
            <label for="id">歌手id:</label>
            <input type="text" name="id" id="id">
        </div>
         <!-- 歌手图片上传框 -->
         <div>
            <label for="singerimg">歌手图片:</label>
            <input type="file" name="singerimg" id="img">
        </div>
        <!-- 保存按钮 -->
        <button type="button" id="btn_add">保存</button>
    </form>

    <!-- 眼睛效果 -->
    <div class="eyes">
          <!-- 左眼 -->
          <div class="eye">
            <!-- 眼珠 -->
            <div class="ball"></div>
          </div>

          <!-- 右眼 -->
          <div class="eye">
            <!-- 眼珠 -->
            <div class="ball"></div>
          </div>
        </div>

    <!-- JavaScript代码 -->
    <script type="text/javascript">
      // 获取所有眼珠元素
      var balls = document.getElementsByClassName("ball");
      // 监听鼠标移动事件
      document.onmousemove = function(){
        // 获取鼠标在窗口中的位置
        var x = event.clientX * 100 / window.innerWidth + "%";
        var y = event.clientY * 100 / window.innerHeight + "%";

        // 遍历所有眼珠元素
        for(var i=0;i<2;i++){
          // 设置眼珠位置
          balls[i].style.left = x;
          balls[i].style.top = y;
          // 设置眼珠的transform属性,使其跟随鼠标移动
          balls[i].style.transform = "translate(-" + x +",-" + y +")";
        }
      }
    </script>
</body>
</html>
singer_edit.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <!-- 设置字符编码为UTF-8 -->
    <meta charset="UTF-8">
    <!-- 设置网页标题 -->
    <title>排行修改</title>
    <!-- 引入CSS样式表 -->
    <link rel="stylesheet" href="/static/css/ranking_add.css">
    <!-- 引入layer样式表 -->
    <link rel="stylesheet" href="/static/js/layer/layer/theme/default/layer.css">
    <!-- 引入jQuery库 -->
    <script src="/static/js/jquery-3.0.0.js"></script>
    <!-- 引入layer库 -->
    <script src="/static/js/layer/layer/layer.js"></script>
    <!-- 引入singer.js库 -->
    <script src="/static/js/singer.js"></script>
</head>
<body>
     <!-- 表单 -->
     <form action="" id="editForm">
         <!-- 隐藏的id输入框 -->
         <input type="hidden" name="id" value="{
  
  { data["id"] }}">
        <!-- 歌手修改标题 -->
        <h2>歌手修改</h2>
        <!-- 歌手名称输入框 -->
        <div>
            <label for="singer">歌手名称:</label>
            <input type="text" name="singer" id="edit_singer" value="{
  
  { data["singer"] }}">
        </div>
         <!-- 歌手id输入框 -->
         <div>
            <label for="id">歌手id:</label>
            <input type="text" name="id" id="edit_id" value="{
  
  { data["id"] }}">
        </div>
         <!-- 歌手图片输入框 -->
         <div>
            <label for="singerimg">歌手图片:</label>
             <!-- 歌手图片 -->
             <img src="/static/images/260.jpg" alt="" id="img">
            <!-- 图片上传 -->
            <input type="file" name="singerimg" id="img">
        </div>
        <!-- 保存按钮 -->
        <button type="button" id="btn_edit">保存</button>
    </form>

     <!-- 眼睛 -->
     <div class="eyes">
      <!-- 左眼 -->
      <div class="eye">
        <!-- 眼珠 -->
        <div class="ball"></div>
      </div>

      <!-- 右眼 -->
      <div class="eye">
        <!-- 眼珠 -->
        <div class="ball"></div>
      </div>
    </div>

<!-- JavaScript代码 -->
<script type="text/javascript">
  // 获取所有眼珠元素
  var balls = document.getElementsByClassName("ball");
  // 监听鼠标移动事件
  document.onmousemove = function(){
    // 获取鼠标在窗口中的位置
    var x = event.clientX * 100 / window.innerWidth + "%";
    var y = event.clientY * 100 / window.innerHeight + "%";

    // 遍历所有眼珠元素
    for(var i=0;i<2;i++){
      // 设置眼珠位置
      balls[i].style.left = x;
      balls[i].style.top = y;
      // 设置眼珠的偏移量
      balls[i].style.transform = "translate(-" + x +",-" + y +")";
    }
  }
</script>
</body>
</html>
singer.js
$(function (){
    basics_bth();// 基础按钮单击事件
    add_form_submit();// 新增表单提交事件
    edit_form_submit();//修改
    // luoma_time();//罗马时间背景
});

// 基础按钮单击事件
function basics_bth(){
	$("#next").on("click",function (){
    var page=parseInt($("#page").val())
    var total=parseInt($("#total").val())
    if (page<total){
        $("#page").val(page+1);
        $("#bth_page").click()
    }
})
    $("#last").on("click",function (){
        var page=parseInt($("#page").val())
        var total=parseInt($("#total").val())
        if (page>1){
            $("#page").val(page-1);
            $("#bth_page").click()
        }
    })

    //条数选择
    $("#rows").on("change",function (){
        $("#page").val(1);
        $("#bth_page").click()
    })
    //新增按钮单击事件
    $(".bth_add").on("click",function (){
        //打开新增界面
        window.parent.frames["mainFrame"].location.href="/singer/singer_add";
    })
	 //图片预览
    $("#img").on("change",function (){
        $("#img").attr("src",window.URL.createObjectURL($(this)[0].files[0]))
    })
    // $("#img").on("change",function (){
    //     $("#title_photo").css({"width":"70px"}).attr("src",window.URL.createObjectURL($(this)[0].files[0]))
    // })

    //新增按钮单击事件
    $(".bth_add").on("click",function (){
        //打开新增界面
        window.parent.frames["mainFrame"].location.href="/singer/singer_add";
    })

    //清空按钮单击事件
    $("#btn_res").on("click",function (){
        $("#singerimg").attr("src","/static/images/260.jpg")
    })

    //删除/修改按钮单击事件
    $(".bth p").each(function (e,i){
        $(i).on("click",function (){
            if($(this).text()=="修改"){
                var id = $(this).attr("data-id")
                window.parent.frames["mainFrame"].location.href="/singer/singer_edit/"+id;
            }
            else {
                var singer=$(this).attr("data-singer");
                var id = $(this).attr("data-id")
                layer.confirm("是否确认删除歌手名称为"+singer+"的数据?",{
                    btn:["确认","取消"]
                },function (){
                    // 使用ajax提交表单数据
                $.ajax({
                    url:"/singer/del",//提交的路径
                    type:"post",//提交的方法
                    data:{
                        "id":id
                    },//提交的数据
                    dataType:"json",//接收的数据类型
                    success:function (result){// result:自定义的接受返回数据的变量名
                        console.log(result)
                        if(result.success){
                            window.parent.frames["mainFrame"].location.reload();
                        }
                        else {
                            layer.msg(result.msg,{icon: 2});
                        }
                    },
                    error:function (){
                        alert("内部错误,请联系管理员")
                    }

                })
                })



            }
        })
    })
}

//新增表单提交事件
function add_form_submit(){
    $("#btn_add").on("click",function (){
       if($("#singer").val()=="") {
           layer.msg("请把信息填写完整",{icon:2});
           return null;
       }
       else {
            // 使用ajax提交表单数据
            $.ajax({
                url:"/singer/add",//提交的路径
                type:"post",//提交的方法
                data:new FormData($("#addForm")[0]),//提交的数据
                cache:false,
                processData: false,
                contentType:false,
                dataType:"json",//接收的数据类型
                success:function (result){// result:自定义的接受返回数据的变量名
                    if(result.success){
                        layer.msg(result.msg,{icon: 1});
                        $("#singer").val("");
                    }
                    else {
                        layer.msg(result.msg,{icon: 2});
                    }
                },
                error:function (){
                    alert("内部错误,请联系管理员")
                }

            })
       }
    })
}

//修改
function edit_form_submit(){
    $("#btn_edit").on("click",function (){
       if($("#edit_singer").val()==""||$("#edit_id").val()=="") {
           layer.msg("请把信息填写完整",{icon:2});
           return null;
       }
       else {

            // 使用ajax提交表单数据
            $.ajax({
                url:"/singer/edit",//提交的路径
                type:"post",//提交的方法
                data:new FormData($("#editForm")[0]),//提交的数据
                cache:false,
                processData: false,
                contentType:false,
                dataType:"json",//接收的数据类型
                success:function (result){// result:自定义的接受返回数据的变量名
                    if(result.success){
                        layer.msg(result.msg,{icon: 1});
                    }
                    else {
                        layer.msg(result.msg,{icon: 2});
                    }
                },
                error:function (){
                    alert("内部错误,请联系管理员")
                }

            })
       }
    })
}




//罗马时间背景
function luoma_time(){
    var monthText = ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"];
			var dayText = ["一号", "二号", "三号", "四号", "五号", "六号", "七号", "八号", "九号", "十号", "十一号", "十二号", "十三号", "十四号", "十五号", "十六号",
				"十七号", "十八号", "十九号", "二十号", "二十一号", "二十二号", "二十三号", "二十四号", "二十五号", "二十六号", "二十七号", "二十八号", "二十九号", "三十号", "三十一号"
			];
			var weekText = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];
			var hourText = ["零点", "一点", "两点", "三点", "四点", "五点", "六点", "七点", "八点", "九点", "十点", "十一点", "十二点", "十三点", "十四点", "十五点",
				"十六点", "十七点", "十八点", "十九点", "二十点", "二十一点", "二十二点", "二十三点"
			];
			var minuteText = ["一分", "二分", "三分", "四分", "五分", "六分", "七分", "八分", "九分", "十分",
				"十一分", "十二分", "十三分", "十四分", "十五分", "十六分", "十七分", "十八分", "十九分", "二十分",
				"二十一分", "二十二分", "二十三分", "二十四分", "二十五分", "二十六分", "二十七分", "二十八分", "二十九分", "三十分",
				"三十一分", "三十二分", "三十三分", "三十四分", "三十五分", "三十六分", "三十七分", "三十八分", "三十九分", "四十分",
				"四十一分", "四十二分", "四十三分", "四十四分", "四十五分", "四十六分", "四十七分", "四十八分", "四十九分", "五十分",
				"五十一分", "五十二分", "五十三分", "五十四分", "五十五分", "五十六分", "五十七分", "五十八分", "五十九分", "六十分"
			];
			var secondsText = ["一秒", "二秒", "三秒", "四秒", "五秒", "六秒", "七秒", "八秒", "九秒", "十秒",
				"十一秒", "十二秒", "十三秒", "十四秒", "十五秒", "十六秒", "十七秒", "十八秒", "十九秒", "二十秒",
				"二十一秒", "二十二秒", "二十三秒", "二十四秒", "二十五秒", "二十六秒", "二十七秒", "二十八秒", "二十九秒", "三十秒",
				"三十一秒", "三十二秒", "三十三秒", "三十四秒", "三十五秒", "三十六秒", "三十七秒", "三十八秒", "三十九秒", "四十秒",
				"四十一秒", "四十二秒", "四十三秒", "四十四秒", "四十五秒", "四十六秒", "四十七秒", "四十八秒", "四十九秒", "五十秒",
				"五十一秒", "五十二秒", "五十三秒", "五十四秒", "五十五秒", "五十六秒", "五十七秒", "五十八秒", "五十九秒", "六十秒"
			];

			var clock;
			// 存放dom元素的数组
			var monthList = [];
			var dayList = [];
			var weekList = [];
			var hourList = [];
			var minuteList = [];
			var secondsList = [];

			// 当前展示是否为圆形
			var isCircle = false;

			//二维数组 存放文字内容及页面显示标签
			var textSet = [
				[monthText, monthList],
				[dayText, dayList],
				[weekText, weekList],
				[hourText, hourList],
				[minuteText, minuteList],
				[secondsText, secondsList]
			];

			window.onload = function() {
				init();
				// 每隔100ms获得 当前时间 更新页面时间显示
				setInterval(function() {
					runTime();
				}, 100);

				// 在变成圆形之前先进性修改定位
				changePosition();
				// 延迟2000ms变成圆形
				setTimeout(function() {
					changeCircle();
				}, 1000);
			}
			// 初始化函数
			function init() {
				clock = document.getElementById('clock');
				// 生成标签 存放文字展示
				for (var i = 0; i < textSet.length; i++) {
					for (var j = 0; j < textSet[i][0].length; j++) {
						var temp = createLabel(textSet[i][0][j]);
						clock.appendChild(temp);
						// 将生成的标签存放在数组list中
						textSet[i][1].push(temp);
					}
				}

			}

			// 创建标签并将文字填充标签内 接收参数为文字内容
			function createLabel(text) {
				var div = document.createElement('div');
				div.classList.add('label');
				div.innerText = text;
				return div;
			}

			function runTime() {
				// 利用时间对象获得 当前 时间
				var now = new Date();
				// 获得月 日期 小时 分钟 秒钟
				var month = now.getMonth();
				var day = now.getDate();
				var week = now.getDay();
				var hour = now.getHours();
				var minute = now.getMinutes() - 1;
				var seconds = now.getSeconds();

				// 初始化时间颜色 同时将走过时间设置为灰色
				initStyle();

				// 设置当前时间为白色
				// 将当前时间月份存放在数组中
				var nowValue = [month, day - 1, week, hour, minute, seconds];
				for (var i = 0; i < nowValue.length; i++) {
					var num = nowValue[i];
					textSet[i][1][num].style.color = '#0c0c0c';
				}

				// 变成圆形
				if (isCircle) {
					// 确定圆心
					var widthMid = document.body.clientWidth / 2;
					var heightMid = document.body.clientHeight / 2;
					// 将每一个dom元素确定到圆的位置
					for (var i = 0; i < textSet.length; i++) {
						for (var j = 0; j < textSet[i][0].length; j++) {
							// 加算出每一个元素的位置  x y 坐标
							// 每一个圆的半径与时分秒的位置有关
							var r = (i + 1) * 35 + 50 * i;
							// 计算每一个平均的角度  再将每一个单位对齐 然后转化成弧度
							var deg = 360 / textSet[i][1].length * (j - nowValue[i]);
							// 计算出每一个dom元素的坐标
							var x = r * Math.sin(deg * Math.PI / 180) + widthMid;
							var y = heightMid - r * Math.cos(deg * Math.PI / 180);
							// 设置样式
							var temp = textSet[i][1][j];
							temp.style.transform = 'rotate(' + (-90 + deg) + 'deg)';
							temp.style.left = x + 'px';
							temp.style.top = y + 'px';
						}
					}
				}
			}

			function initStyle() {
				// 将所有标签置为灰色
				// 利用取出dom元素
				var label = document.getElementsByClassName('label');
				for (var i = 0; i < label.length; i++) {
					label[i].style.color = '#834a4a';
				}
				// 利用二维数组存放dom元素的数组
				// for (var i = 0 ; i < textSet.length ; i ++) {
				//     for (var j = 0 ; j < textSet[i][0].length ; j ++) {
				//         textSet[i][1][j].style.color = "#4d4d4d";
				//     }
				// }
			}

			// 修改position
			function changePosition() {
				for (let i = 0; i < textSet.length; i++) {
					for (let j = 0; j < textSet[i][1].length; j++) {
						// 先获得原来的位置  再修改position 设置left top
						let tempX = textSet[i][1][j].offsetLeft + "px";
						let tempY = textSet[i][1][j].offsetTop + "px";
						// console.log(textSet[i][1][j]);
						// 利用let 防止闭包
						setTimeout(function() {
							textSet[i][1][j].style.position = "absolute";
							textSet[i][1][j].style.left = tempX;
							textSet[i][1][j].style.top = tempY;
						}, 50);
					}
				}
			}

			function changeCircle() {
				isCircle = true;
				clock.style.transform = "rotate(90deg)";
			}
}



7.歌曲管理页面内容

singing.py
import os
import re

from flask import Blueprint, render_template, session, jsonify, request

from dbcomn import MysqlUtil

# 注册蓝图
singing = Blueprint("singing", __name__, url_prefix="/singing", template_folder="templates")


@singing.route("/singing_list")
def singing_list(song="", page=1, rows=5):
    sql = """SELECT a.*,b.`id` as id FROM singinginfo as a 
JOIN singerinfo as b 
ON a.singerid=b.id"""
    sql2 = """SELECT COUNT(*) as count FROM singinginfo as a 
JOIN singerinfo as b 
ON a.singerid=b.id"""
    if song != "":
        sql += f" WHERE song LIKE '%{song}%'"
    # sql += f"ORDER BY id DESC LIMIT {(page - 1) * rows}, {rows}"
    mysqlutil = MysqlUtil()
    lis = mysqlutil.get_list(sql)
    # print(lis)
    mysqlutil2 = MysqlUtil()
    lis2 = mysqlutil2.get_one(sql2)
    count = lis2["count"]
    if count % rows == 0:
        total = count // rows
    else:
        total = count // rows + 1
    rows_lis = [5, 10, 20, 30, 40, 50]
    data = {
        "lis": lis,
        "song": song,
        "page": page,
        "total": total,
        "rows_lis": rows_lis,
        "rows": rows
    }
    return render_template("singing.html", data=data)



#翻页
@singing.route("/page_data", methods=["get"])
def page_data():
    page = request.args.get("page")
    rows = request.args.get("rows")
    return singing_list(page=int(page), rows=int(rows))


# 根据提交查询
@singing.route("/find_singing", methods=["get"])
def find_singing():
    song = request.args.get("song")
    return singing_list(song=song)



# 新增界面
@singing.route("/singing_add")
def singing_add():
    id_lis=find_id()
    rank_lis=find_rank()
    data={
        "id_lis":id_lis,
        "rank_lis":rank_lis
    }
    return render_template("singing_add.html", data=data)


#查询歌手
@singing.route("/find_id")
def find_id():
    sql = "SELECT * FROM singerinfo"
    mysqlutil = MysqlUtil()
    lis = mysqlutil.get_list(sql)
    return lis

#查询榜单
@singing.route("/find_rank")
def find_rank():
    sql = "SELECT * FROM rankinfo"
    mysqlutil = MysqlUtil()
    lis = mysqlutil.get_list(sql)
    return lis

# 新增
@singing.route("/add", methods=["post"])
def add():
    if request.method == "POST":
        song = request.form["song"]
        id = request.form["singerid"]
        rankid = request.form["rankid"]
        album = request.form["album"]
        singing=request.files["singing"]
        img=request.files["img"]
        intro=request.form["intro"]
        sql = f"SELECT * FROM singinginfo WHERE `song`='{song}'"
        mysqlutil = MysqlUtil()
        row = mysqlutil.get_one(sql)
        if row is None:
            singing_path = os.path.join(os.getcwd() + "/static/singing", singing.filename)  # 设置歌曲文件的保存路径
            singing.save(singing_path)  # 保存歌曲文件
            singing_path = "/static/singing/" + singing.filename

            singing_img_path = os.path.join(os.getcwd() + "/static/images", img.filename)
            img.save(singing_img_path)
            singing_img_path = "/static/images/" + img.filename
            sql3 = f"INSERT INTO singinginfo(`song`,`singerid`,`album`,`singing`,`img`,`intro`,`rankid`) "
            sql3 += f"VALUES('{song}','{id}','{album}','{singing_path}','{singing_img_path}','{intro}','{rankid}')"
            mysqlutil2 = MysqlUtil()
            i = mysqlutil2.adddeledit(sql3)
            if i > 0:
                return jsonify({"success": True, "msg": "保存成功!"})
            else:
                return jsonify({"success": False, "msg": "保存失败!"})
        else:
            return jsonify({"success": False, "msg": "该类别已存在!"})



#修改

@singing.route("/singing_edit/<id>")
def singing_edit(id):
    sql = f"SELECT * FROM singinginfo WHERE id={id}"
    mysqlutil = MysqlUtil()
    id_lis=find_id()
    rank_lis=find_rank()
    row = mysqlutil.get_one(sql)
    # print(row)
    data= {
        "singing_data": row,
        "id_lis":id_lis,
        "rank_lis":rank_lis
    }
    return render_template("singing_edit.html", data=data)

#修改
@singing.route("/edit", methods=["post"])
def edit():
    # 判断提交方式是否为post提交
    if request.method == "POST":
        id = request.form["id"]
        print(id)
        rankid=request.form["rankid"]
        print(rankid)
        singerid = request.form["singerid"]
        print(singerid)
        song = request.form["song"]
        print(song)
        album = request.form["album"]
        print(album)
        singing = request.files["singing"]
        print(singing)
        img = request.files["img"]
        print(img)
        intro = request.form["intro"]
        print(intro)
        sql = f"SELECT * FROM singinginfo WHERE `song`='{song}'"
        if singing.filename != "":
           singing_path = os.path.join(os.getcwd() + "/static/singing", singing.filename)
           singing.save(singing_path)
           singing_path = "/static/singing/" + singing.filename
           if img.filename != "":
               img_temp_path = os.path.join(os.getcwd() + "/static/images", img.filename)
               img.save(img_temp_path)
               img_temp_path = "/static/images/" + img.filename
               sql2 = f"UPDATE singinginfo SET `song`='{song}',`album`='{album}',`singerid`='{singerid}',`singing`='{singing_path}',`img`='{img_temp_path}',`intro`='{intro}',`rankid`='{rankid}' WHERE id='{id}'"
           else:
               sql2 = f"UPDATE singinginfo SET `song`='{song}',`album`='{album}',`singerid`='{singerid}',`singing`='{singing_path}',`intro`='{intro}',`rankid`='{rankid}' WHERE id='{id}'"
        else:
            sql2=f"UPDATE singinginfo SET `song`='{song}',`album`='{album}',`singerid`='{singerid}',`intro`='{intro}',`rankid`='{rankid}' WHERE id='{id}'"
        mysqlutil = MysqlUtil()
        row = mysqlutil.get_one(sql)
        mysqlutil2 = MysqlUtil()
        i = mysqlutil2.adddeledit(sql2)
        if i > 0:
            return jsonify({"success": True, "msg": "修改成功!"})
        else:
            return jsonify({"success": False, "msg": "未修改信息!"})



# 删除
@singing.route("/del", methods=["post"])
def delete():
    if request.method == "POST":
        id = request.form["id"]
        sql2= f"DELETE FROM singinginfo WHERE `id`='{id}'"
        mysqlutil2 = MysqlUtil()
        i = mysqlutil2.adddeledit(sql2)
        if i > 0:
            return jsonify({"success": True, "msg": "删除成功!"})
        else:
            return jsonify({"success": False, "msg": "删除失败!"})
    else:
            return jsonify({"success": False, "msg": "该类别无法删除!"})
singing.css
body {
    font-family: Arial, sans-serif;
    background-color: #f2f2f2;
    margin: 0;
    padding: 0;
}

table {
    width: 80%;
    margin: 20px auto;
    border-collapse: collapse;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    background-color: #fff;
}

table thead tr {
    background-color: #4CAF50;
    color: white;
}

table tbody tr {
    background-color: #f2f2f2;
}

table tbody tr:nth-child(even) {
    background-color: #ddd;
}

table td, table th {
    padding: 8px;
    text-align: left;
}

table td img {
    width: 100px;
    height: 100px;
    border-radius: 50%;
}

form {
    width: 80%;
    margin: 20px auto;
}

form input[type="text"] {
    width: 70%;
    padding: 8px;
    margin-right: 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
}

form input[type="submit"] {
    padding: 8px 16px;
    background-color: #4CAF50;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

form select {
    padding: 8px;
    margin-right: 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
}

form input[type="text"]:focus, form select:focus {
    outline: none;
    border: 1px solid #4CAF50;
}

button {
    padding: 8px 16px;
    background-color: #4CAF50;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

button:hover {
    background-color: #45a049;
}

button.bth_add {
    margin-bottom: 20px;
}

button.bth_add:hover {
    background-color: #45a049;
}

button.bth {
    padding: 0;
    background-color: transparent;
    border: none;
    cursor: pointer;
}

button.bth p {
    display: inline-block;
    padding: 8px 16px;
    background-color: #4CAF50;
    color: white;
    margin-right: 10px;
    border-radius: 4px;
}

button.bth p:hover {
    background-color: #45a049;
}

button.bth p:last-child {
    margin-right: 0;
}
singing_add.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>歌曲新增</title>
    <!-- 引入样式表 -->
    <link rel="stylesheet" href="/static/css/singing_add.css">
    <link rel="stylesheet" href="/static/js/layer/layer/theme/default/layer.css">
    <!-- 引入js文件 -->
    <script src="/static/js/jquery-3.0.0.js"></script>
    <script src="/static/js/layer/layer/layer.js"></script>
    <script src="/static/js/singing.js"></script>
</head>
<body>
     <!-- 表单 -->
     <form action="" id="addForm">
        <h2>歌曲新增</h2>
        <div>
            <label for="song">歌曲名称:</label>
            <input type="text" name="song" id="song">
        </div>
         <div>
            <label for="album">专辑:</label>
            <input type="text" name="album" id="album">
        </div>
         <div>
            <label for="singing">歌曲:</label>
            <input type="file" name="singing" id="singing">
        </div>
         <div>
            <label for="img">歌曲封面:</label>
            <input type="file" name="img" id="img">
        </div>
         <div>
           <label for="singerid">歌手:</label>
            <select name="singerid" id="singerid">
                <option value="">===请选择歌手id===</option>
                <!-- 循环输出歌手列表 -->
                {% for item in data.id_lis %}
                <option value="{
  
  { item["id"] }}">{
  
  { item["singer"] }}</option>
                {% endfor %}
            </select>
        </div>
        <div>
           <label for="rankid">排行榜:</label>
            <select name="rankid" id="rankid">
                <option value="">===请选择排行榜===</option>
                <!-- 循环输出排行榜列表 -->
                {% for item in data.rank_lis %}
                <option value="{
  
  { item["id"] }}">{
  
  { item["name"] }}</option>
                {% endfor %}
            </select>
        </div>
         <div>
            <label for="intro">歌词:</label>
            <input type="text" name="intro" id="intro">
        </div>
        <div>
        <!-- 保存按钮 -->
        <button type="button" id="btn_add">保存</button>
        <!-- 清空按钮 -->
        <button type="reset" id="btn_res">清空</button>
        </div>
    </form>


</body>
</html>
singing.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>歌曲管理</title>
    <!-- 引入CSS样式表 -->
    <link rel="stylesheet" href="/static/css/singing.css">
    <link rel="stylesheet" href="/static/js/layer/layer/theme/default/layer.css">
    <!-- 引入JS文件 -->
    <script src="/static/js/jquery-3.0.0.js"></script>
    <script src="/static/js/layer/layer/layer.js"></script>
    <script src="/static/js/singing.js"></script>
</head>

<body>

    <!-- 搜索表单 -->
    <form action="/singing/find_singing" method="get">
        <input type="text" name="song" value="{
  
  { data.song }}" placeholder="请输入歌曲名称">
        <input type="submit" value="搜索">
    </form>
        <!-- 新增按钮 -->
        <button type="button" class="bth_add">新增</button>
    <!-- 时钟 -->
    <div id="clock" style="position: fixed; z-index: -1;"></div>
    <!-- 表格 -->
    <table>
        <thead>
            <tr>
                <td>歌曲信息</td>
                <td>专辑</td>
                <td>歌曲链接</td>
                <td>歌曲封面</td>
                <td>歌手</td>
                <td>排行榜id</td>
                <td>歌词</td>
                <td>操作</td>
            </tr>
        </thead>

        <tbody>
            <!-- 循环输出歌曲信息 -->
            {% for item in data.lis %}
                <tr>
                    <td>{
  
  { item["song"] }}</td>
                    <td>{
  
  { item["album"] }}</td>
                    <td>
                       <!-- 歌曲链接 -->
                       <audio controls>
                          <source src="{
  
  { item["singing"] }}"  id="singing">
                          Your browser does not support the audio element.
                       </audio>
                    <td>
                        <!-- 歌曲封面 -->
                        <img src="{
  
  { item["img"] }}" alt="" id="img">
                    </td>
                    <td>{
  
  { item["singerid"] }}</td>
                    <td>{
  
  { item["rankid"] }}</td>
                    <td style="font-size: 12px"><h6>{
  
  { item["intro"] }}</h6></td>
                    <td class="bth">
                        <!-- 修改按钮 -->
                        <p data-id="{
  
  { item["id"] }}">修改</p>
                        <!-- 删除按钮 -->
                        <p data-id="{
  
  { item["id"] }}" data-song="{
  
  { item["song"] }}">删除</p>
                    </td>
                </tr>
            {% endfor %}
        </tbody>
    </table>



{#    <!-- 分页表单 -->#}
{#    <form action="/singing/page_data" id="pageForm" method="get">#}
{#        <!-- 隐藏的页码和总页数 -->#}
{#        <input type="hidden" value="{
  
  { data.page }}" name="page" id="page">#}
{#        <input type="hidden" value="{
  
  { data.total }}"id="total">#}
{#        <input type="text" value="下一页" name="next" id="next" readonly="readonly">#}
{#        <p>当前第{
  
  { data.page }}页,共{
  
  { data.total }}页</p>#}
{#        <input type="text" value="上一页" name="last" id="next" readonly="readonly">#}
{#        <select name="rows" id="rows">#}
{#            {% for item in data.rows_lis  %}#}
{#                {% if data.rows==item %}#}
{#                    <option value="{
  
  { item }}" selected>{
  
  { item }}</option>#}
{#                {% else %}#}
{#                    <option value="{
  
  { item }}">{
  
  { item }}</option>#}
{#            	{% endif %}#}
{#            {% endfor %}#}
{##}
{#        </select>#}
{#        <input type="submit" id="bth_page" value="提交">#}
{#    </form>#}



</body>
</html>
singing_edit.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>歌曲新增</title>
    <!-- 引入歌曲新增的样式表 -->
    <link rel="stylesheet" href="/static/css/singing_add.css">
    <!-- 引入layer插件的样式表 -->
    <link rel="stylesheet" href="/static/js/layer/layer/theme/default/layer.css">
    <!-- 引入jQuery库 -->
    <script src="/static/js/jquery-3.0.0.js"></script>
    <!-- 引入layer插件 -->
    <script src="/static/js/layer/layer/layer.js"></script>
    <!-- 引入歌曲新增的js文件 -->
    <script src="/static/js/singing.js"></script>
</head>
<body>
     <!-- 表单 -->
     <form action="" id="editForm">
        <h2>歌曲修改</h2>
        <!-- 歌曲名称 -->
        <div>
            <label for="song">歌曲名称:</label>
            <input type="text" name="song" id="song" value="{
  
  { data.singing_data["song"] }}">
        </div>
         <!-- 专辑 -->
         <div>
            <label for="album">专辑:</label>
            <input type="text" name="album" id="album" value="{
  
  { data.singing_data["album"] }}">
        </div>
         <!-- 歌曲文件 -->
         <div>
            <label for="singing">歌曲:</label>
            <input type="file" name="singing" id="singing" value="{
  
  { data.singing_data["singing"] }}">
        </div>
         <!-- 歌曲封面 -->
         <div>
            <label for="img">歌曲封面:</label>
            <input type="file" name="img" id="img" value="{
  
  { data.singing_data["img"] }}">
        </div>
         <!-- 歌手 -->
         <div>
            <label for="singerid">歌手:</label>
            <select name="singerid" id="singerid">
                <option value="">===请选择歌手id===</option>
                {% for item in data.id_lis %}
                <option value="{
  
  { item["id"] }}">{
  
  { item["singer"] }}</option>
                {% endfor %}
            </select>
        </div>
        <!-- 排行榜 -->
        <div>
           <label for="rankid">排行榜:</label>
            <select name="rankid" id="rankid">
                <option value="">===请选择排行榜===</option>
                {% for item in data.rank_lis %}
                <option value="{
  
  { item["id"] }}">{
  
  { item["name"] }}</option>
                {% endfor %}
            </select>
        </div>
         <!-- 歌词 -->
         <div>
            <label for="intro">歌词:</label>
            <input type="text" name="intro" id="intro" value="{
  
  { data.singing_data["intro"] }}">
        </div>
        <!-- 保存按钮 -->
        <div>
        <button type="button" id="btn_edit">保存</button>
        </div>
    </form>


</body>
</html>
singing_add.css
/* 全局样式 */
body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 0;
    background-color: #f4f4f4;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
}

/* 表单样式 */
form {
    background-color: #fff;
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    width: 400px;
}

h2 {
    text-align: center;
    margin-bottom: 20px;
}

div {
    margin-bottom: 10px;
}

label {
    display: block;
    margin-bottom: 5px;
}

input[type="text"],
select {
    width: 100%;
    padding: 10px;
    border-radius: 5px;
    border: 1px solid #ccc;
}

input[type="file"] {
    width: 100%;
    padding: 10px;
    border-radius: 5px;
    border: 1px solid #ccc;
}

button {
    width: 100%;
    padding: 10px;
    border-radius: 5px;
    border: none;
    background-color: #4CAF50;
    color: white;
    cursor: pointer;
    margin-top: 10px;
}

button:hover {
    background-color: #45a049;
}
singing.js
$(function (){
    basics_bth();// 基础按钮单击事件
    add_form_submit();// 新增表单提交事件
    edit_form_submit();//修改
});

// 基础按钮单击事件
function basics_bth(){
    //下一页
    $("#next").on("click",function (){
        var page=parseInt($("#page").val())
        var total=parseInt($("#total").val())
        if (page<total){
            $("#page").val(page+1);
            $("#bth_page").click()
        }
    })
    //上一页
    $("#last").on("click",function (){
        var page=parseInt($("#page").val())
        var total=parseInt($("#total").val())
        if (page>1){
            $("#page").val(page-1);
            $("#bth_page").click()
        }
    })

    //条数选择
    $("#rows").on("change",function (){
        $("#page").val(1);
        $("#bth_page").click()
    })

    //图片预览
    $("#img").on("change",function (){
        $("#img").attr("src",window.URL.createObjectURL($(this)[0].files[0]))
    })
    // $("#img").on("change",function (){
    //     $("#title_photo").css({"width":"70px"}).attr("src",window.URL.createObjectURL($(this)[0].files[0]))
    // })

    //新增按钮单击事件
    $(".bth_add").on("click",function (){
        //打开新增界面
        window.parent.frames["mainFrame"].location.href="/singing/singing_add";
    })

    //清空按钮单击事件
    $("#btn_res").on("click",function (){
        $("#img").attr("src","/static/images/260.jpg")
    })

    //删除/修改按钮单击事件
    $(".bth p").each(function (e,i){
        $(i).on("click",function (){
            if($(this).text()=="修改"){
                var id = $(this).attr("data-id")
                window.parent.frames["mainFrame"].location.href="/singing/singing_edit/"+id;
            }
            else {
                var song=$(this).attr("data-song");
                var id = $(this).attr("data-id")
                layer.confirm("是否确认删除歌曲名称为"+song+"的数据?",{
                    btn:["确认","取消"]
                },function (){
                    // 使用ajax提交表单数据
                $.ajax({
                    url:"/singing/del",//提交的路径
                    type:"post",//提交的方法
                    data:{
                        "id":id
                    },//提交的数据
                    dataType:"json",//接收的数据类型
                    success:function (result){// result:自定义的接受返回数据的变量名
                        if(result.success){
                            window.parent.frames["mainFrame"].location.reload();
                        }
                        else {
                            layer.msg(result.msg,{icon: 2});
                        }
                    },
                    error:function (){
                        alert("内部错误,请联系管理员")
                    }

                })
                })



            }
        })
    })
}

//新增表单提交事件
function add_form_submit(){
    $("#btn_add").on("click",function (){
       if($("#song").val()==""||$("#album").val()==""||$("#singing").val()==""||$("#img").val()==""||$("#singerid").val()==""||$("#intro").val()=="") {
           layer.msg("请把信息填写完整",{icon:2});
           return null;
       }
       else {
            // 使用ajax提交表单数据
            $.ajax({
                url:"/singing/add",//提交的路径
                type:"post",//提交的方法
                data:new FormData($("#addForm")[0]),//提交的数据
                cache:false,
                processData: false,
                contentType:false,
                dataType:"json",//接收的数据类型
                success:function (result){// result:自定义的接受返回数据的变量名
                    if(result.success){
                        layer.msg(result.msg,{icon: 1});
                        $("#btn_res").click()
                    }
                    else {
                        layer.msg(result.msg,{icon: 2});
                    }
                },
                error:function (){
                    alert("内部错误,请联系管理员")
                }

            })
       }
    })
}

//修改
function edit_form_submit(){
    $("#btn_edit").on("click",function (){
       if($("#song").val()==""||$("#album").val()==""||$("#singing").val()==""||$("#img").val()==""||$("#intro").val()==""||$("#singerid").val()==""||$("#rankid").val()=="") {
           layer.msg("请把信息填写完整",{icon:2});
           return null;
       }
       else {

             // 使用ajax提交表单数据
            $.ajax({
                url:"/singing/edit",//提交的路径
                type:"post",//提交的方法
                data:new FormData($("#editForm")[0]),//提交的数据
                cache:false,
                processData: false,
                contentType:false,
                dataType:"json",//接收的数据类型
                success:function (result){// result:自定义的接受返回数据的变量名
                    if(result.success){
                        layer.msg(result.msg,{icon: 1});
                    }
                    else {
                        layer.msg(result.msg,{icon: 2});
                    }
                },
                error:function (){
                    alert("内部错误,请联系管理员")
                }

            })
       }
    })
}

//



后端服务器大致就这些内容了

五.客户端(前端内容)

至于前端我只实现了排行榜的内容,但是方法都是一样的,举一反三,就可以把其他内容做出来。

1.项目结构

2.项目实现

通过跨域,设置一个统一路径site.js里的URL在前端写路由时,在路由前加URL,让其可以跨域访问服务器内容

site.js
const URL="http://127.0.0.1:5000";

将要展示的页面写出来,把需要重服务器获取到的内容用字符串拼贴,再通过服务器端content.py进行方法书写,目录位置:\kuwo\userweb\content.py

content.py
import re

from flask import Blueprint, render_template, jsonify, request
from dbcomn import MysqlUtil


# 注册蓝图
content = Blueprint("content", __name__, url_prefix="/content")


#查询榜单
@content.route("/find_rank", methods=["post"])
def find_rank():
    if request.method == "POST":
        sql = "SELECT * FROM rankinfo"
        mysqlutil = MysqlUtil()
        lis = mysqlutil.get_list(sql)
        return jsonify({"data": lis})

#查询歌手
@content.route("/find_singer", methods=["post"])
def find_singer():
    if request.method == "POST":
        sql = "SELECT * FROM singerinfo"
        mysqlutil = MysqlUtil()
        lis = mysqlutil.get_list(sql)
        return jsonify({"data": lis})

#
# 查询作品
@content.route("/find_singing", methods=["post"])
def find_singing():
    if request.method == "POST":
        page =request.form["page"]
        rank_name= request.form["rank_name"]
        singer= request.form["singer"]
        sql = """SELECT a.*,b.`name` as rank_name ,c.`singer` as singer FROM singinginfo as a
JOIN rankinfo as b
ON a.rankid=b.id
JOIN singerinfo as c
ON a.singerid=c.id"""
        sql2 = """SELECT  COUNT(*) as count FROM singinginfo as a
JOIN rankinfo as b
ON a.rankid=b.id
JOIN singerinfo as c
ON a.singerid=c.id"""

        if rank_name != "":
            sql += f" WHERE  b.`name`='{rank_name}' "
            sql2 += f" WHERE  b.`name`='{rank_name}'"
        else:
            sql += f" WHERE b.`name`='{rank_name}'"
            sql2 += f" WHERE b.`name`='{rank_name}'"
        sql += f"ORDER BY a.id DESC LIMIT {(int(page) - 1) * 10},10"
        mysqlutil = MysqlUtil()
        lis = mysqlutil.get_list(sql)
        mysqlutil2 = MysqlUtil()
        row = mysqlutil2.get_one(sql2)
        if row["count"] % 10 == 0:
            total = row["count"] // 10
        else:
            total = row["count"] // 10 + 1
        return jsonify({"data": lis, "total": total})





# 查询目录
@content.route("/find_catalogue", methods=["post"])
def find_catalogue():
        if request.method == "POST":
            astr = request.form["astr"]
            sql = f"""SELECT a.*,b.`name` as rank_name ,c.`singer` as singer, c.singerimg as singerimg FROM singinginfo as a
JOIN rankinfo as b
ON a.rankid=b.id
JOIN singerinfo as c
ON a.singerid=c.id
WHERE a.id={astr}"""
            mysqlutil = MysqlUtil()
            lis = mysqlutil.get_list(sql)
            return jsonify({"data": lis})



@content.route("/get_song_detail", methods=["post"])
def get_song_detail():
    if request.method == "POST":
        songId = request.form["songId"]
        sql = """SELECT a.singing as url, a.img, CONCAT(a.song,'- ', c.singer) as songInfo, a.intro as intro
                 FROM singinginfo as a
                 JOIN singerinfo as c ON a.singerid = c.id
                 WHERE a.id = %s""" % songId
        mysqlutil = MysqlUtil()
        row = mysqlutil.get_one(sql)
        if row:
            return jsonify({"url": row["url"], "img": row["img"], "songInfo": row["songInfo"], "intro": row["intro"]})
        else:
            return jsonify({})





# #get_singer_songs
# @content.route("/get_singer_songs", methods=["post"])
# def get_singer_songs():
#     if request.method == "POST":
#         singerId = request.form["singerId"]
#         sql = """SELECT a.singing as url, a.img, CONCAT(a.song,'- ', c.singer) as songInfo, a.intro as intro
#                  FROM singinginfo as a
#                  JOIN singerinfo as c ON a.singerid = c.id
#                  WHERE a.singerid = %s"""
#         params = (singerId,)
#         mysqlutil = MysqlUtil()
#         lis = mysqlutil.get_list(sql, params)
#         return jsonify({"data": lis})
#
#
# #content/get_singer_detail
# @content.route("/get_singer_detail", methods=["post"])
# def get_singer_detail():
#     if request.method == "POST":
#         singerId = request.form["singerId"]
#         try:
#             sql = """SELECT a.singing as url, a.img, CONCAT(a.song,'- ', c.singer) as songInfo, a.intro as intro
#                      FROM singinginfo as a
#                      JOIN singerinfo as c ON a.singerid = c.id
#                      WHERE a.singerid = %s"""
#             params = (singerId,)
#             mysqlutil = MysqlUtil()
#             lis = mysqlutil.get_list(sql, params)
#             return jsonify({"data": lis})
#         except Exception as e:
#             print(f"数据库查询错误: {str(e)}")
#             return jsonify({"error": "数据库查询出错,请稍后重试"}), 500




需要展示内容的页面

index.html
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
		<!-- 引入字体图标 -->
		<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" rel="stylesheet"/>
		<!-- 引入样式表 -->
		<link rel="stylesheet" href="css/shouye.css" />
		<!-- 引入jQuery库 -->
		<script src="js/jquery-3.0.0.js"></script>
		<!-- 引入自定义脚本 -->
		<script src="js/site.js"></script>
		<script src="js/kugou.js"></script>
	</head> 
	<body>
		<!-- 导航栏 -->
		<nav class="top-nav">
		   <!-- logo -->
		   <div class="logo">
			<a href=""><img alt="NetEase Cloud Music logo - red cloud-shaped icon with music note" height="30" src="img/微信图片_20241129160757.jpg" width="30"/></a>
		    <span style="color: white; font-size: 18px;">音乐网</span>
		   </div>
		   <!-- 导航链接 -->
		   <div class="nav-links">
				<a href="#">发现音乐</a>
				<a href="#">我的音乐</a><a href="#">关注</a>
				<a href="#">商城</a>
				<a href="#">音乐人</a>
				<a href="#">云推歌</a>
				<a href="https://freepiano.tiwb.com/cn/">下载键盘钢琴</a>
		   </div>
		   <!-- 搜索栏 -->
		   <div class="search-bar">
				<input placeholder="音乐/视频/电台/用户" type="text"/>
		   </div>
				<!-- 创作者中心 -->
				<a class="creator-center" href="#">创作者中心</a>
		   <div class="user-profile">
				<span class="notification"></span>
		   </div>
		  </nav>
		  <nav class="sub-nav">
			   <a href="#">推荐</a>
			   <a href="#">排行榜</a>
			   <a href="#">歌单</a>
			   <a href="#">主播电台</a><a href="#">歌手</a>
			   <a href="#">新碟上架</a>
		  </nav>
		    <div class="content">
				<div class="left">
					<dl></dl>
		        </div>  
				<div class="right">
						<h2 class="rank"></h2>
						<ul></ul>
						<div class="singing_show">
							<ul></ul>
							<div class="page">
								<p class="last">上一页</p>
								<p class="text">当前第1页共100页</p>
								<p class="next">下一页</p>
							</div>
						</div>
				</div>
			<div id="player-container" style="display: none; position: fixed; bottom: 0; left: 0; width: 100%; background-color: rgba(34, 34, 34, 0.9); z-index: 101; padding: 10px;">
			  <audio id="player-audio" controls></audio>
			  <img id="player-img" src="" alt="">
			  <div id="player-song-info"></div>
			  <div id="lyrics-container"></div>
			</div>
			</div>
	</body>
</html>

把内容在js中渲染出来并给播放按钮写出点击事件,给歌词按照时间戳给他,展示在底部div中

kugou.js
$(function(){
	load_rank();//加载排行榜
	rank_click();//排行榜点击事件
	page_click();//分页点击事件
	load_singing(page=1,rank_name="飙升榜",singer="");//加载歌曲
	load_music();//加载音乐
});
var page=1;
var rank_name="飙升榜";
var total=0;
singer="";
//加载排行榜
function load_rank(){
	$.ajax({
		url:URL+"/content/find_rank",
		type:"post",// 提交的方式
		    dataType:"json",// 接收的数据类型
		    success:function (result){ // result:自定义的接收返回数据的变量名
		       var json_data=result.data;
		       var str=""
		       for(var i=0;i<json_data.length;i++){
		       		str+="<dd>"+json_data[i].name+"</dd>";
		       }
		       $("div.left dl").empty();
		       $("div.left dl").append(str);
		       $(".rank").text(json_data[0].name)
		       rank_click();
		       
		    },
		    error:function (){
		        alert("内部错误,请联系管理员!")
		    }
		})
}
//排行榜点击事件
function rank_click(){
	$("div.left dl dd").each(function(e,i){
		$(i).on("click",function(){
			var text=$(this).text();
			$(this).css({"color":"#aa55ff","border-left":"4px solid #aaffff","font-weight":"600"})
				.siblings().css({"color":"#aaffff","border-left":"none","font-weight":"500"});
			$(".rank").text(text);
			rank_name=text;
			console.log(rank_name);
			load_singing(page,rank_name,singer);
		})
	})
}

//加载歌曲
function load_singing(page, rank_name,singer) {
    $.ajax({
        url:URL+"/content/find_singing", // 提交的路径
        type: "post", // 提交的方式
        data: {
            "rank_name": rank_name,
            "page": page,
            "singer": singer,
        },
        dataType: "json", // 接收的数据类型
        success: function (result) { // result:自定义的接收返回数据的变量名
			console.log(result); // 检查返回的数据结构
			console.log(rank_name);
			console.log(page);
			console.log(singer);
            var json_data = result.data;
            total = result.total;
            $("p.text").text("当前第" + page + "页共" + total + "页");
            var str = "<li>封面&nbsp;&nbsp;&nbsp;歌曲名&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;播放&nbsp;&nbsp;&nbsp;&nbsp;歌手&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;专辑&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;支持正版,付费歌曲请前往正规音乐平台</li>";
            for (var i = 0; i < json_data.length; i++) {
                str+="<li><a href='singer.html?astr="+json_data[i].id+"' class='singer_into' astr="+json_data[i].id+"'><img src='"+URL+json_data[i].img+"' alt='' class='img'></a><h4 class='song'>"+json_data[i].song+"</h4>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
				str+="<p><span class='playmusic_button' id='"+json_data[i].id+"'> <img src='"+URL+"/static/images/12.jpg'  </span>&nbsp;&nbsp;&nbsp;&nbsp;<span class='singer' id='"+json_data[i].singerid+"'>"+json_data[i].singer+"</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class='album'>"+json_data[i].album+"</span></p></li>";
            }
			
            $("div.right>div ul").empty();
            $("div.right>div ul").append(str);
        },
        error: function () {
            alert("内部错误,请联系管理员!");
        },
    });
	
}



//翻页
function page_click(){
	//下一页
	$("p.next").on("click",function(){
		if(page<total){
			page=page+1;
			load_singing(page,rank_name,singer);
		}
	})
	
	//上一页
	$("p.last").on("click",function(){
		if(page>1){
			page = page - 1;
			load_singing(page,rank_name,singer);
		}
	})
}





function load_music() {
    let playerContainer = document.getElementById('player-container');
    let playerAudio = document.getElementById('player-audio');
    let playerImg = document.getElementById('player-img');
    let playerSongInfo = document.getElementById('player-song-info');
    let lyricsContainer = document.getElementById('lyrics-container');

    // 先移除播放按钮的点击事件绑定,避免重复绑定
    $("span img").off("click").on("click", function () {
        var songId = $(this).parent().attr("id");
        $.ajax({
            url: URL + "/content/get_song_detail",
            type: "post",
            data: { songId: songId },
            dataType: "json",
            success: function (result) {
                if (result && result.url && result.img && result.songInfo && result.intro) {
                    playerContainer.style.display = "block";
                    playerAudio.src = URL + result.url;
                    playerImg.src = URL + result.img;
                    playerSongInfo.textContent = result.songInfo;
                    displayLyrics(result.intro, playerAudio, lyricsContainer);
                    playerAudio.play();
                } else {
                    alert("歌曲信息获取失败,请检查网络或稍后重试。");
                }
            },
            error: function () {
                alert("歌曲信息获取出错,请检查网络连接或联系客服。");
            }
        });
    });

    $(document).ready(function () {
        load_music();
    });
}












//获取歌词
function displayLyrics(intro, audio, lyricsContainer) {
    // 假设歌词格式为 [时间戳]歌词内容\n[时间戳]歌词内容\n...
    // 先按换行符分割歌词字符串
    let lines = intro.split('\n'); 
    let lyricsData = [];
    // 遍历每一行歌词,提取时间戳和歌词文本
    lines.forEach(line => {
        let match = line.match(/\[(.*?)\](.*)/); 
        if (match) {
            let time = match[1]; 
            let text = match[2]; 
            lyricsData.push({ time: time, text: text }); 
        }
    });

    // 监听音频时间更新事件
    audio.addEventListener('timeupdate', function () {
        let currentTime = audio.currentTime; 
        for (let i = 0; i < lyricsData.length; i++) {
            let lyric = lyricsData[i];
            // 将歌词时间戳转换为秒数
            let lyricTime = convertTimeToSeconds(lyric.time); 
            if (currentTime >= lyricTime && (i === 0 || currentTime < convertTimeToSeconds(lyricsData[i + 1].time))) {
                lyricsContainer.textContent = lyric.text; 
                break; 
            }
        }
    });
}

function convertTimeToSeconds(timeStr) {
    // 将时间戳字符串(如 00:00:00)转换为秒数
    let parts = timeStr.split(':');
    let seconds = 0;
    if (parts.length === 3) {
        seconds += parseInt(parts[0]) * 3600;
        seconds += parseInt(parts[1]) * 60;
        seconds += parseInt(parts[2]);
    } else if (parts.length === 2) {
        seconds += parseInt(parts[0]) * 60;
        seconds += parseInt(parts[1]);
    } else {
        seconds = parseInt(parts[0]);
    }
    return seconds;
}





// function displayLyrics(songId) {
//     $.ajax({
//         url: URL + "/content/get_song_detail",
//         type: "post",
//         data: { songId: songId },
//         dataType: "json",
//         success: function (result) {
//             if (result && result.lyrics) {
//                 let $lyricsContainer = $('#player-lyrics');
//                 $lyricsContainer.empty();

//                 // 创建所有歌词行
//                 result.lyrics.forEach((line, index) => {
//                     $('<p>', {
//                         text: line.lyric,
//                         'data-time': line.time,
//                         class: index === 0 ? 'active' : ''
//                     }).appendTo($lyricsContainer);
//                 });

//                 // 监听音频时间更新事件
//                 let audioPlayer = $('#player-audio')[0];
//                 audioPlayer.addEventListener('timeupdate', function() {
//                     let currentTime = this.currentTime;
//                     let $lines = $lyricsContainer.find('p');

//                     $lines.each(function() {
//                         let lineTime = parseFloat($(this).data('time'));
//                         $(this).toggleClass('active', Math.abs(currentTime - lineTime) < 2); // 当前时间接近歌词时间时激活
//                     });
//                 });
//             } else {
//                 alert("无法获取歌词");
//             }
//         },
//         error: function () {
//             alert("获取歌词出错,请联系管理员!");
//         }
//     });
// }


// //播放音乐
// function load_music() {
//     let playerContainer = document.getElementById('player-container');
//     let playerAudio = document.getElementById('player-audio');
//     let playerImg = document.getElementById('player-img');
//     let playerSongInfo = document.getElementById('player-song-info');
//     $(".playmusic_button img").on("click", function () {
//         var songId = $(this).parent().attr("id");

//         // 暂停音频并重置播放时间、移除加载事件监听器(若存在)
//                 if (!playerAudio.paused) {
//                     playerAudio.pause();
//                     playerAudio.currentTime = 0;
//                     playerAudio.removeEventListener('loadedmetadata', onMetadataLoaded); 
//                     // 假设存在此监听器,按需调整
//                 }

//         // 发送 AJAX 请求获取歌曲详细信息(包括播放地址、图片地址和歌曲信息)
//         $.ajax({
//             url: URL + "/content/get_song_detail",
//             type: "post",
//             data: { songId: songId },
//             dataType: "json",
//             success: function (result) {
//                 if (result && result.url && result.img && result.songInfo) {
//                     // 显示播放器容器
//                     playerContainer.style.display = "block";
//                     // 设置音频源、图片和歌曲信息
//                     playerAudio.src =URL+result.url;
//                     playerImg.src =URL+result.img;
//                     playerSongInfo.textContent = result.songInfo;
// 					 // 显示歌词
// 					displayLyrics(songId);
//                     // 播放歌曲
//                     playerAudio.play();
//                 } else {
//                     alert("无法获取歌曲详细信息");
//                 }
//             },
//             error: function () {
//                 alert("获取歌曲详细信息出错,请联系管理员!");
//             }
//         });
//     });

//     $(document).ready(function () {
//         load_music();
//     });
// }









最后是点击歌曲封面,展示信息,做了一点点

singer.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>歌手详情页</title>
    <link rel="stylesheet" href="singer.css">
	<script src="js/jquery-3.0.0.js"></script>
	<script src="js/site.js"></script>
	<script src="js/singer.js"></script>
</head>
<body>
    <ul class="catalogue"></ul>
</body>
</html>
singer.js
$(function(){
	load_catalogue();
});

function load_catalogue(){
	//获取url携带的参数值
	var urlparams=new URLSearchParams(window.location.search)
	var astr=urlparams.get("astr");
	//使用ajax提交
	$.ajax({
	            url:URL+"/content/find_catalogue", // 提交的路径
	            type:"post",// 提交的方式
	            data:{
	            	"astr":astr
	            },
	            dataType:"json",// 接收的数据类型
	            success:function (result){ // result:自定义的接收返回数据的变量名
	               var json_data=result.data;
				   console.log(json_data);
				   console.log(result);
	               var str="";
	               for(var i=0;i<json_data.length;i++){
	               		str+="<li><span class='singer'>"+json_data[i].singer+"</span></li><li><span class='song'>"+json_data[i].song+"</span></li><li><span class='album'>"+json_data[i].album+"</span></li><li><span class='singerimg'><img src='"+URL+json_data[i].singerimg+"'></span></li><li><span class='intro'>"+json_data[i].intro+"</span></li>";
	               		}
	               $("ul.catalogue").empty();
	               $("ul.catalogue").append(str);
	            },
	            error:function (){
	                alert("内部错误,请联系管理员!")
	            }
	        })
}


singer.css
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: 'Roboto', sans-serif;
  background-color: #0f0f0f;
  color: #ffffff;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
.catalogue {
  list-style: none;
  padding: 40px;
  position: relative;
  z-index: 5;
}

.catalogue li {
  background-color: #151515;
  border-radius: 15px;
  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3);
  padding: 30px;
  margin-bottom: 30px;
  transition: transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
}

.catalogue li:hover {
  transform: translateY(-5px);
  box-shadow: 0 8px 15px rgba(0, 0, 0, 0.4);
}

.catalogue li.singer {
  font-size: 28px;
  color: #00ffea;
  text-shadow: 0 3px 6px rgba(0, 0, 0, 0.3);
  margin-bottom: 10px;
  transition: transform 0.3s ease-in-out, text-shadow 0.3s ease-in-out;
}

.catalogue li.singer:hover {
  transform: translateX(5px);
  text-shadow: 0 0 5px #00ffea;
}

.catalogue li.song {
  font-size: 24px;
  color: #fff;
  text-shadow: 0 3px 6px rgba(0, 0, 0, 0.3);
  margin-bottom: 10px;
  transition: transform 0.3s ease-in-out, text-shadow 0.3s ease-in-out;
}

.catalogue li.song:hover {
  transform: translateX(5px);
  text-shadow: 0 0 5px #00ffea;
}

.catalogue li.album {
  font-size: 20px;
  color: #ccc;
  margin-bottom: 10px;
  transition: color 0.3s ease-in-out, text-shadow 0.3s ease-in-out;
}

.catalogue li.album:hover {
  color: #00ffea;
  text-shadow: 0 0 5px #00ffea;
}

.catalogue li.singerimg img {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  object-fit: cover;
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
  margin-bottom: 10px;
  transition: transform 0.3s ease-in-out;
}

.catalogue li.singerimg img:hover {
  transform: scale(1.1);
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.6);
}

.catalogue li.intro {
  font-size: 18px;
  color: #999;
  line-height: 1.5;
  transition: color 0.3s ease-in-out, text-shadow 0.3s ease-in-out;
}

.catalogue li.intro:hover {
  color: #00ffea;
  text-shadow: 0 0 5px #00ffea;
}
shouye.css
/* 全局样式 */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: 'Roboto', sans-serif;
  background-color: #0f0f0f;
  color: #ffffff;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* 导航栏样式 */
.top-nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background-color: #1a1a1a;
  padding: 15px 30px;
  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3);
  position: relative;
  z-index: 10;
  transition: all 0.3s ease-in-out;
}

.top-nav:hover {
  background-color: #222222;
  box-shadow: 0 8px 15px rgba(0, 0, 0, 0.4);
}

.logo img {
  width: 45px;
  height: 45px;
  border-radius: 50%;
  margin-right: 15px;
  transition: transform 0.6s cubic-bezier(0.215, 0.61, 0.355, 1);
}

.logo img:hover {
  transform: rotate(360deg);
}

.nav-links a {
  color: #ccc;
  text-decoration: none;
  margin: 0 20px;
  position: relative;
  padding: 5px 0;
  transition: color 0.3s ease-in-out, text-shadow 0.3s ease-in-out;
}

.nav-links a::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  width: 0;
  height: 2px;
  background-color: #00ffea;
  transition: width 0.3s ease-in-out;
}

.nav-links a:hover {
  color: #fff;
  text-shadow: 0 0 5px #00ffea;
}

.nav-links a:hover::after {
  width: 100%;
}

.search-bar input {
  padding: 10px 20px;
  border: none;
  border-radius: 30px;
  background-color: rgba(255, 255, 255, 0.1);
  color: #fff;
  width: 250px;
  transition: width 0.3s ease-in-out, background-color 0.3s ease-in-out;
}

.search-bar input:focus {
  width: 350px;
  background-color: rgba(255, 255, 255, 0.2);
}

.user-profile.notification {
  color: #ccc;
  margin-left: 20px;
  cursor: pointer;
  transition: color 0.3s ease-in-out, transform 0.3s ease-in-out;
}

.user-profile.notification:hover {
  color: #fff;
  transform: scale(1.1);
}

/* 二级导航栏样式 */
.sub-nav {
  display: flex;
  justify-content: center;
  background-color: #222222;
  padding: 12px 0;
  transition: background-color 0.3s ease-in-out;
}

.sub-nav:hover {
  background-color: #333333;
}

.sub-nav a {
  color: #ccc;
  text-decoration: none;
  margin: 0 25px;
  transition: color 0.3s ease-in-out, text-shadow 0.3s ease-in-out;
}

.sub-nav a:hover {
  color: #fff;
  text-shadow: 0 0 5px #00ffea;
}

/* 主体内容区域样式 */
.content {
    position: relative;
    display: flex;
    justify-content: space-between;
    padding: 40px;
    z-index: 5;
}

.left {
  flex-basis: 20%;
  background-color: #151515;
  border-radius: 15px;
  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3);
  padding: 30px;
  transition: transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
}

.left:hover {
  transform: translateY(-5px);
  box-shadow: 0 8px 15px rgba(0, 0, 0, 0.4);
}

.left dl {
  margin-bottom: 30px;
}

.left dl dd {
  cursor: pointer;
  padding: 12px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  transition: all 0.3s ease-in-out, text-shadow 0.3s ease-in-out;
}

.left dl dd:hover {
  color: #00ffea;
  border-left: 4px solid #00ffea;
  font-weight: 600;
  padding-left: 10px;
  text-shadow: 0 0 5px #00ffea;
}

.right {
  flex-basis: 75%;
}

.rank {
  font-size: 36px;
  margin-bottom: 30px;
  color: #00ffea;
  text-shadow: 0 3px 6px rgba(0, 0, 0, 0.3);
  transition: transform 0.3s ease-in-out, text-shadow 0.3s ease-in-out;
}

.rank:hover {
  transform: scale(1.05);
  text-shadow: 0 0 10px #00ffea;
}

.singing_show ul {
  list-style: none;
  background-color: #151515;
  border-radius: 15px;
  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3);
  padding: 30px;
  transition: transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
}

.singing_show ul:hover {
  transform: translateY(-5px);
  box-shadow: 0 8px 15px rgba(0, 0, 0, 0.4);
}

.singing_show ul li {
  display: flex;
  align-items: center;
  margin-bottom: 30px;
  padding: 20px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  transition: all 0.3s ease-in-out, background-color 0.3s ease-in-out;
}

.singing_show ul li:hover {
  background-color: #222222;
}

.singing_show ul li.img {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  overflow: hidden;
  margin-right: 20px;
  transition: transform 0.3s ease-in-out;
  object-fit: cover;
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
}
.img {
  width: 50px;
  height: 50px;
}

.singing_show ul li.img:hover {
  transform: scale(1.1);
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.6);
}

.singing_show ul li.song {
  flex-grow: 1;
  font-size: 28px;
  color: #fff;
  text-shadow: 0 3px 6px rgba(0, 0, 0, 0.3);
  transition: transform 0.3s ease-in-out, text-shadow 0.3s ease-in-out;
}

.singing_show ul li.song:hover {
  transform: translateX(5px);
  text-shadow: 0 0 5px #00ffea;
}

.singing_show ul li.singer {
  color: #ccc;
  margin-left: 20px;
  font-size: 20px;
  transition: color 0.3s ease-in-out, text-shadow 0.3s ease-in-out;
}

.singing_show ul li.singer:hover {
  color: #00ffea;
  text-shadow: 0 0 5px #00ffea;
}

.singing_show ul li.album {
  color: #999;
  margin-left: 20px;
  font-size: 18px;
}

.singing_show ul li.playmusic_button {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background-color: #00ffea;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
  margin-right: 15px;
  transition: all 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
}

.singing_show ul li.playmusic_button:hover {
  background-color: #00e0d0;
  transform: scale(1.1);
  box-shadow: 0 0 10px #00e0d0;
}

.singing_show ul li.playmusic_button::before {
  content: '\f04b';
  font-family: 'Font Awesome 5 Free';
  font-weight: 900;
  color: #000;
  font-size: 24px;
}

/* 明确.page 元素的定位和样式 */
.page {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 30px;
    position: relative;
    z-index: 102; /* 确保高于播放器容器 */
}

.page p {
    cursor: pointer;
    padding: 12px 25px;
    border-radius: 30px;
    border: 1px solid rgba(255, 255, 255, 0.1);
    color: #ccc;
    transition: all 0.3s ease-in-out, background-color 0.3s ease-in-out;
}

.page p:hover {
    background-color: #222222;
    color: #fff;
}

/* 背景动态线条样式 */
@keyframes moveLines {
  0% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-10px);
  }
  100% {
    transform: translateY(0);
  }
}

.background-lines {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 1;
  overflow: hidden;
}

.background-lines span {
  position: absolute;
  width: 2px;
  height: 100%;
  background-color: rgba(0, 255, 234, 0.1);
  animation: moveLines 5s ease-in-out infinite;
}

.background-lines span:nth-child(1) {
  left: 10%;
  animation-delay: 0s;
}

.background-lines span:nth-child(2) {
  left: 20%;
  animation-delay: 1s;
}

.background-lines span:nth-child(3) {
  left: 30%;
  animation-delay: 2s;
}

.background-lines span:nth-child(4) {
  left: 40%;
  animation-delay: 3s;
}

.background-lines span:nth-child(5) {
  left: 50%;
  animation-delay: 4s;
}

.background-lines span:nth-child(6) {
  left: 60%;
  animation-delay: 5s;
}

.background-lines span:nth-child(7) {
  left: 70%;
  animation-delay: 6s;
}

.background-lines span:nth-child(8) {
  left: 80%;
  animation-delay: 7s;
}

.background-lines span:nth-child(9) {
  left: 90%;
  animation-delay: 8s;
}

/* 按钮发光特效 */
@keyframes glow {
  0% {
    box-shadow: 0 0 5px #00ffea;
  }
  50% {
    box-shadow: 0 0 15px #00ffea;
  }
  100% {
    box-shadow: 0 0 5px #00ffea;
  }
}

.playmusic_button:hover::before {
  animation: glow 1s ease-in-out infinite;
}
.playmusic_button img{
	width: 20px;
	height: 20px;
	border-radius: 50%;
}
/* 调整 #player-container 的 z-index 值,使其高于页面其他内容但不影响关键交互元素 */
#player-container {
    display: flex;
    align-items: center;
    justify-content: space-between;
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    background-color: rgba(34, 34, 34, 0.9);
    z-index: 101;
    padding: 10px;
}

#player-container:hover {
    background: linear-gradient(to bottom, #222222, #151515);
}

#player-audio {
    /* 调整音频进度条宽度占比 */
    width: 60%; 
    margin-bottom: 0;
    background-color: rgba(50, 50, 50, 0.6);
    border-radius: 8px;
    appearance: none;
    -webkit-appearance: none;
    background-image: url('http://127.0.0.1:5000/static/images/流萤.webp'); 
    background-size: 100% 6px;
    background-repeat: no-repeat;
    background-position-y: center;
    cursor: pointer;
}

#player-audio::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 16px; 
    height: 16px; 
    border-radius: 50%;
    background-color: transparent; 
    cursor: pointer;
    margin-top: -5px;
    transition: transform 0.3s ease-in-out;
    box-shadow: 0 0 6px rgba(0, 0, 0, 0.4);
    background-image: url('http://127.0.0.1:5000/static/images/流萤.webp'); 
    background-size: cover; 
}

#player-audio::-webkit-slider-thumb:hover {
  transform: scale(1.3);
}

#player-img {
    flex-basis: 20%; 
    width: 65px;
    height: 65px;
    border-radius: 50%;
    object-fit: cover;
    margin-right: 15px;
    box-shadow: 0 0 8px rgba(0, 0, 0, 0.5);
    transition: transform 0.3s ease-in-out;
}

#player-img:hover {
  transform: scale(1.2);
  box-shadow: 0 0 12px rgba(0, 0, 0, 0.6);
}

#player-song-info {
    flex-grow: 1; 
    color: #ccc;
    font-size: 16px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    line-height: 1.5;
}

#player-song-info span {
  margin-bottom: 4px;
}

#player-song-info span:first-child {
  font-size: 20px;
  text-decoration: underline;
  text-shadow: 0 0 5px #00ffea;
  font-weight: 600;
  color: #fff;
}

#player-song-info span:last-child {
  font-size: 16px;
  color: #999;
}
/* 添加歌词样式 */
.lyrics-container {
    width: 100%;
    /* 调整歌词容器高度 */
    height: 80px; 
    background-color: rgba(0, 0, 0, 0.5);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 5px;
    overflow-y: scroll;
    scroll-behavior: smooth;
    margin-top: 5px; 
    padding: 5px; 
    text-align: right; 
}

.lyric-line {
    font-family: Arial, sans-serif;
    color: #ccc;
    font-size: 12px; 
    line-height: 1.3; 
    text-align: right; 
}

.active-lyric {
    color: #00ffea;
    font-weight: bold;
}

.play-button {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background-color: #00ffea;
    display: flex;
    justify-content: center;
    align-items: center;
    cursor: pointer;
    margin-right: 10px;
    transition: all 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
}

.play-button:hover {
    background-color: #00e0d0;
    transform: scale(1.1);
    box-shadow: 0 0 8px #00e0d0; 
}

.play-button img {
    width: 20px;
    height: 20px;
    border-radius: 50%;
}
到这里项目就初现成效了。完整源码在顶部相关资源,需要的小伙伴自行下载,原创:折纸鸢信歌,项目是开源的,文章未经允许禁止搬运!!!

作者:折纸鸢信歌

物联沃分享整理
物联沃-IOTWORD物联网 » 【学生专区】python flask 轻量级项目实战,前后端分离音乐后台管理系统及前端排行榜。

发表回复