develop
zhangyu 3 months ago
parent 77b4835a47
commit 6639ec668c

@ -0,0 +1,73 @@
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect, get_object_or_404
from django.views.generic.edit import CreateView
from django.urls import reverse_lazy
from .models import Article, Category, Tag, ArticleImage
from .forms import ArticleForm
class ArticleCreateView(LoginRequiredMixin, CreateView):
"""创建文章视图"""
model = Article
form_class = ArticleForm
template_name = 'blog/article_create.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['all_categories'] = Category.objects.all()
context['all_tags'] = Tag.objects.all()
return context
def form_valid(self, form):
# 先不保存表单,先处理分类
form.instance.author = self.request.user
# 处理分类
category_name = form.cleaned_data['category']
category, created = Category.objects.get_or_create(
name=category_name,
defaults={'parent_category': None}
)
# 直接设置分类对象,而不是名称
form.instance.category = category
# 先保存文章实例,以便可以添加多对多关系
# 但在保存前先从表单中移除category字段避免表单尝试保存它
category_temp = form.cleaned_data.pop('category')
tags_temp = form.cleaned_data.pop('tags')
response = super().form_valid(form)
# 处理标签
if tags_temp:
tag_names = [tag.strip() for tag in tags_temp.split(',') if tag.strip()]
for tag_name in tag_names:
tag, created = Tag.objects.get_or_create(name=tag_name)
form.instance.tags.add(tag)
# 关联临时图片到新创建的文章
# 获取会话中的临时图片ID列表
temp_image_ids = self.request.session.get('temp_image_ids', [])
# 查找这些临时图片
temp_images = ArticleImage.objects.filter(id__in=temp_image_ids)
# 将这些图片关联到新创建的文章
for image in temp_images:
image.article = form.instance
image.save()
# 清除会话中的临时图片ID列表
self.request.session['temp_image_ids'] = []
self.request.session.modified = True
return response
def get_success_url(self):
return reverse_lazy('blog:detailbyid', kwargs={
'article_id': self.object.id,
'year': self.object.creation_time.year,
'month': self.object.creation_time.month,
'day': self.object.creation_time.day
})

@ -0,0 +1,46 @@
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect, get_object_or_404
from django.views.generic.edit import DeleteView
from django.urls import reverse_lazy
from .models import Article, Category, Tag, ArticleImage
class ArticleDeleteView(LoginRequiredMixin, DeleteView):
"""删除文章视图"""
model = Article
template_name = 'blog/article_delete_confirm.html'
pk_url_kwarg = 'article_id'
def dispatch(self, request, *args, **kwargs):
obj = self.get_object()
# 只有文章作者或管理员可以删除
if obj.author != request.user and not request.user.is_superuser:
return redirect('blog:detailbyid',
article_id=obj.id,
year=obj.creation_time.year,
month=obj.creation_time.month,
day=obj.creation_time.day)
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['all_categories'] = Category.objects.all()
context['all_tags'] = Tag.objects.all()
return context
def delete(self, request, *args, **kwargs):
# 删除文章前先删除所有相关图片
self.object = self.get_object()
images = ArticleImage.objects.filter(article=self.object)
for image in images:
# 删除图片文件
image.image.delete()
# 删除图片记录
image.delete()
# 调用父类的delete方法删除文章
return super().delete(request, *args, *kwargs)
def get_success_url(self):
return reverse_lazy('blog:index')

