This commit is contained in:
Jane Doe
2024-09-25 12:18:07 +08:00
parent 3f369584fb
commit 9992293b9f
124 changed files with 2770 additions and 975 deletions

View File

@@ -0,0 +1,262 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>网站信息管理</title>
<link rel="stylesheet" href="https://file.guimiaokeji.com/layui/layui.css">
<script src="https://file.guimiaokeji.com/axios.js"></script>
<script src="https://file.guimiaokeji.com/qs.js"></script>
<style>
body {
background-color: #f5f5f5;
}
.layui-container {
background-color: #fff;
padding: 20px;
margin: 20px auto;
border-radius: 10px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
width: 100% !important;
}
.layui-col-md6 {
padding: 10px;
}
.layui-form-label {
width: auto;
font-weight: bold;
}
.layui-input-block {
margin-left: 150px;
}
.preview-img {
max-width: 200px;
margin-top: 10px;
}
.layui-form-item {
margin-bottom: 5px;
}
.layui-input-block {
background-color: #f3f3f3;
border-radius: 5px;
padding: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.layui-col-md6:nth-child(odd) .layui-input-block {
background-color: #e9f7f7;
}
.layui-col-md6:nth-child(even) .layui-input-block {
background-color: #e9f7f7;
}
textarea {
width: 100%;
height: 150px;
border: 1px solid #dcdcdc;
border-radius: 5px;
background-color: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.layui-btn-container {
text-align: center;
margin-top: 20px;
}
/* 居中上传图片按钮和预览图片 */
.layui-input-block {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
#preview_image {
margin-top: 10px;
display: block;
max-width: 200px;
border-radius: 5px;
}
#center{
position: relative;
left: 50%;
transform: translateX(-50%);
}
</style>
</head>
<body>
<div class="layui-container">
<form class="layui-form" action="" method="post">
{% csrf_token %}
<!-- 第一行 封面图片 -->
<div class="layui-row">
<div class="layui-col-md6" id="center">
<div class="layui-form-item">
<div class="layui-input-block">
<input type="text" name="image_url" class="layui-input" value="{{ article.image_url|default_if_none:'' }}" placeholder="请输入图片 URL 或点击上传图片">
{% if article.image_url %}
<img id="preview_image" src="{{ article.image_url }}" alt="封面图片" class="preview-img">
{% else %}
<img id="preview_image" style="display: none;" class="preview-img">
{% endif %}
<button type="button" class="layui-btn layui-btn-sm" id="upload_image">
<i class="layui-icon">&#xe67c;</i> 上传图片
</button>
</div>
</div>
</div>
</div>
<!-- 第二行 标题 -->
<div class="layui-row">
<div class="layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">english_title</label>
<div class="layui-input-block">
<input type="text" name="english_title" required lay-verify="required" placeholder="请输入英文标题" class="layui-input" value="{{ article.english_title }}">
</div>
</div>
</div>
<div class="layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">中文标题</label>
<div class="layui-input-block">
<input type="text" name="chinese_title" required lay-verify="required" placeholder="请输入中文标题" class="layui-input" value="{{ article.chinese_title }}">
</div>
</div>
</div>
</div>
<!-- 第三行 关键词 -->
<div class="layui-row">
<div class="layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">english_keywords</label>
<div class="layui-input-block">
<input type="text" name="english_keywords" placeholder="请输入英文关键词" class="layui-input" value="{{ article.english_keywords }}">
</div>
</div>
</div>
<div class="layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">中文关键词</label>
<div class="layui-input-block">
<input type="text" name="chinese_keywords" placeholder="请输入中文关键词" class="layui-input" value="{{ article.chinese_keywords }}">
</div>
</div>
</div>
</div>
<!-- 第四行 描述 -->
<div class="layui-row">
<div class="layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">english_content</label>
<div class="layui-input-block">
<textarea id="english_description" name="english_content" style="display: none;">{{ article.english_content }}</textarea>
</div>
</div>
</div>
<div class="layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">中文描述</label>
<div class="layui-input-block">
<textarea id="chinese_description" name="chinese_content" style="display: none;">{{ article.chinese_content }}</textarea>
</div>
</div>
</div>
</div>
<!-- 提交按钮 -->
<div class="layui-form-item layui-btn-container" style="text-align: center;">
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveArticle">保存修改</button>
</div>
</form>
</div>
<script src="https://file.guimiaokeji.com/layui/jquery-3.6.0.min.js"></script>
<script src="https://file.guimiaokeji.com/layui/layui.js"></script>
<script>
layui.use(['form', 'layedit', 'upload'], function () {
var form = layui.form;
var layedit = layui.layedit;
var upload = layui.upload;
var $ = layui.jquery;
layedit.set({
uploadImage: {
url: '/api/upload_article_image/' //接口url
,type: 'post' //默认post
}
});
// 初始化 layedit 编辑器
var englishEditor = layedit.build('english_description', {
height: 250,
tool: ['strong', 'italic', 'underline', 'del', '|', 'left', 'center', 'right', '|', 'link', 'unlink', 'image']
});
var chineseEditor = layedit.build('chinese_description', {
height: 250,
tool: ['strong', 'italic', 'underline', 'del', '|', 'left', 'center', 'right', '|', 'link', 'unlink', 'image']
});
// 表单提交前同步内容
form.on('submit(saveArticle)', function(data) {
// 获取编辑器内容
data.field.english_content = layedit.getContent(englishEditor);
data.field.chinese_content = layedit.getContent(chineseEditor);
// 使用 Axios 提交表单
axios.post('', data.field)
.then(function(response) {
if (response.data.code === 200) {
layer.msg(response.data.message, {
time: 1000 // 设置显示时间为 1000 毫秒1 秒)
}, function() {
// 在消息框关闭后执行的操作
var index = parent.layer.getFrameIndex(window.name);
parent.layer.close(index);
});
} else {
layer.msg('保存失败: ' + response.data.message);
}
})
.catch(function(error) {
layer.msg('请求失败: ' + error.message);
});
return false; // 阻止表单默认提交
});
// 图片上传功能
upload.render({
elem: '#upload_image',
url: '/api/upload_article_image/', // 图片上传接口
field: 'file',
done: function (res) {
if (res.code === 0) {
$('#preview_image').attr('src', res.data.src).show();
$('input[name="image_url"]').val(res.data.src);
layer.msg('图片上传成功');
} else {
layer.msg('图片上传失败: ' + res.message);
}
},
error: function () {
layer.msg('图片上传失败');
}
});
});
</script>
</body>
</html>

View File

@@ -6,55 +6,82 @@
<link rel="stylesheet" href="https://file.guimiaokeji.com/layui/css/layui.css">
<style>
body {
background: linear-gradient(to right, #6a11cb, #2575fc);
background: linear-gradient(to right, #e0f7fa, #80deea);
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
font-family: 'Arial', sans-serif;
color: #fff;
color: #333;
}
.login-container {
width: 400px;
padding: 30px;
background: rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.5);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
border-radius: 10px;
animation: fadeIn 1s ease-in-out;
border-radius: 20px;
backdrop-filter: blur(10px);
text-align: center;
animation: fadeIn 1s ease-in-out;
position: relative;
}
.login-container h2 {
text-align: center;
margin-bottom: 20px;
color: #fff;
color: #333;
}
.layui-btn {
width: 100%;
transition: background 0.3s ease;
}
.layui-btn:hover {
background: linear-gradient(45deg, #3b82ec, #2196f3);
background: #5cb85c;
}
.loading {
.loading-spinner {
z-index: 99999;
display: none;
border: 4px solid rgba(0, 0, 0, 0.1);
border-radius: 50%;
border-top: 4px solid #5cb85c;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
from { opacity: 0; transform: translateY(-20px); }
to { opacity: 1; transform: translateY(0); }
}
.captcha-container {
display: flex;
align-items: center;
justify-content: space-between;
}
.captcha-container .layui-form-label {
flex: 0 0 90px; /* 确保验证码输入框的标签与其他字段一致 */
}
.captcha-container img {
height: 38px;
width: 100px;
cursor: pointer;
}
.captcha-input {
flex-grow: 1;
margin-right: 10px;
}
</style>
</head>
<body>
<div class="login-container">
<h2>管理员登录</h2>
<div class="loading-spinner" id="loadingSpinner"></div>
<form class="layui-form" id="loginForm">
{% csrf_token %}
<div class="layui-form-item">
@@ -69,55 +96,59 @@
<input type="password" name="password" required lay-verify="required" placeholder="请输入密码" class="layui-input">
</div>
</div>
<div class="layui-form-item captcha-container">
<label class="layui-form-label">验证码</label>
<input type="text" name="captcha" required lay-verify="required" placeholder="请输入验证码" class="layui-input captcha-input">
<img src="{% url 'captcha_image' %}" alt="验证码" onclick="this.src='{% url 'captcha_image' %}?'+Math.random();">
</div>
<div class="layui-form-item">
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="login">登录</button>
</div>
<div id="error-msg" style="display: none;">
<p style="color: red;">用户名或密码错误</p>
</div>
<div class="loading">
<div class="layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop" style="font-size: 40px;"></div>
<p>登录中,请稍候...</p>
<button type="submit" class="layui-btn">登录</button>
</div>
</form>
</div>
<script src="https://file.guimiaokeji.com/layui/jquery-3.6.0.min.js"></script>
<script src="https://file.guimiaokeji.com/layui/layui.js"></script>
<script>
layui.use(['form', 'layer'], function() {
$(document).ready(function() {
var form = layui.form;
var $ = layui.jquery;
var layer = layui.layer;
form.on('submit(login)', function(data) {
document.querySelector('.loading').style.display = 'block';
document.getElementById('loginForm').style.display = 'none';
$('#loginForm').on('submit', function(event) {
event.preventDefault(); // 阻止默认提交行为
$('#loadingSpinner').show(); // 显示加载动画
const username = $('input[name="username"]').val();
const password = $('input[name="password"]').val();
const captcha = $('input[name="captcha"]').val();
$.ajax({
type: 'POST',
url: '{% url "admin_login" %}',
data: JSON.stringify(data.field),
url: '{% url "admin_login" %}', // 替换为实际的 URL
contentType: 'application/json',
data: JSON.stringify({ username, password, captcha }),
success: function(response) {
document.querySelector('.loading').style.display = 'none';
$('#loadingSpinner').hide(); // 隐藏加载动画
layer.msg(response.message); // 使用 Layer.js 显示消息
if (response.code === 200) {
layer.msg(response.message, {icon: 1, time: 2000}, function() {
window.location.href = '{% url "admin_home" %}';
});
} else {
document.getElementById('loginForm').style.display = 'block';
layer.msg(response.message, {icon: 2, time: 2000});
// 登录成功后跳转
setTimeout(function() {
window.location.href = '{% url "admin_home" %}'; // 替换为实际的 URL
}, 1000);
}else if (response.code === 400){
$('img[alt="验证码"]').attr('src', '{% url "captcha_image" %}?'+Math.random());
}
},
error: function(xhr, status, error) {
document.querySelector('.loading').style.display = 'none';
document.getElementById('loginForm').style.display = 'block';
layer.msg('登录失败,请稍后重试。', {icon: 2, time: 2000});
error: function(xhr) {
$('#loadingSpinner').hide(); // 隐藏加载动画
const response = xhr.responseJSON;
layer.msg(response.message); // 使用 Layer.js 显示错误消息
}
});
return false; // 阻止表单跳转。如果需要表单跳转,去掉这段即可。
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,217 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>文章管理</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/layui-src/dist/css/layui.css">
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<style>
body {
background-color: #f5f5f5;
}
.layui-container {
width: 100% !important;
padding: 20px;
background-color: #ffffff;
}
h2 {
color: #333;
font-size: 24px;
}
.layui-table {
width: 100%;
margin-top: 20px;
}
.layui-table tbody tr:nth-child(odd) {
background-color: #f9f9f9;
}
.layui-table tbody tr:nth-child(even) {
background-color: #eaf6f6;
}
.stats-container {
display: flex;
align-items: center;
margin-top: 20px;
}
.stats-container p {
margin: 0 20px;
font-size: 14px;
}
.stats-container span {
color: #2de5cf;
}
..layui-table-view .layui-table{
width: 100% !important;
}
</style>
</head>
<body>
<div class="layui-container">
<div class="layui-row">
<div class="layui-col-xs12 layui-col-sm6">
<button class="layui-btn layui-btn-normal layui-btn-sm" id="addArticle">添加文章</button>
</div>
<div class="layui-col-xs12 layui-col-sm6 layui-text-right">
<select id="filterStatus" class="layui-select layui-btn-sm">
<option value="">全部</option>
<option value="published">已审核</option>
<option value="pending">待审核</option>
</select>
<select id="filterDate" class="layui-select layui-btn-sm">
<option value="">所有时间</option>
<option value="today">今日发表</option>
<option value="latest">最新发布</option>
</select>
</div>
</div>
<table id="articleTable" lay-filter="articleTable">
</table>
<script type="text/html" id="actionToolbar">
<button class="layui-btn layui-btn-sm layui-btn-normal" lay-event="edit">修改</button>
<button class="layui-btn layui-btn-sm layui-btn-danger" lay-event="delete">删除</button>
<button class="layui-btn layui-btn-sm layui-btn-warm" lay-event="audit">审核</button>
</script>
<div class="stats-container">
<p>总文章数量: <span id="totalCount">0</span></p>
<p>今日发表数量: <span id="todayCount">0</span></p>
<p>待审核数量: <span id="pendingCount">0</span></p>
</div>
</div>
{% verbatim %}
<script type="text/html" id="chineseTitleTemplate">
<a href="/articles/{{ d.id }}/zh">{{ d.chinese_title }}</a>
</script>
<script type="text/html" id="englishTitleTemplate">
<a href="/articles/{{ d.id }}/en">{{ d.english_title }}</a>
</script>
{% endverbatim %}
<script>
layui.use(['jquery', 'table', 'layer'], function() {
const $ = layui.jquery;
const table = layui.table;
const layer = layui.layer;
function loadArticleList() {
axios.post('/custom_admin/home/article-list/')
.then(res => {
if (res.data.code === 200) {
const articleList = res.data.data;
table.render({
elem: '#articleTable',
data: articleList,
cols: [[
{field: 'chinese_title', title: '中文标题', width: '35%', toolbar: '#chineseTitleTemplate'},
{field: 'english_title', title: '英文标题', width: '35%', toolbar: '#englishTitleTemplate'},
{field: 'published_at', title: '发布时间', width: '10%'},
{ title: '操作',
width: '20%',
templet: function(d) {
let buttons = `
<button class="layui-btn layui-btn-sm layui-btn-normal" lay-event="edit">修改</button>
<button class="layui-btn layui-btn-sm layui-btn-danger" lay-event="delete">删除</button>
`;
if (d.status === 'pending') {
buttons += `<button class="layui-btn layui-btn-sm layui-btn-warm" lay-event="audit">审核</button>`;
}
return buttons;
}}
]],
done: function() {
$('#totalCount').text(articleList.length);
$('#todayCount').text(articleList.filter(a => new Date(a.published_at).toDateString() === new Date().toDateString()).length);
$('#pendingCount').text(articleList.filter(a => a.status === 'pending').length);
}
});
} else {
layer.msg('加载文章失败: ' + res.data.message);
}
})
.catch(() => {
layer.msg('请求失败');
});
}
$('#addArticle').on('click', function() {
openArticleForm();
});
table.on('tool(articleTable)', function(obj) {
const data = obj.data;
const layEvent = obj.event;
if (layEvent === 'edit') {
openArticleForm(data.id);
} else if (layEvent === 'delete') {
deleteArticle(data.id);
} else if (layEvent === 'audit') {
auditArticle(data.id);
}
});
function openArticleForm(id = null) {
console.log(id)
layer.open({
type: 2,
title: id ? "编辑文章" : "添加文章",
area: ['90%', '90%'],
content: id ? `save-article/${id}/` : 'save-article/',
end: loadArticleList
});
}
function deleteArticle(id) {
layer.confirm('确定删除这篇文章吗?', function(index) {
axios.delete(`/api/delete-article/${id}/`)
.then(res => {
if (res.data.code === 200) {
layer.msg('删除成功');
loadArticleList();
} else {
layer.msg('删除失败: ' + res.data.message);
}
})
.catch(() => {
layer.msg('请求失败');
});
layer.close(index);
});
}
function auditArticle(id) {
layer.confirm('确定审核这篇文章吗?', function(index) {
axios.post(`/api/audit-article/${id}/`)
.then(res => {
if (res.data.code === 200) {
layer.msg('审核成功');
loadArticleList();
} else {
layer.msg('审核失败: ' + res.data.message);
}
})
.catch(() => {
layer.msg('请求失败');
});
layer.close(index);
});
}
// function previewArticle(id,language) {
// // window.location.href = `/articles/${id}/${language}`;
// }
loadArticleList();
});
</script>
</body>
</html>

View File

@@ -0,0 +1,181 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
<meta name="keywords" content="{{ keywords }}">
<meta name="description" content="{{ content|truncatewords:20 }}">
<style>
/* 整体页面布局 */
body {
background-color: #1a1a1a;
color: #f0f0f0;
font-family: 'Helvetica Neue', Arial, sans-serif;
margin: 0;
padding: 0;
}
h1, h2, h3, p {
margin: 0;
padding: 0;
}
/* 顶部导航 */
.layui-container {
width: 100%;
background-color: #2d2d2d;
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.container a img {
max-height: 50px;
}
.layui-btn-primary {
background-color: #3a3a3a;
border-color: #565656;
color: #fff;
border-radius: 4px;
}
.layui-btn-primary:hover {
background-color: #565656;
border-color: #565656;
}
/* 文章内容区域 */
.content {
max-width: 960px;
margin: 40px auto;
padding: 20px;
background-color: #2d2d2d;
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
.article {
margin: 20px 0;
}
.article h1 {
font-size: 36px;
color: #00d1b2;
margin-bottom: 20px;
border-bottom: 2px solid #00d1b2;
padding-bottom: 10px;
}
.article p {
line-height: 1.8;
font-size: 18px;
color: #e0e0e0;
}
/* 发布时间样式 */
.article strong {
font-weight: bold;
color: #00d1b2;
}
.article p:first-of-type {
margin-top: 10px;
font-size: 14px;
color: #cccccc;
}
/* 链接美化 */
a {
color: #00d1b2;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* 底部样式 */
footer {
background-color: #2d2d2d;
padding: 30px 0;
color: #f0f0f0;
text-align: center;
font-size: 14px;
border-top: 1px solid #565656;
}
footer a {
color: #00d1b2;
}
footer a:hover {
text-decoration: underline;
}
footer .text-gray-300 {
margin: 10px 0;
}
footer .text-gray-300 a {
margin: 0 10px;
}
/* 响应式布局 */
@media (max-width: 768px) {
.layui-container {
flex-direction: column;
align-items: flex-start;
}
.content {
padding: 15px;
}
.article h1 {
font-size: 28px;
}
.article p {
font-size: 16px;
}
}
</style>
</head>
<body>
<!-- 顶部导航 -->
<div class="layui-container">
<div class="container">
<a href="/">
<img src="https://file.typeframes.com.cn/AI%E4%B8%AD%E6%96%87.png" alt="光映 logo" width="150">
</a>
</div>
</div>
<!-- 文章内容 -->
<div class="content">
<div class="article">
<h1>{{ title }}</h1>
<p><strong>发布时间:</strong>{{ published_at }}</p>
<p>{{ content }}</p>
</div>
</div>
<!-- 底部 -->
<footer class="layui-container">
<section class="content">
<div class="text-center text-gray-300">
© 2024 光映 | Illuminix<br>
<a href="https://www.typeframes.ai/" target="_blank" rel="noopener noreferrer">https://www.typeframes.ai/</a> |
<a href="https://www.typeframes.com.cn/" target="_blank" rel="noopener noreferrer">https://www.typeframes.com.cn/</a><br>
备案号桂ICP备2024038462号-3
</div>
<div class="text-center text-gray-300 mt-2">
<a href="/terms">服务条款</a> | <a href="/privacy">隐私政策</a>
</div>
</section>
</footer>
</body>
</html>

View File

@@ -1 +1,190 @@
1
{% load static %}
<html>
<head>
<meta charset="utf-8">
<title>后台管理系统</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://file.guimiaokeji.com/layui/css/layui.css">
<script src="https://file.guimiaokeji.com/layui/jquery-3.6.0.min.js"></script>
<script src="https://file.guimiaokeji.com/layui/layui.js"></script>
<script src="https://file.guimiaokeji.com/axios.js"></script>
<script src="https://file.guimiaokeji.com/qs.js"></script>
<style>
.layui-nav .layui-nav-item a:hover, .layui-nav .layui-this a {
background-color: #009688 !important;
}
.clickable-element:hover {
cursor: pointer;
}
/* 可以根据需要添加自定义样式 */
.layui-form-item {
margin-bottom: 20px;
}
.layui-form-label {
width: 120px;
}
.layui-input-block {
margin-left: 140px;
}
</style>
</head>
<body>
<script>
var qs = Qs
</script>
<div class="layui-layout layui-layout-admin">
<div class="layui-header">
<div class="layui-logo layui-hide-xs layui-bg-black nev_list clickable-element" >后台主页</div>
<ul class="layui-nav layui-layout-right">
<li class="layui-nav-item">
<a href="javascript:;">
<img src="http://layui.apixx.net/other/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" class="layui-nav-img">
管理员
</a>
<dl class="layui-nav-child">
<dd><a href="javascript:void(0);" id="change-password">修改信息</a></dd>
<dd><a href="/custom_admin/logout">退出登录</a></dd>
</dl>
</li>
</ul>
</div>
<div class="layui-side layui-bg-black">
<div class="layui-side-scroll">
<ul class="layui-nav layui-nav-tree" lay-filter="test">
<li class="layui-nav-item layui-this admin-index" data-url="site-info"><a href="#site-info">网站信息</a></li>
<li class="layui-nav-item" data-url="user-list"><a href="#user-list">用户列表</a></li>
<li class="layui-nav-item" data-url="order-list"><a href="#order-list">订单列表</a></li>
<li class="layui-nav-item" data-url="task-list"><a href="#task-list">任务列表</a></li>
<li class="layui-nav-item" data-url="article-list"><a href="#article-list">文章列表</a></li>
<li class="layui-nav-item" data-url="site-log"><a href="#site-log">网站日志</a></li>
</ul>
</div>
</div>
<div class="layui-body">
<div class="layui-content" id="content">
</div>
</div>
</div>
<!-- 弹出层表单 -->
<div id="passwordModal" style="display: none; padding: 20px;">
<form class="layui-form" id="changePasswordForm">
<div class="layui-form-item">
<label class="layui-form-label">旧密码</label>
<div class="layui-input-block">
<input type="password" name="old_password" required lay-verify="required" placeholder="请输入旧密码" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">新密码</label>
<div class="layui-input-block">
<input type="password" name="new_password" required lay-verify="required" placeholder="请输入新密码" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="changePassword">确认修改</button>
</div>
</div>
</form>
</div>
<script>
//JS
layui.use(['element', 'layer', 'form'], function(){
var element = layui.element;
var layer = layui.layer;
var form = layui.form;
var $ = layui.$;
// 点击 "修改密码" 按钮,打开弹出层
document.getElementById("change-password").addEventListener("click", function() {
layer.open({
type: 1, // 页面层
title: '修改密码',
area: ['600px', '350px'], // 宽高
content: $('#passwordModal'),
success: function () {
// 重置表单
// 提交修改密码表单
form.on('submit(changePassword)', function(data) {
// 使用 axios 发送 POST 请求
axios.post('/custom_admin/change-password/', data.field)
.then(function (response) {
if (response.data.code === 200) {
layer.msg('密码修改成功', {icon: 1});
layer.closeAll(); // 关闭弹出层
} else {
layer.msg(response.data.msg, {icon: 2});
}
})
.catch(function (error) {
layer.msg('请求失败,请稍后再试', {icon: 2});
});
return false; // 阻止表单跳转。如果需要表单跳转,去掉这句即可。
});
}
});
});
});
$(document).ready(function () {
function loadContent(url) {
// 使用 AJAX 加载内容
$.get(url, function (data) { // 假设同名文件是 .html 格式
$('#content').html(data);
console.log(url);
});
}
// 在 localStorage 中存储当前 URL
function saveActiveMenuUrl(url) {
localStorage.setItem('activeMenuUrl', url);
}
// 从 localStorage 中获取当前 URL
function getActiveMenuUrl() {
return localStorage.getItem('activeMenuUrl');
}
$('.nev_list').on('click', function () {
$('.layui-nav-tree .layui-nav-item, .nev_list').removeClass('layui-this');
$(this).addClass('layui-this');
var url = $(this).data('url');
loadContent(url);
saveActiveMenuUrl(url);
});
$('.layui-nav-tree .layui-nav-item').on('click', function () {
$('.layui-nav-tree .layui-nav-item, .nev_list').removeClass('layui-this');
$(this).addClass('layui-this');
var url = $(this).data('url');
loadContent(url);
saveActiveMenuUrl(url);
});
// 默认加载上次激活的菜单项的内容,如果没有则加载第一个菜单项
var activeMenuUrl = getActiveMenuUrl();
if (activeMenuUrl) {
loadContent(activeMenuUrl);
$('.layui-nav-tree .layui-nav-item[data-url="' + activeMenuUrl + '"]').addClass('layui-this');
} else {
$('.admin-index').trigger('click'); // 触发第一个菜单项("后台主页")的点击事件
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,137 @@
<style>
body {
background-color: #f5f5f5;
}
.layui-container {
width: 100% !important;
padding: 20px;
background-color: #ffffff;
}
h2 {
color: #333;
font-size: 24px;
}
.layui-table {
width: 100%;
margin-top: 20px;
}
.layui-table tbody tr:nth-child(odd) {
background-color: #f9f9f9;
}
.layui-table tbody tr:nth-child(even) {
background-color: #eaf6f6;
}
.stats-container {
display: flex;
align-items: center;
margin-top: 20px;
}
.stats-container p {
margin: 0 20px;
font-size: 14px;
}
.stats-container span {
color: #2de5cf;
}
</style>
</head>
<body>
<div class="layui-container">
<h2>订单列表</h2>
<table id="orderTable" lay-filter="orderTable"></table>
<div class="stats-container">
<p>总订单数量: <span id="totalCount">0</span></p>
<p>支付宝订单数量: <span id="alipayCount">0</span>--¥:<span id="alipaySum"></span>人民币</p>
<p>贝宝订单数量: <span id="paypalCount">0</span>--$<span id="paypalSum"></span>美金</p>
<p>待处理订单: <span id="pendingCount">0</span></p>
<p>已完成订单: <span id="completedCount">0</span></p>
<p>已取消订单: <span id="canceledCount">0</span></p>
<p>失败订单: <span id="failedCount">0</span></p>
</div>
</div>
<script>
layui.use(['jquery', 'table', 'layer'], function() {
const $ = layui.jquery;
const table = layui.table;
const layer = layui.layer;
// 加载订单列表
function loadOrderList() {
table.render({
elem: '#orderTable',
url: '/custom_admin/home/order-list/',
method: 'post',
page: true, // 开启分页
limit: 10, // 每页显示的数量
cols: [[
{field: 'order_id', title: '订单号', width: '20%'},
{field: 'username', title: '用户名', width: '15%'},
{field: 'plan__title', title: '购买套餐', width: '10%'},
{field: 'amount', title: '金额', width: '10%', templet: function(d) {
// 根据支付方式设置金额前面的符号
return d.payment_method === '支付宝' ? `¥${d.amount}` : `$${d.amount}`;
}},
{field: 'payment_method', title: '支付方式', width: '10%'},
{field: 'status', title: '订单状态', width: '10%'},
{field: 'created_at', title: '创建时间', width: '15%'},
{title: '操作', width: '10%', toolbar: '#actionToolbar'}
]],
done: function(res) {
$('#totalCount').text(res.count); // 更新总订单数
res.payment_method_count.forEach(item => {
if (item.payment_method === 'alipay') {
$('#alipayCount').text(item.count);
} else if (item.payment_method === 'paypal') {
$('#paypalCount').text(item.count);
}
});
$('#alipaySum').text(res.total_income_by_payment_method[0].total_income);
$('#paypalSum').text(res.total_income_by_payment_method[1].total_income);
$('#pendingCount').text(res.status_count.pending);
$('#completedCount').text(res.status_count.completed);
$('#canceledCount').text(res.status_count.canceled);
$('#failedCount').text(res.status_count.failed);
}
});
}
// 删除订单功能
table.on('tool(orderTable)', function(obj) {
const data = obj.data;
const layEvent = obj.event;
if (layEvent === 'delete') {
deleteOrder(data.id);
}
});
function deleteOrder(id) {
layer.confirm('确定删除该订单吗?', function(index) {
$.post(`/api/delete-order/${id}/`, function(res) {
if (res.code === 200) {
layer.msg('删除成功');
loadOrderList();
} else {
layer.msg('删除失败: ' + res.message);
}
});
layer.close(index);
});
}
loadOrderList();
});
</script>
<script type="text/html" id="actionToolbar">
<button class="layui-btn layui-btn-sm layui-btn-danger" lay-event="delete">删除</button>
</script>

View File

@@ -0,0 +1,142 @@
{% load static %}
{% block content %}
<style>
/* 顶部颜色 */
.layui-card-header {
background-color: #f0f8ff; /* 浅蓝色 */
color: #333; /* 字体颜色 */
}
/* 输入框样式 */
.layui-input {
background-color: #e6f7ff; /* 浅蓝色(英文) */
border: 1px solid #a6c8ff; /* 边框颜色 */
}
.layui-textarea {
background-color: #e6f7ff; /* 浅蓝色(英文) */
border: 1px solid #a6c8ff; /* 边框颜色 */
}
/* 中文输入框样式 */
.layui-input[name^="domain_zh"], .layui-input[name^="title_zh"],
.layui-textarea[name^="keywords_zh"], .layui-textarea[name^="description_zh"] {
background-color: #ffe6e6; /* 浅红色(中文) */
border: 1px solid #ffb3b3; /* 边框颜色 */
}
.layui-form-item {
margin-bottom: 20px;
}
.layui-form-label {
width: 120px;
text-align: right; /* 居中标签 */
}
.layui-input-block {
margin-left: 140px;
}
.layui-textarea {
height: 80px; /* 调整文本域高度 */
}
</style>
<div class="layui-card">
<div class="layui-card-header">网站信息</div>
<div class="layui-card-body">
<form class="layui-form" lay-filter="website-info-form">
{% csrf_token %}
<div class="layui-row">
<div class="layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">英文域名</label>
<div class="layui-input-block">
<input type="text" name="domain_en" value="{{ website_info.domain_en }}" required lay-verify="required" placeholder="请输入英文域名" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">英文标题</label>
<div class="layui-input-block">
<input type="text" name="title_en" value="{{ website_info.title_en }}" required lay-verify="required" placeholder="请输入英文标题" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">英文关键词</label>
<div class="layui-input-block">
<textarea name="keywords_en" required lay-verify="required" placeholder="请输入英文关键词" class="layui-textarea">{{ website_info.keywords_en }}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">英文描述</label>
<div class="layui-input-block">
<textarea name="description_en" required lay-verify="required" placeholder="请输入英文描述" class="layui-textarea">{{ website_info.description_en }}</textarea>
</div>
</div>
</div>
<div class="layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">中文域名</label>
<div class="layui-input-block">
<input type="text" name="domain_zh" value="{{ website_info.domain_zh }}" required lay-verify="required" placeholder="请输入中文域名" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">中文标题</label>
<div class="layui-input-block">
<input type="text" name="title_zh" value="{{ website_info.title_zh }}" required lay-verify="required" placeholder="请输入中文标题" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">中文关键词</label>
<div class="layui-input-block">
<textarea name="keywords_zh" required lay-verify="required" placeholder="请输入中文关键词" class="layui-textarea">{{ website_info.keywords_zh }}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">中文描述</label>
<div class="layui-input-block">
<textarea name="description_zh" required lay-verify="required" placeholder="请输入中文描述" class="layui-textarea">{{ website_info.description_zh }}</textarea>
</div>
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block" style="text-align: center;">
<button class="layui-btn" lay-submit lay-filter="save-website-info">保存修改</button>
</div>
</div>
</form>
</div>
</div>
<script>
layui.use(['form'], function(){
var form = layui.form;
// 监听提交
form.on('submit(save-website-info)', function(data){
console.log(data)
// 使用 Axios 提交表单数据
axios.post('site-info/', data.field) // 替换为你的 URL
.then(function(response) {
console.log(response)
// 处理成功的响应
if(response.data.code === 200) {
layer.msg('保存成功', {icon: 1});
} else {
layer.msg(response.data.message, {icon: 2});
}
})
.catch(function() {
layer.msg('服务器错误,请重试', {icon: 2});
});
return false; // 阻止表单跳转
});
});
</script>
{% endblock %}

View File

@@ -0,0 +1,108 @@
<style>
body {
background-color: #f5f5f5;
}
.layui-container {
width: 100% !important;
padding: 20px;
background-color: #ffffff;
}
h2 {
color: #333;
font-size: 24px;
}
.layui-table {
width: 100%;
margin-top: 20px;
}
.layui-table tbody tr:nth-child(odd) {
background-color: #f9f9f9;
}
.layui-table tbody tr:nth-child(even) {
background-color: #eaf6f6;
}
.stats-container {
display: flex;
align-items: center;
margin-top: 20px;
}
.stats-container p {
margin: 0 20px;
font-size: 14px;
}
.stats-container span {
color: #2de5cf;
}
</style>
</head>
<body>
<div class="layui-container">
<h2>任务列表</h2>
<table id="taskTable" lay-filter="taskTable"></table>
<div class="stats-container">
<p>总任务数量: <span id="totalCount">0</span></p>
<p>分类统计:
<span id="videoTypeCount"></span>
</p>
<p>状态统计:
成功: <span id="completedCount">0</span>
失败: <span id="failedCount">0</span>
进行中: <span id="inProgressCount">0</span>
</p>
</div>
</div>
{% verbatim %}
<script type="text/html" id="actionToolbar">
<a class="layui-btn layui-btn-sm" href="{{ d.video_url }}" target="_blank">查看视频</a>
</script>
{% endverbatim %}
<script>
layui.use(['jquery', 'table', 'layer'], function() {
const $ = layui.jquery;
const table = layui.table;
const layer = layui.layer;
// 加载任务列表
function loadTaskList() {
table.render({
elem: '#taskTable',
url: '/custom_admin/home/task-list/',
method: 'post',
page: true, // 开启分页
limit: 10, // 每页显示的数量
cols: [[
{field: 'video_id', title: '视频ID', width: '15%'},
{field: 'user_id', title: '用户ID', width: '10%'},
{field: 'text', title: '生成文本', width: '20%'},
{field: 'slug', title: '视频类型', width: '10%'},
{field: 'time_duration', title: '生成时长', width: '10%'},
{field: 'created_at', title: '生成时间', width: '15%'},
{field: 'status', title: '状态', width: '10%'},
{title: '操作', width: '10%', toolbar: '#actionToolbar'}
]],
done: function(res) {
// 更新总任务数
$('#totalCount').text(res.count);
// 分类统计(视频类型)
let videoTypeCount = '';
res.video_type_count.forEach(item => {
videoTypeCount += `${item.slug}: ${item.count} `;
});
$('#videoTypeCount').text(videoTypeCount);
// 状态统计
$('#completedCount').text(res.status_count.completed);
$('#failedCount').text(res.status_count.failed);
$('#inProgressCount').text(res.status_count.in_progress);
}
});
}
loadTaskList(); // 初始化加载任务列表
});
</script>

View File

@@ -0,0 +1,116 @@
<style>
body {
background-color: #f5f5f5;
}
.layui-container {
width: 100% !important;
padding: 20px;
background-color: #ffffff;
}
h2 {
color: #333;
font-size: 24px;
}
.layui-table {
width: 100%;
margin-top: 20px;
}
.layui-table tbody tr:nth-child(odd) {
background-color: #f9f9f9;
}
.layui-table tbody tr:nth-child(even) {
background-color: #eaf6f6;
}
.stats-container {
display: flex;
align-items: center;
margin-top: 20px;
}
.stats-container p {
margin: 0 20px;
font-size: 14px;
}
.stats-container span {
color: #2de5cf;
}
</style>
</head>
<body>
<div class="layui-container">
<h2>用户列表</h2>
<table id="userTable" lay-filter="userTable"></table>
<div class="stats-container">
<p>总用户数量: <span id="totalCount">0</span></p>
</div>
</div>
<script>
layui.use(['jquery', 'table', 'layer'], function() {
const $ = layui.jquery;
const table = layui.table;
const layer = layui.layer;
// 加载用户列表
function loadUserList() {
table.render({
elem: '#userTable',
url: '/custom_admin/home/user-list/',
method: 'post',
page: true, // 开启分页
limit: 10, // 每页显示的数量
cols: [[
{field: 'username', title: '用户名', width: '15%'},
{field: 'phone', title: '手机号', width: '15%'},
{field: 'email', title: '邮箱', width: '15%'},
{field: 'points', title: '积分', width: '10%'},
{field: 'created_at', title: '注册时间', width: '15%'},
{field: 'login_count', title: '登录次数', width: '10%'},
{field: 'source', title: '来源', width: '10%'},
{title: '操作', width: '10%', toolbar: '#actionToolbar'}
]],
done: function(res) {
$('#totalCount').text(res.count); // 更新总用户数
}
});
}
// 删除用户功能
table.on('tool(userTable)', function(obj) {
const data = obj.data;
const layEvent = obj.event;
if (layEvent === 'delete') {
deleteUser(data.id);
}
});
function deleteUser(id) {
layer.confirm('确定删除该用户吗?', function(index) {
axios.delete(`/api/delete-user/${id}/`)
.then(res => {
if (res.data.code === 200) {
layer.msg('删除成功');
loadUserList();
} else {
layer.msg('删除失败: ' + res.data.message);
}
})
.catch(() => {
layer.msg('请求失败');
});
layer.close(index);
});
}
loadUserList();
});
</script>
<script type="text/html" id="actionToolbar">
<button class="layui-btn layui-btn-sm layui-btn-danger" lay-event="delete">删除</button>
</script>