2.0
This commit is contained in:
262
templates/admin/add-article.html
Normal file
262
templates/admin/add-article.html
Normal 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"></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>
|
||||
@@ -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>
|
||||
|
||||
217
templates/admin/article-list.html
Normal file
217
templates/admin/article-list.html
Normal 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>
|
||||
181
templates/admin/article_detail.html
Normal file
181
templates/admin/article_detail.html
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
137
templates/admin/order-list.html
Normal file
137
templates/admin/order-list.html
Normal 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>
|
||||
|
||||
142
templates/admin/site-info.html
Normal file
142
templates/admin/site-info.html
Normal 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 %}
|
||||
108
templates/admin/task-list.html
Normal file
108
templates/admin/task-list.html
Normal 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>
|
||||
116
templates/admin/user-list.html
Normal file
116
templates/admin/user-list.html
Normal 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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user