@ -0,0 +1,101 @@
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect, get_object_or_404
from django.views.generic.edit import UpdateView
from django.urls import reverse_lazy
from .models import Article, Category, Tag, ArticleImage
from .forms import ArticleForm
class ArticleUpdateView(LoginRequiredMixin, UpdateView):
"""更新文章视图"""
model = Article
form_class = ArticleForm
template_name = 'blog/article_edit.html'
pk_url_kwarg = 'article_id'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['all_categories'] = Category.objects.all()
context['all_tags'] = Tag.objects.all()
return context
def get_initial(self):
initial = super().get_initial()
# 设置分类初始值
if self.object.category:
initial['category'] = self.object.category.name
# 设置标签初始值
if self.object.tags.exists():
tag_names = [tag.name for tag in self.object.tags.all()]
initial['tags'] = ', '.join(tag_names)
return initial
def dispatch(self, request, *args, **kwargs):
obj = self.get_object()
# 只有文章作者或管理员可以编辑
if obj.author != request.user and not request.user.is_superuser:
return redirect('blog:detailbyid',
article_id=obj.id,
year=obj.creation_time.year,
month=obj.creation_time.month,
day=obj.creation_time.day)
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):
# 先不保存表单,先处理分类
form.instance.author = self.request.user
# 处理分类
category_name = form.cleaned_data['category']
category, created = Category.objects.get_or_create(
name=category_name,
defaults={'parent_category': None}
)
# 直接设置分类对象,而不是名称
form.instance.category = category
# 先保存文章实例,以便可以添加多对多关系
# 但在保存前先从表单中移除category字段避免表单尝试保存它
category_temp = form.cleaned_data.pop('category')
tags_temp = form.cleaned_data.pop('tags')
response = super().form_valid(form)
# 处理标签
if tags_temp:
tag_names = [tag.strip() for tag in tags_temp.split(',') if tag.strip()]
form.instance.tags.clear() # 清除现有标签
for tag_name in tag_names:
tag, created = Tag.objects.get_or_create(name=tag_name)
form.instance.tags.add(tag)
else:
form.instance.tags.clear() # 如果没有标签,清除所有标签
# 关联临时图片到文章
# 获取会话中的临时图片ID列表
temp_image_ids = self.request.session.get('temp_image_ids', [])
# 查找这些临时图片
temp_images = ArticleImage.objects.filter(id__in=temp_image_ids)
# 将这些图片关联到文章
for image in temp_images:
image.article = form.instance
image.save()
# 清除会话中的临时图片ID列表
self.request.session['temp_image_ids'] = []
self.request.session.modified = True
return response
def get_success_url(self):
return reverse_lazy('blog:detailbyid', kwargs={
'article_id': self.object.id,
'year': self.object.creation_time.year,
'month': self.object.creation_time.month,
'day': self.object.creation_time.day
})

@ -3,7 +3,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect, get_object_or_404
from django.views.generic.edit import CreateView, UpdateView
from django.urls import reverse_lazy
from .models import Article, Category, Tag
from .models import Article, Category, Tag, ArticleImage
from .forms import ArticleForm

@ -0,0 +1,153 @@
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect, get_object_or_404
from django.views.generic.edit import CreateView, UpdateView
from django.urls import reverse_lazy
from .models import Article, Category, Tag, ArticleImage
from .forms import ArticleForm
class ArticleCreateView(LoginRequiredMixin, CreateView):
"""创建文章视图"""
model = Article
form_class = ArticleForm
template_name = 'blog/article_create.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['all_categories'] = Category.objects.all()
context['all_tags'] = Tag.objects.all()
return context
def form_valid(self, form):
# 先不保存表单,先处理分类
form.instance.author = self.request.user
# 处理分类
category_name = form.cleaned_data['category']
category, created = Category.objects.get_or_create(
name=category_name,
defaults={'parent_category': None}
)
# 直接设置分类对象,而不是名称
form.instance.category = category
# 先保存文章实例,以便可以添加多对多关系
# 但在保存前先从表单中移除category字段避免表单尝试保存它
category_temp = form.cleaned_data.pop('category')
tags_temp = form.cleaned_data.pop('tags')
response = super().form_valid(form)
# 处理标签
if tags_temp:
tag_names = [tag.strip() for tag in tags_temp.split(',') if tag.strip()]
for tag_name in tag_names:
tag, created = Tag.objects.get_or_create(name=tag_name)
form.instance.tags.add(tag)
# 处理临时上传的图片
temp_image_ids = self.request.session.get('temp_image_ids', [])
if temp_image_ids:
# 关联这些图片到当前文章
ArticleImage.objects.filter(id__in=temp_image_ids).update(article=self.object)
# 清除会话中的临时图片ID列表
self.request.session['temp_image_ids'] = []
self.request.session.modified = True
return response
def get_success_url(self):
return reverse_lazy('blog:detailbyid', kwargs={
'article_id': self.object.id,
'year': self.object.creation_time.year,
'month': self.object.creation_time.month,
'day': self.object.creation_time.day
})
class ArticleUpdateView(LoginRequiredMixin, UpdateView):
"""更新文章视图"""
model = Article
form_class = ArticleForm
template_name = 'blog/article_edit.html'
pk_url_kwarg = 'article_id'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['all_categories'] = Category.objects.all()
context['all_tags'] = Tag.objects.all()
return context
def get_initial(self):
initial = super().get_initial()
# 设置分类初始值
if self.object.category:
initial['category'] = self.object.category.name
# 设置标签初始值
if self.object.tags.exists():
tag_names = [tag.name for tag in self.object.tags.all()]
initial['tags'] = ', '.join(tag_names)
return initial
def dispatch(self, request, *args, **kwargs):
obj = self.get_object()
# 只有文章作者或管理员可以编辑
if obj.author != request.user and not request.user.is_superuser:
return redirect('blog:detailbyid',
article_id=obj.id,
year=obj.creation_time.year,
month=obj.creation_time.month,
day=obj.creation_time.day)
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):
# 先不保存表单,先处理分类
form.instance.author = self.request.user
# 处理分类
category_name = form.cleaned_data['category']
category, created = Category.objects.get_or_create(
name=category_name,
defaults={'parent_category': None}
)
# 直接设置分类对象,而不是名称
form.instance.category = category
# 先保存文章实例,以便可以添加多对多关系
# 但在保存前先从表单中移除category字段避免表单尝试保存它
category_temp = form.cleaned_data.pop('category')
tags_temp = form.cleaned_data.pop('tags')
response = super().form_valid(form)
# 处理标签
if tags_temp:
tag_names = [tag.strip() for tag in tags_temp.split(',') if tag.strip()]
form.instance.tags.clear() # 清除现有标签
for tag_name in tag_names:
tag, created = Tag.objects.get_or_create(name=tag_name)
form.instance.tags.add(tag)
else:
form.instance.tags.clear() # 如果没有标签,清除所有标签
# 处理临时上传的图片
temp_image_ids = self.request.session.get('temp_image_ids', [])
if temp_image_ids:
# 关联这些图片到当前文章
ArticleImage.objects.filter(id__in=temp_image_ids).update(article=self.object)
# 清除会话中的临时图片ID列表
self.request.session['temp_image_ids'] = []
self.request.session.modified = True
return response
def get_success_url(self):
return reverse_lazy('blog:detailbyid', kwargs={
'article_id': self.object.id,
'year': self.object.creation_time.year,
'month': self.object.creation_time.month,
'day': self.object.creation_time.day
})

