fix: 低优先级收尾 — 图片上传/语言编辑/插件恢复/URL 编码
- P3-2: ArticleEditor 图片上传接入 /upload 端点 + 封面图上传按钮 - P4-3: recover_plugins 添加 tenant 日志 + 同 ID 去重保护 - P4-4: LanguageManager 编辑弹窗改为真实表单 (name 字段) + 后端 name 持久化 - P4-6: Settings API getSetting/updateSetting 添加 encodeURIComponent
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState, useCallback, useMemo } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { Button, Input, Select, Space, message, Spin } from 'antd';
|
||||
import { ArrowLeftOutlined, SaveOutlined, SendOutlined } from '@ant-design/icons';
|
||||
import { Button, Input, Select, Space, message, Spin, Upload } from 'antd';
|
||||
import { ArrowLeftOutlined, SaveOutlined, SendOutlined, UploadOutlined } from '@ant-design/icons';
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-react';
|
||||
import type { IDomEditor, IEditorConfig, IToolbarConfig } from '@wangeditor/editor';
|
||||
import {
|
||||
@@ -104,10 +104,24 @@ export default function ArticleEditor() {
|
||||
placeholder: '请输入文章内容...',
|
||||
MENU_CONF: {
|
||||
uploadImage: {
|
||||
// 自定义图片上传 - 预留后端接口
|
||||
async customUpload(_file: File, _insertFn: (url: string, alt?: string, href?: string) => void) {
|
||||
// TODO: 实现图片上传到后端
|
||||
message.warning('图片上传功能待实现');
|
||||
async customUpload(file: File, insertFn: (url: string, alt?: string, href?: string) => void) {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
const token = localStorage.getItem('access_token');
|
||||
const resp = await fetch('/api/v1/upload', {
|
||||
method: 'POST',
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
||||
body: formData,
|
||||
});
|
||||
if (!resp.ok) throw new Error('上传失败');
|
||||
const result = await resp.json();
|
||||
const url: string = result.data.url;
|
||||
const urlWithToken = token ? `${url}?token=${token}` : url;
|
||||
insertFn(urlWithToken, file.name, urlWithToken);
|
||||
} catch {
|
||||
message.error('图片上传失败');
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -441,13 +455,41 @@ export default function ArticleEditor() {
|
||||
color: isDark ? '#94a3b8' : '#475569',
|
||||
}}
|
||||
>
|
||||
封面图 URL
|
||||
封面图
|
||||
</label>
|
||||
<Input
|
||||
value={coverImage}
|
||||
onChange={(e) => setCoverImage(e.target.value)}
|
||||
placeholder="请输入封面图片 URL"
|
||||
/>
|
||||
<Space.Compact style={{ width: '100%' }}>
|
||||
<Input
|
||||
value={coverImage}
|
||||
onChange={(e) => setCoverImage(e.target.value)}
|
||||
placeholder="请输入封面图片 URL 或上传文件"
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<Upload
|
||||
accept="image/*"
|
||||
showUploadList={false}
|
||||
beforeUpload={async (file) => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
const token = localStorage.getItem('access_token');
|
||||
const resp = await fetch('/api/v1/upload', {
|
||||
method: 'POST',
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
||||
body: formData,
|
||||
});
|
||||
if (!resp.ok) throw new Error('上传失败');
|
||||
const result = await resp.json();
|
||||
setCoverImage(result.data.url);
|
||||
message.success('封面图上传成功');
|
||||
} catch {
|
||||
message.error('封面图上传失败');
|
||||
}
|
||||
return false;
|
||||
}}
|
||||
>
|
||||
<Button icon={<UploadOutlined />}>上传</Button>
|
||||
</Upload>
|
||||
</Space.Compact>
|
||||
{coverImage && (
|
||||
<div
|
||||
style={{
|
||||
|
||||
Reference in New Issue
Block a user