forked from mrlan/EnglishPal
				
			feat: admin can manage articles and users without interfering with each other
							parent
							
								
									3e35679a91
								
							
						
					
					
						commit
						2cf65123e9
					
				|  | @ -1,9 +1,10 @@ | ||||||
|  | # System Library | ||||||
| from flask import * | from flask import * | ||||||
| from model import * | 
 | ||||||
| from pony.orm import * | # Personal library | ||||||
| from Yaml import yml | from Yaml import yml | ||||||
| from Login import md5 | from model.user import * | ||||||
| from datetime import datetime | from model.article import * | ||||||
| 
 | 
 | ||||||
| ADMIN_NAME = "lanhui"  # unique admin name | ADMIN_NAME = "lanhui"  # unique admin name | ||||||
| _cur_page = 1  # current article page | _cur_page = 1  # current article page | ||||||
|  | @ -11,33 +12,54 @@ _page_size = 5  # article sizes per page | ||||||
| adminService = Blueprint("admin_service", __name__) | adminService = Blueprint("admin_service", __name__) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @adminService.route("/admin", methods=["GET", "POST"]) | def check_is_admin(): | ||||||
| def admin(): |  | ||||||
|     global _cur_page, _page_size |  | ||||||
|     # 未登录,跳转到未登录界面 |     # 未登录,跳转到未登录界面 | ||||||
|     if not session.get("logged_in"): |     if not session.get("logged_in"): | ||||||
|         return render_template("not_login.html") |         return render_template("not_login.html") | ||||||
| 
 | 
 | ||||||
|     # 获取session里的用户名 |     # 用户名不是admin_name | ||||||
|     username = session.get("username") |     if session.get("username") != ADMIN_NAME: | ||||||
|     if username != ADMIN_NAME: |  | ||||||
|         return "You are not admin!" |         return "You are not admin!" | ||||||
| 
 | 
 | ||||||
|  |     return "pass" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @adminService.route("/admin", methods=["GET"]) | ||||||
|  | def admin(): | ||||||
|  |     is_admin = check_is_admin() | ||||||
|  |     if is_admin != "pass": | ||||||
|  |         return is_admin | ||||||
|  | 
 | ||||||