@ -49,38 +49,47 @@ def markdown_image_upload(request):
"""为Markdown编辑器提供的图片上传接口"""
if request.method == 'POST' and request.FILES.get('image'):
image_file = request.FILES['image']
description = request.POST.get('description', '')
article_id = request.POST.get('article_id')
try:
# 创建一个默认文章对象(如果需要的话)
# 或者使用一个特殊的标记值来表示这是一个临时图片
from blog.models import Article
default_article = Article.objects.filter(author=request.user).first()
if not default_article:
# 如果用户没有任何文章,创建一个临时的草稿文章
default_article = Article(
title="临时图片存储",
body="",
status="d", # 草稿状态
author=request.user
)
default_article.save()
# 创建图片对象,关联到默认文章
# 获取会话中的临时图片ID列表
temp_image_ids = request.session.get('temp_image_ids', [])
# 创建图片对象,但不立即关联到文章
temp_image = ArticleImage(
image=image_file,
article=default_article,
description="Markdown编辑器上传"
description=description or "Markdown编辑器上传"
)
# 如果有文章ID尝试关联到文章
if article_id:
try:
from blog.models import Article
article = Article.objects.get(id=article_id, author=request.user)
temp_image.article = article
except Article.DoesNotExist:
# 文章不存在,不关联
temp_image.article_id = None
else:
# 没有文章ID保存为临时图片
temp_image.article_id = None
# 保存图片
temp_image.save()
# 如果是临时图片将图片ID添加到会话中的临时图片列表
if not temp_image.article:
temp_image_ids.append(temp_image.id)
request.session['temp_image_ids'] = temp_image_ids
request.session.modified = True
# 返回Markdown编辑器需要的格式
return JsonResponse({
'success': 1,
'message': '上传成功',
'url': temp_image.image.url
'url': temp_image.image.url,
'image_id': temp_image.id # 添加图片ID以便后续可以删除
})
except Exception as e:
# 记录错误并返回失败信息

@ -0,0 +1,24 @@
# Generated by Django
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('blog', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='articleimage',
name='article',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=models.deletion.CASCADE,
related_name='images',
to='blog.article'
),
),
]

@ -0,0 +1,14 @@
# Generated by Django 4.2.14 on 2025-11-20 22:28
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('blog', '0002_auto_20231201_1200'),
('blog', '0007_articleimage'),
]
operations = [
]

@ -306,8 +306,8 @@ class SideBar(models.Model):
class ArticleImage(models.Model):
"""文章图片模型"""
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='images')
image = models.ImageField(_('图片'), upload_to='article_images/%Y/%m/%d/')
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='images', null=True, blank=True)
image = models.ImageField(_('图片'), upload_to='images/')
description = models.CharField(_('图片描述'), max_length=255, blank=True)
created_time = models.DateTimeField(_('创建时间'), auto_now_add=True)

@ -3,6 +3,8 @@ from django.views.decorators.cache import cache_page
from . import views
from . import article_views
from .article_views_new import ArticleCreateView, ArticleUpdateView
from .article_delete_view import ArticleDeleteView
from . import image_views
app_name = "blog"
@ -21,12 +23,16 @@ urlpatterns = [
name='detailbyid'),
path(
r'article/create/',
article_views.ArticleCreateView.as_view(),
ArticleCreateView.as_view(),
name='article_create'),
path(
r'article/edit/<int:article_id>/',
article_views.ArticleUpdateView.as_view(),
ArticleUpdateView.as_view(),
name='article_edit'),
path(
r'article/delete/<int:article_id>/',
ArticleDeleteView.as_view(),
name='article_delete'),
path(
r'my-articles/',
views.my_articles,

@ -150,6 +150,11 @@ class ArticleDetailView(DetailView):
kwargs['next_article'] = self.object.next_article
kwargs['prev_article'] = self.object.prev_article
# 获取文章关联的图片
from .models import ArticleImage
article_images = ArticleImage.objects.filter(article=self.object)
kwargs['article_images'] = article_images
context = super(ArticleDetailView, self).get_context_data(**kwargs)
article = self.object
@ -414,7 +419,27 @@ class ArticleCreateView(LoginRequiredMixin, CreateView):
tag, created = Tag.objects.get_or_create(name=tag_name)
form.instance.tags.add(tag)
return super().form_valid(form)
# 先保存文章,以便可以关联图片
response = super().form_valid(form)
# 将临时图片关联到新创建的文章
from .models import ArticleImage
# 获取会话中的临时图片ID列表
temp_image_ids = self.request.session.get('temp_image_ids', [])
# 查找这些临时图片
temp_images = ArticleImage.objects.filter(id__in=temp_image_ids)
# 将这些图片关联到新创建的文章
for image in temp_images:
image.article = form.instance
image.save()
# 清除会话中的临时图片ID列表
self.request.session['temp_image_ids'] = []
self.request.session.modified = True
return response
def get_success_url(self):
return reverse_lazy('blog:detailbyid', kwargs={

@ -355,8 +355,53 @@ STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesSto
COMPRESS_URL = STATIC_URL
COMPRESS_ROOT = STATIC_ROOT
MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'images')
MEDIA_URL = '/images/'
# MDEDITOR配置
MDEDITOR_CONFIGS = {
'default': {
'width': '100%', # 编辑器宽度
'height': 500, # 编辑器高度
'toolbar': ["undo", "redo", "|",
"bold", "del", "italic", "quote", "ucwords", "uppercase", "lowercase", "|",
"h1", "h2", "h3", "h5", "h6", "|",
"list-ul", "list-ol", "hr", "|",
"link", "reference-link", "image", "code", "preformatted-text", "code-block", "table", "datetime", "emoji", "html-entities", "pagebreak", "|",
"goto-line", "watch", "preview", "fullscreen", "clear", "search", "|",
"help", "info"
],
'upload_image_formats': ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
'image_folder': 'images',
'theme': 'default', # dark / default
'preview_theme': 'default', # dark / default
'editor_theme': 'default', # pastel-on-dark / dark
'toolbar_autofixed': True,
'search_place': 'top', # top / bottom
'language': 'zh-cn', # 语言
'line_numbers': True, # 显示行号
'tab_size': 4, # Tab缩进
'tex': True, # 科学公式TeX支持
'flow_chart': True, # 流程图支持
'sequence': True, # 时序图支持
'mind_map': True, # 脑图支持
'inline_break': True, # 换行符
'line_wrapping': True, # 自动换行
'auto_save': 1000, # 自动保存时间间隔(ms)
'save_html_to textarea': True, # 保存HTML到文本框
'html_decode': True, # HTML解码
'emoji': True, # 表情
'task_list': True, # 任务列表
'katex': True, # 科学公式KaTeX支持
'toc': True, # 目录
'at_link': True, # @link
'email_link': True, # email链接
'image_upload': True, # 图片上传
'image_upload_url': '/mdeditor/upload/', # 图片上传URL
'image_path': 'images/' # 图片上传路径
}
}
X_FRAME_OPTIONS = 'SAMEORIGIN'
# 安全头部配置 - 防XSS和其他攻击

@ -73,6 +73,8 @@ urlpatterns += i18n_patterns(
re_path(r'', include('servermanager.urls', namespace='servermanager')),
re_path(r'', include('owntracks.urls', namespace='owntracks'))
, prefix_default_language=False) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT)

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

File diff suppressed because it is too large Load Diff

@ -8,6 +8,8 @@
{{ block.super }}
<!-- 添加Bootstrap的CSS和JavaScript -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
{% endblock %}
{% block content %}
@ -126,6 +128,8 @@
</div>
</div>
{% block extra_js %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/js/all.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const uploadImageBtn = document.getElementById('upload-image-btn');
@ -188,6 +192,16 @@ document.addEventListener('DOMContentLoaded', function() {
document.getElementById('confirm-upload').addEventListener('click', function() {
const form = document.getElementById('image-form');
const formData = new FormData(form);
// 如果正在编辑文章添加文章ID
const articleForm = document.querySelector('form[method="post"]');
if (articleForm && articleForm.action.includes('edit')) {
// 从URL中提取文章ID
const articleId = window.location.pathname.match(/\/edit\/(\d+)\//);
if (articleId && articleId[1]) {
formData.append('article_id', articleId[1]);
}
}
fetch("{% url 'blog:markdown_image_upload' %}", {
method: 'POST',

@ -0,0 +1,56 @@
{% extends "share_layout/base.html" %}
{% load static %}
{% load i18n %}
{% block title %}{% trans "Delete Article" %}{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header bg-danger text-white">
<h2>{% trans "Delete Article" %}: {{ article.title }}</h2>
</div>
<div class="card-body">
<div class="alert alert-warning">
<h4>{% trans "Warning" %}</h4>
<p>{% trans "You are about to delete this article. This action cannot be undone." %}</p>
<p>{% trans "All associated images will also be deleted." %}</p>
</div>
<div class="mb-3">
<h5>{% trans "Article Details" %}</h5>
<ul>
<li><strong>{% trans "Title" %}:</strong> {{ article.title }}</li>
<li><strong>{% trans "Created" %}:</strong> {{ article.creation_time|date:"Y-m-d H:i" }}</li>
<li><strong>{% trans "Modified" %}:</strong> {{ article.last_modify_time|date:"Y-m-d H:i" }}</li>
<li><strong>{% trans "Category" %}:</strong> {{ article.category.name }}</li>
<li><strong>{% trans "Tags" %}:</strong>
{% for tag in article.tags.all %}
{{ tag.name }}{% if not forloop.last %}, {% endif %}
{% empty %}
{% trans "None" %}
{% endfor %}
</li>
</ul>
</div>
<form method="post">
{% csrf_token %}
<input type="hidden" name="confirm" value="true">
<div class="mb-3">
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash"></i> {% trans "Confirm Delete" %}
</button>
<a href="{{ article.get_absolute_url }}" class="btn btn-secondary">
<i class="fas fa-times"></i> {% trans "Cancel" %}
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

@ -8,6 +8,8 @@
{{ block.super }}
<!-- 添加Bootstrap的CSS和JavaScript -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
{% endblock %}
{% block content %}
@ -137,6 +139,10 @@
<div class="mb-3">
<button type="submit" class="btn btn-primary">{% trans "更新" %}</button>
<a href="{{ article.get_absolute_url }}" class="btn btn-secondary">{% trans "取消" %}</a>
<a href="{% url 'blog:article_delete' article_id=article.id %}" class="btn btn-danger float-end"
onclick="return confirm('确定要删除这篇文章吗?此操作不可恢复!')">
<i class="fas fa-trash"></i> {% trans "删除文章" %}
</a>
</div>
</form>
</div>
@ -144,6 +150,7 @@
</div>
{% block extra_js %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/js/all.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const uploadImageBtn = document.getElementById('upload-image-btn');
@ -205,6 +212,12 @@ document.addEventListener('DOMContentLoaded', function() {
document.getElementById('confirm-upload').addEventListener('click', function() {
const form = document.getElementById('image-form');
const formData = new FormData(form);
// 添加当前文章ID
const articleId = window.location.pathname.match(/\/edit\/(\d+)\//);
if (articleId && articleId[1]) {
formData.append('article_id', articleId[1]);
}
fetch("{% url 'blog:markdown_image_upload' %}", {
method: 'POST',

Loading…
Cancel
Save