|  |     return render_template( | ||||||
|  |         "admin_index.html", yml=yml, username=session.get("username") | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @adminService.route("/admin/article", methods=["GET", "POST"]) | ||||||
|  | def article(): | ||||||
|  |     global _cur_page, _page_size | ||||||
|  | 
 | ||||||
|  |     is_admin = check_is_admin() | ||||||
|  |     if is_admin != "pass": | ||||||
|  |         return is_admin | ||||||
|  | 
 | ||||||
|     article_number = get_number_of_articles() |     article_number = get_number_of_articles() | ||||||
|     try: |     try: | ||||||
|         _page_size = min(max(1, int(request.args.get("size", 5))), article_number)  # 最小的size是1 |         _page_size = min( | ||||||
|         _cur_page = min(max(1, int(request.args.get("page", 1))), article_number // _page_size + 1) # 最小的page是1 |             max(1, int(request.args.get("size", 5))), article_number | ||||||
|  |         )  # 最小的size是1 | ||||||
|  |         _cur_page = min( | ||||||
|  |             max(1, int(request.args.get("page", 1))), article_number // _page_size + 1 | ||||||
|  |         )  # 最小的page是1 | ||||||
|     except ValueError: |     except ValueError: | ||||||
|         return "page parmas must be int!" |         return "page parmas must be int!" | ||||||
| 
 | 
 | ||||||
|     context = { |     context = { | ||||||
|         "article_number": article_number, |         "article_number": article_number, | ||||||
|  |         "text_list": get_page_articles(_cur_page, _page_size), | ||||||
|         "page_size": _page_size, |         "page_size": _page_size, | ||||||
|         "cur_page": _cur_page, |         "cur_page": _cur_page, | ||||||
|         "text_list": get_page_articles(_cur_page, _page_size), |         "username": session.get("username"), | ||||||
|         "user_list": get_users(), |  | ||||||
|         "username": username, |  | ||||||
|         "yml": yml, |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     def _update_context(): |     def _update_context(): | ||||||
|  | @ -46,71 +68,50 @@ def admin(): | ||||||
|         context["text_list"] = get_page_articles(_cur_page, _page_size) |         context["text_list"] = get_page_articles(_cur_page, _page_size) | ||||||
| 
 | 
 | ||||||
|     if request.method == "GET": |     if request.method == "GET": | ||||||
|  |         try: | ||||||
|             delete_id = int(request.args.get("delete_id", 0)) |             delete_id = int(request.args.get("delete_id", 0)) | ||||||
|  |         except: | ||||||
|  |             return "Delete article ID must be int!" | ||||||
|         if delete_id:  # delete article |         if delete_id:  # delete article | ||||||
|             delete_article(delete_id) |             delete_article_by_id(delete_id) | ||||||
|             _update_context() |             _update_context() | ||||||
|     else: |     elif request.method == "POST": | ||||||
|         data = request.form |         data = request.form | ||||||
|         content = data.get("content", "") |         content = data.get("content", "") | ||||||
|         source = data.get("source", "") |         source = data.get("source", "") | ||||||
|         question = data.get("question", "") |         question = data.get("question", "") | ||||||
|         username = data.get("username", "") |  | ||||||
|         level = data.get("level", "5") |         level = data.get("level", "5") | ||||||
|         if content: |         if content: | ||||||
|             try:  # check level |             try:  # check level | ||||||
|                 if level not in [str(x + 1) for x in range(5)]: |                 if level not in [str(x + 1) for x in range(5)]: | ||||||
|                     raise ValueError |                     raise ValueError | ||||||
|             except ValueError: |             except ValueError: | ||||||
|                 return "level must be between 1 and 5" |                 return "Level must be between 1 and 5" | ||||||
|             add_article(content, source, level, question) |             add_article(content, source, level, question) | ||||||
|             _update_context() |             _update_context() | ||||||
|  | 
 | ||||||
|  |     return render_template("admin_manage_article.html", **context) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @adminService.route("/admin/user", methods=["GET", "POST"]) | ||||||
|  | def user(): | ||||||
|  |     is_admin = check_is_admin() | ||||||
|  |     if is_admin != "pass": | ||||||
|  |         return is_admin | ||||||
|  |      | ||||||
|  |     context = { | ||||||
|  |         "user_list": get_users(), | ||||||
|  |         "username": session.get("username"), | ||||||
|  |     } | ||||||
|  |     if request.method == "POST": | ||||||
|  |         data = request.form | ||||||
|  |         username = data.get("username","") | ||||||
|  |         new_password = data.get("new_password", "") | ||||||
|  |         expiry_time = data.get("expiry_time", "") | ||||||
|         if username: |         if username: | ||||||
|             update_user_password(username) |             if new_password: | ||||||
|  |                 update_password_by_username(username, new_password) | ||||||
|  |             if expiry_time: | ||||||
|  |                 update_expiry_time_by_username(username, "".join(expiry_time.split("-"))) | ||||||
|      |      | ||||||
|     return render_template("admin_index.html", **context) |     return render_template("admin_manage_user.html", **context) | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def add_article(content, source="manual_input", level="5", question="No question"): |  | ||||||
|     with db_session: |  | ||||||
|         # add one article to sqlite |  | ||||||
|         Article( |  | ||||||
|             text=content, |  | ||||||
|             source=source, |  | ||||||
|             date=datetime.now().strftime("%-d %b %Y"),  # format style of `5 Oct 2022` |  | ||||||
|             level=level, |  | ||||||
|             question=question, |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def delete_article(article_id): |  | ||||||
|     article_id &= 0xFFFFFFFF  # max 32 bits |  | ||||||
|     with db_session: |  | ||||||
|         article = Article.select(article_id=article_id) |  | ||||||
|         if article: |  | ||||||
|             article.first().delete() |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def get_number_of_articles(): |  | ||||||
|     with db_session: |  | ||||||
|         return len(Article.select()[:]) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def get_page_articles(num, size): |  | ||||||
|     with db_session: |  | ||||||
|         return [ |  | ||||||
|             x |  | ||||||
|             for x in Article.select().order_by(desc(Article.article_id)).page(num, size) |  | ||||||
|         ] |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def get_users(): |  | ||||||
|     with db_session: |  | ||||||
|         return User.select().order_by(User.name)[:] |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def update_user_password(username, password="123456"): |  | ||||||
|     with db_session: |  | ||||||
|         user = User.select(name=username) |  | ||||||
|         if user: |  | ||||||
|             user.first().password = md5(username + password) |  | ||||||
|  | @ -42,82 +42,15 @@ | ||||||
|     </nav> |     </nav> | ||||||
| 
 | 
 | ||||||
|     <div class="card" style="margin-top:24px;"> |     <div class="card" style="margin-top:24px;"> | ||||||
|         <h5 style="margin-top: 10px;padding-left: 10px;">重置选中用户密码</h5> |         <div class="card-header"> | ||||||
|         <form action="" method="post" class="container mb-3"> |             请选择您需要的操作 | ||||||
|             <div class="mb-3"> |  | ||||||
|                 <label  class="form-label">用户</label> |  | ||||||
|                 <select id="username" name="username" class="form-select" aria-label="Default select example"> |  | ||||||
|                     <option selected>选择用户</option> |  | ||||||
|                     {% for user in user_list %} |  | ||||||
|                     <option value="{{ user.name }}">{{ user.name }}</option> |  | ||||||
|                     {% endfor %} |  | ||||||
|                 </select> |  | ||||||
|         </div> |         </div> | ||||||
|             <input type="submit" value="重置密码为:123456" class="btn btn-outline-primary"> |         <ul class="list-group list-group-flush"> | ||||||
|         </form> |             <li class="list-group-item"> | ||||||
|  |                 <div class="d-grid gap-2"> | ||||||
|  |                     <a href="/admin/article" class="btn btn-outline-primary" type="button">管理文章</a> | ||||||
|  |                     <a href="/admin/user" class="btn btn-outline-primary" type="button">管理用户</a> | ||||||
|                 </div> |                 </div> | ||||||
| 
 |  | ||||||
|     <div class="card" style="margin-top:24px;"> |  | ||||||
|         {% if tips %} |  | ||||||
|         <div class="alert alert-success" role="alert"> |  | ||||||
|             {{ tips }} |  | ||||||
|         </div> |  | ||||||
|         {% endif %} |  | ||||||
|         <div class="card-content"> |  | ||||||
|             <h5 style="margin-top: 10px;padding-left: 10px;">录入文章</h5> |  | ||||||
|             <form action="" method="post" class="container mb-3"> |  | ||||||
|                 <div class="mb-3"> |  | ||||||
|                     <label class="form-label">文章内容</label> |  | ||||||
|                     <textarea id="content" name="content" class="form-control" placeholder="请输入文章内容"></textarea> |  | ||||||
|                     <label class="form-label">文章来源</label> |  | ||||||
|                     <textarea id="source" name="source" class="form-control" placeholder="请输入来源"></textarea> |  | ||||||
|                     <label class="form-label">文章等级</label> |  | ||||||
|                     <select id="level" class="form-select" name="level"> |  | ||||||
|                         <option value="1">1</option> |  | ||||||
|                         <option value="2">2</option> |  | ||||||
|                         <option value="3">3</option> |  | ||||||
|                         <option value="4">4</option> |  | ||||||
|                         <option selected value="5">5</option> |  | ||||||
|                     </select> |  | ||||||
|                     <label class="form-label">文章问题</label> |  | ||||||
|                     <textarea id="question" name="question" class="form-control" placeholder="请输入问题"></textarea> |  | ||||||
|                 </div> |  | ||||||
|                 <input type="submit" value="保存" class="btn btn-outline-primary"> |  | ||||||
|             </form> |  | ||||||
|         </div> |  | ||||||
|     </div> |  | ||||||
| 
 |  | ||||||
|     <div class="card" style="margin-top:24px;"> |  | ||||||
|         <h5 style="margin-top: 10px;padding-left: 10px;">文章列表</h5> |  | ||||||
|         <div class="list-group"> |  | ||||||
|             {% for text in text_list %} |  | ||||||
|             <div class="list-group-item list-group-item-action" aria-current="true"> |  | ||||||
|                 <div class="d-flex w-100 justify-content-between"> |  | ||||||
|                     <h5 class="mb-1">{{ text.source }}</h5> |  | ||||||
|                     <small>Date:{{ text.date }} Level:{{ text.level }}</small> |  | ||||||
|                 </div> |  | ||||||
|                 <div style="text-align: right; padding-bottom: 5px;"><a href="/admin?delete_id={{text.article_id}}" |  | ||||||
|                         class="btn btn-outline-danger btn-sm"> |  | ||||||
|                         删除文章 |  | ||||||
|                     </a></div> |  | ||||||
|                 <p class="mb-1">{{ text.text }}</p> |  | ||||||
|             </div> |  | ||||||
|             {% endfor %} |  | ||||||
|         </div> |  | ||||||
|     </div> |  | ||||||
|     <div style="margin:20px 0;"> |  | ||||||
|         <ul class="pagination pagination-sm justify-content-center"> |  | ||||||
|             <li class="page-item"><a class="page-link" href="/admin?page={{ cur_page - 1 }}&size={{ page_size }}">Previous</a> |  | ||||||
|             </li> |  | ||||||
|             {% for i in range(1, article_number // page_size + 2) %} |  | ||||||
|             {% if cur_page == i %} |  | ||||||
|             <li class="page-item active"><a class="page-link" href="/admin?page={{ i }}&size={{ page_size }}">{{ i }}</a> |  | ||||||
|             </li> |  | ||||||
|             {% else %} |  | ||||||
|             <li class="page-item"><a class="page-link" href="/admin?page={{ i }}&size={{ page_size }}">{{ i }}</a></li> |  | ||||||
|             {% endif %} |  | ||||||
|             {% endfor %} |  | ||||||
|             <li class="page-item"><a class="page-link" href="/admin?page={{ cur_page + 1 }}&size={{ page_size }}">Next</a> |  | ||||||
|             </li> |             </li> | ||||||
|         </ul> |         </ul> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|  | @ -0,0 +1,99 @@ | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  | 
 | ||||||
|  | <head> | ||||||
|  |     <meta charset="UTF-8"> | ||||||
|  |     <meta name="viewport" | ||||||
|  |         content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" /> | ||||||
|  |     <meta name="format-detection" content="telephone=no" /> | ||||||
|  |     <link href="../static/css/bootstrap.css" rel="stylesheet"> | ||||||
|  | </head> | ||||||
|  | 
 | ||||||
|  | <body class="container" style="width: 800px; margin: auto; margin-top:24px;"> | ||||||
|  |     <nav class="navbar navbar-expand-lg bg-light"> | ||||||
|  |         <div class="container-fluid"> | ||||||
|  |             <a class="navbar-brand" href="#">管理员 {{ username }} 您好!</a> | ||||||
|  |             <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" | ||||||
|  |                 aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> | ||||||
|  |                 <span class="navbar-toggler-icon"></span> | ||||||
|  |             </button> | ||||||
|  |             <div class="collapse navbar-collapse" id="navbarNav"> | ||||||
|  |                 <ul class="navbar-nav"> | ||||||
|  |                     <li class="nav-item"> | ||||||
|  |                         <a class="nav-link" href="/{{ username }}">返回主页</a> | ||||||
|  |                     </li> | ||||||
|  |                     <li class="nav-item"> | ||||||
|  |                         <a class="nav-link" href="/logout">退出登录</a> | ||||||
|  |                     </li> | ||||||
|  |                 </ul> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |     </nav> | ||||||
|  | 
 | ||||||
|  |     <div class="card" style="margin-top:24px;"> | ||||||
|  |         {% if tips %} | ||||||
|  |         <div class="alert alert-success" role="alert"> | ||||||
|  |             {{ tips }} | ||||||
|  |         </div> | ||||||
|  |         {% endif %} | ||||||
|  |         <div class="card-content"> | ||||||
|  |             <h5 style="margin-top: 10px;padding-left: 10px;">录入文章</h5> | ||||||
|  |             <form action="" method="post" class="container mb-3"> | ||||||
|  |                 <div class="mb-3"> | ||||||
|  |                     <label class="form-label">文章内容</label> | ||||||
|  |                     <textarea id="content" name="content" class="form-control" placeholder="请输入文章内容"></textarea> | ||||||
|  |                     <label class="form-label">文章来源</label> | ||||||
|  |                     <textarea id="source" name="source" class="form-control" placeholder="请输入来源"></textarea> | ||||||
|  |                     <label class="form-label">文章等级</label> | ||||||
|  |                     <select id="level" class="form-select" name="level"> | ||||||
|  |                         <option value="1">1</option> | ||||||
|  |                         <option value="2">2</option> | ||||||
|  |                         <option value="3">3</option> | ||||||
|  |                         <option value="4">4</option> | ||||||
|  |                         <option selected value="5">5</option> | ||||||
|  |                     </select> | ||||||
|  |                     <label class="form-label">文章问题</label> | ||||||
|  |                     <textarea id="question" name="question" class="form-control" placeholder="请输入问题"></textarea> | ||||||
|  |                 </div> | ||||||
|  |                 <input type="submit" value="保存" class="btn btn-outline-primary"> | ||||||
|  |             </form> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div class="card" style="margin-top:24px;"> | ||||||
|  |         <h5 style="margin-top: 10px;padding-left: 10px;">文章列表</h5> | ||||||
|  |         <div class="list-group"> | ||||||
|  |             {% for text in text_list %} | ||||||
|  |             <div class="list-group-item list-group-item-action" aria-current="true"> | ||||||
|  |                 <div class="d-flex w-100 justify-content-between"> | ||||||
|  |                     <h5 class="mb-1">{{ text.source }}</h5> | ||||||
|  |                     <small>Date:{{ text.date }} Level:{{ text.level }}</small> | ||||||
|  |                 </div> | ||||||
|  |                 <div style="text-align: right; padding-bottom: 5px;"><a href="/admin/article?delete_id={{text.article_id}}" | ||||||
|  |                         class="btn btn-outline-danger btn-sm"> | ||||||
|  |                         删除文章 | ||||||
|  |                     </a></div> | ||||||
|  |                 <p class="mb-1">{{ text.text }}</p> | ||||||
|  |             </div> | ||||||
|  |             {% endfor %} | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  |     <div style="margin:20px 0;"> | ||||||
|  |         <ul class="pagination pagination-sm justify-content-center"> | ||||||
|  |             <li class="page-item"><a class="page-link" href="/admin/article?page={{ cur_page - 1 }}&size={{ page_size }}">Previous</a> | ||||||
|  |             </li> | ||||||
|  |             {% for i in range(1, article_number // page_size + 2) %} | ||||||
|  |             {% if cur_page == i %} | ||||||
|  |             <li class="page-item active"><a class="page-link" href="/admin/article?page={{ i }}&size={{ page_size }}">{{ i }}</a> | ||||||
|  |             </li> | ||||||
|  |             {% else %} | ||||||
|  |             <li class="page-item"><a class="page-link" href="/admin/article?page={{ i }}&size={{ page_size }}">{{ i }}</a></li> | ||||||
|  |             {% endif %} | ||||||
|  |             {% endfor %} | ||||||
|  |             <li class="page-item"><a class="page-link" href="/admin/article?page={{ cur_page + 1 }}&size={{ page_size }}">Next</a> | ||||||
|  |             </li> | ||||||
|  |         </ul> | ||||||
|  |     </div> | ||||||
|  | </body> | ||||||
|  | 
 | ||||||
|  | </html> | ||||||
|  | @ -0,0 +1,83 @@ | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  | 
 | ||||||
|  | <head> | ||||||
|  |     <meta charset="UTF-8"> | ||||||
|  |     <meta name="viewport" | ||||||
|  |         content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" /> | ||||||
|  |     <meta name="format-detection" content="telephone=no" /> | ||||||
|  |     <link href="../static/css/bootstrap.css" rel="stylesheet"> | ||||||
|  |     <script src="../static/js/jquery.js"></script> | ||||||
|  | </head> | ||||||
|  | 
 | ||||||
|  | <body class="container" style="width: 800px; margin: auto; margin-top:24px;"> | ||||||
|  |     <nav class="navbar navbar-expand-lg bg-light"> | ||||||
|  |         <div class="container-fluid"> | ||||||
|  |             <a class="navbar-brand" href="#">管理员 {{ username }} 您好!</a> | ||||||
|  |             <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" | ||||||
|  |                 aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> | ||||||
|  |                 <span class="navbar-toggler-icon"></span> | ||||||
|  |             </button> | ||||||
|  |             <div class="collapse navbar-collapse" id="navbarNav"> | ||||||
|  |                 <ul class="navbar-nav"> | ||||||
|  |                     <li class="nav-item"> | ||||||
|  |                         <a class="nav-link" href="/{{ username }}">返回主页</a> | ||||||
|  |                     </li> | ||||||
|  |                     <li class="nav-item"> | ||||||
|  |                         <a class="nav-link" href="/logout">退出登录</a> | ||||||
|  |                     </li> | ||||||
|  |                 </ul> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |     </nav> | ||||||
|  | 
 | ||||||
|  |     <div class="card" style="margin-top:24px;"> | ||||||
|  |         <h5 style="margin-top: 10px;padding-left: 10px;">重置选中用户的信息</h5> | ||||||
|  |         <form id="user_form" action="" method="post" class="container mb-3"> | ||||||
|  |             <div> | ||||||
|  |                 <label class="form-label" style="padding-top: 10px;">用户</label> | ||||||
|  |                 <select id="username" name="username" class="form-select" aria-label="Default select example"> | ||||||
|  |                     <option selected>选择用户</option> | ||||||
|  |                     {% for user in user_list %} | ||||||
|  |                     <option value="{{ user.name }}">{{ user.name }}</option> | ||||||
|  |                     {% endfor %} | ||||||
|  |                 </select> | ||||||
|  | 
 | ||||||
|  |                 <label class="form-label" style="padding-top: 10px;">修改密码</label> | ||||||
|  |                 <div> | ||||||
|  |                     <button type="button" id="reset_pwd_btn" class="btn btn-outline-success">获取12位随机密码</button> | ||||||
|  |                     <input style="margin-left: 20px;border: 0; font-size: 20px;" name="new_password" | ||||||
|  |                         id="new_password"></input> | ||||||
|  |                 </div> | ||||||
|  | 
 | ||||||
|  |                 <label class="form-label" style="padding-top: 10px;">过期时间</label> | ||||||
|  |                 <div> | ||||||
|  |                     <input type="date" name="expiry_time" placeholder="YYYY-MM-DD" pattern="yyyyMMdd"> | ||||||
|  |                 </div> | ||||||
|  | 
 | ||||||
|  |             </div> | ||||||
|  | 
 | ||||||
|  |             <button style="margin-top: 50px;" type="submit" class="btn btn-primary">更新用户信息</button> | ||||||
|  |         </form> | ||||||
|  |     </div> | ||||||
|  | </body> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  |     // 密码生成器 | ||||||
|  |     function generatePassword(length) { | ||||||
|  |         var charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+~`|}{[]\:;?><,./-="; | ||||||
|  |         var password = ""; | ||||||
|  |         for (var i = 0; i < length; i++) { | ||||||
|  |             password += charset.charAt(Math.floor(Math.random() * charset.length)); | ||||||
|  |         } | ||||||
|  |         return password; | ||||||
|  |     } | ||||||
|  |     document.getElementById("reset_pwd_btn").addEventListener("click", () => { | ||||||
|  |         // 生成12位随机密码 | ||||||
|  |         let pwd = generatePassword(12) | ||||||
|  |         document.getElementById("new_password").value = pwd | ||||||
|  |     }) | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | </html> | ||||||
		Loading…
	
		Reference in New Issue