375 lines
9.0 KiB
Markdown
375 lines
9.0 KiB
Markdown
# 详情图集功能使用指南
|
||
|
||
## 🎯 功能概述
|
||
|
||
详情图集功能允许为工作流和课程添加多张详情展示图片,为用户提供更丰富的视觉内容介绍。
|
||
|
||
## 🔧 技术实现
|
||
|
||
### 数据库字段
|
||
```sql
|
||
-- 工作流表
|
||
ALTER TABLE workflow ADD COLUMN detail_gallery longtext DEFAULT NULL
|
||
COMMENT '详情图集(JSON格式存储多张图片URL)';
|
||
|
||
-- 课程表
|
||
ALTER TABLE course ADD COLUMN detail_gallery longtext DEFAULT NULL
|
||
COMMENT '详情图集(JSON格式存储多张图片URL)';
|
||
```
|
||
|
||
### 存储格式
|
||
```json
|
||
// 详情图集字段存储格式
|
||
"detailGallery": "[\"https://oss.../image1.jpg\",\"https://oss.../image2.jpg\",\"https://oss.../image3.jpg\"]"
|
||
```
|
||
|
||
## 📱 前端集成
|
||
|
||
### 1. 图片上传流程
|
||
|
||
```javascript
|
||
/**
|
||
* 上传详情图集
|
||
* @param {FileList} files - 选择的图片文件
|
||
* @param {string} userId - 用户ID
|
||
* @returns {Promise<string>} 详情图集JSON字符串
|
||
*/
|
||
async function uploadDetailGallery(files, userId) {
|
||
const uploadPromises = Array.from(files).map(async (file) => {
|
||
// 1. 获取OSS上传签名
|
||
const signResponse = await fetch('/user/oss/post-signature/json', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
fileName: file.name,
|
||
userId: userId
|
||
})
|
||
});
|
||
|
||
if (!signResponse.ok) {
|
||
throw new Error('获取上传签名失败');
|
||
}
|
||
|
||
const signResult = await signResponse.json();
|
||
const signData = signResult.data;
|
||
|
||
// 2. 构建上传表单
|
||
const formData = new FormData();
|
||
const objectKey = signData.dir + file.name;
|
||
|
||
formData.append('key', objectKey);
|
||
formData.append('policy', signData.policy);
|
||
formData.append('x-oss-credential', signData.x_oss_credential);
|
||
formData.append('x-oss-date', signData.x_oss_date);
|
||
formData.append('x-oss-signature-version', signData.version);
|
||
formData.append('x-oss-signature', signData.signature);
|
||
formData.append('success_action_status', '200');
|
||
formData.append('file', file);
|
||
|
||
// 3. 上传到OSS
|
||
const uploadResponse = await fetch(signData.url, {
|
||
method: 'POST',
|
||
body: formData
|
||
});
|
||
|
||
if (!uploadResponse.ok) {
|
||
throw new Error('文件上传失败');
|
||
}
|
||
|
||
// 4. 返回完整的文件URL
|
||
return `${signData.url}/${objectKey}`;
|
||
});
|
||
|
||
try {
|
||
const imageUrls = await Promise.all(uploadPromises);
|
||
return JSON.stringify(imageUrls);
|
||
} catch (error) {
|
||
console.error('上传详情图集失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. 解析详情图集
|
||
|
||
```javascript
|
||
/**
|
||
* 解析详情图集
|
||
* @param {string} detailGallery - 详情图集JSON字符串
|
||
* @returns {string[]} 图片URL数组
|
||
*/
|
||
function parseDetailGallery(detailGallery) {
|
||
if (!detailGallery || detailGallery.trim() === '') {
|
||
return [];
|
||
}
|
||
|
||
try {
|
||
const urls = JSON.parse(detailGallery);
|
||
return Array.isArray(urls) ? urls : [];
|
||
} catch (error) {
|
||
console.error('解析详情图集失败:', error);
|
||
return [];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 渲染详情图集
|
||
* @param {string} detailGallery - 详情图集JSON字符串
|
||
* @param {HTMLElement} container - 容器元素
|
||
*/
|
||
function renderDetailGallery(detailGallery, container) {
|
||
const imageUrls = parseDetailGallery(detailGallery);
|
||
|
||
container.innerHTML = '';
|
||
|
||
if (imageUrls.length === 0) {
|
||
container.innerHTML = '<p>暂无详情图片</p>';
|
||
return;
|
||
}
|
||
|
||
imageUrls.forEach((url, index) => {
|
||
const img = document.createElement('img');
|
||
img.src = url;
|
||
img.alt = `详情图片 ${index + 1}`;
|
||
img.className = 'detail-gallery-image';
|
||
img.style.cssText = `
|
||
width: 100%;
|
||
max-width: 400px;
|
||
height: auto;
|
||
margin: 10px;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||
cursor: pointer;
|
||
`;
|
||
|
||
// 点击预览
|
||
img.addEventListener('click', () => {
|
||
showImagePreview(url);
|
||
});
|
||
|
||
container.appendChild(img);
|
||
});
|
||
}
|
||
```
|
||
|
||
### 3. 完整使用示例
|
||
|
||
```html
|
||
<!-- HTML -->
|
||
<div class="upload-section">
|
||
<label for="detail-images">选择详情图片(可选择多张):</label>
|
||
<input type="file" id="detail-images" multiple accept="image/*">
|
||
<button onclick="handleUpload()">上传作品</button>
|
||
</div>
|
||
|
||
<div id="preview-container"></div>
|
||
|
||
<script>
|
||
async function handleUpload() {
|
||
const fileInput = document.getElementById('detail-images');
|
||
const files = fileInput.files;
|
||
|
||
let detailGallery = '';
|
||
|
||
// 如果选择了图片,则上传详情图集
|
||
if (files.length > 0) {
|
||
try {
|
||
detailGallery = await uploadDetailGallery(files, getCurrentUserId());
|
||
console.log('详情图集上传成功:', detailGallery);
|
||
} catch (error) {
|
||
alert('详情图集上传失败: ' + error.message);
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 提交工作流
|
||
const workflowData = {
|
||
name: "我的工作流",
|
||
description: "工作流描述",
|
||
detailGallery: detailGallery, // 详情图集
|
||
vodVideoId: "a0776b0179bf71f0bea45017f1e90102",
|
||
data: JSON.stringify({nodes: [], edges: []}),
|
||
isFree: 1
|
||
};
|
||
|
||
try {
|
||
const response = await fetch('/user/workflow/submit', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': 'Bearer ' + getToken()
|
||
},
|
||
body: JSON.stringify(workflowData)
|
||
});
|
||
|
||
if (response.ok) {
|
||
const result = await response.json();
|
||
alert('工作流上传成功!ID: ' + result.data);
|
||
} else {
|
||
alert('工作流上传失败');
|
||
}
|
||
} catch (error) {
|
||
alert('提交失败: ' + error.message);
|
||
}
|
||
}
|
||
|
||
// 获取当前用户ID
|
||
function getCurrentUserId() {
|
||
return '17543607206742139'; // 示例用户ID
|
||
}
|
||
|
||
// 获取认证token
|
||
function getToken() {
|
||
return localStorage.getItem('token');
|
||
}
|
||
</script>
|
||
```
|
||
|
||
## 🚀 后端接口支持
|
||
|
||
### 1. 工作流相关接口
|
||
|
||
**上传工作流** - `POST /user/workflow/submit`
|
||
```json
|
||
{
|
||
"name": "工作流名称",
|
||
"detailGallery": "[\"url1\",\"url2\"]",
|
||
"vodVideoId": "视频ID",
|
||
"data": "工作流JSON数据"
|
||
}
|
||
```
|
||
|
||
**更新工作流** - `PUT /user/content/workflows/{id}`
|
||
```json
|
||
{
|
||
"name": "更新的名称",
|
||
"detailGallery": "[\"new_url1\",\"new_url2\"]"
|
||
}
|
||
```
|
||
|
||
### 2. 课程相关接口
|
||
|
||
**更新课程** - `PUT /user/course/{id}`
|
||
```json
|
||
{
|
||
"title": "课程标题",
|
||
"detailGallery": "[\"url1\",\"url2\"]",
|
||
"price": 99.99
|
||
}
|
||
```
|
||
|
||
**用户内容管理** - `PUT /user/content/courses`
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"title": "课程标题",
|
||
"detailGallery": "[\"url1\",\"url2\"]"
|
||
}
|
||
```
|
||
|
||
## 📋 响应示例
|
||
|
||
### 工作流详情API响应
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "success",
|
||
"data": {
|
||
"workflow": {
|
||
"id": 1,
|
||
"name": "智能图像生成工作流",
|
||
"coverUrl": "https://oss.../cover.jpg",
|
||
"detailGallery": "[\"https://oss.../detail1.jpg\",\"https://oss.../detail2.jpg\",\"https://oss.../detail3.jpg\"]",
|
||
"vodVideoId": "a0776b0179bf71f0bea45017f1e90102",
|
||
"price": 29.99
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 课程详情API响应
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"title": "AI图像处理入门课程",
|
||
"coverUrl": "https://oss.../cover.jpg",
|
||
"detailGallery": "[\"https://oss.../detail1.jpg\",\"https://oss.../detail2.jpg\"]",
|
||
"price": 99.99,
|
||
"chapters": [...]
|
||
}
|
||
```
|
||
|
||
## ⚙️ 最佳实践
|
||
|
||
### 1. 图片要求
|
||
- **尺寸**: 建议 1200x800px 或同等比例
|
||
- **格式**: 推荐 JPG/PNG
|
||
- **大小**: 单张图片不超过 5MB
|
||
- **数量**: 建议 2-5 张图片
|
||
|
||
### 2. 用户体验
|
||
- **预览功能**: 支持图片点击放大预览
|
||
- **加载优化**: 使用懒加载和图片压缩
|
||
- **错误处理**: 提供友好的错误提示
|
||
- **进度显示**: 显示上传进度
|
||
|
||
### 3. 性能优化
|
||
```javascript
|
||
// 图片压缩示例
|
||
function compressImage(file, maxWidth = 1200, quality = 0.8) {
|
||
return new Promise((resolve) => {
|
||
const canvas = document.createElement('canvas');
|
||
const ctx = canvas.getContext('2d');
|
||
const img = new Image();
|
||
|
||
img.onload = () => {
|
||
const ratio = Math.min(maxWidth / img.width, maxWidth / img.height);
|
||
canvas.width = img.width * ratio;
|
||
canvas.height = img.height * ratio;
|
||
|
||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||
canvas.toBlob(resolve, 'image/jpeg', quality);
|
||
};
|
||
|
||
img.src = URL.createObjectURL(file);
|
||
});
|
||
}
|
||
```
|
||
|
||
## 🔍 测试验证
|
||
|
||
### 1. 功能测试清单
|
||
- [ ] 单张图片上传
|
||
- [ ] 多张图片批量上传
|
||
- [ ] 图片格式验证
|
||
- [ ] 文件大小限制
|
||
- [ ] 详情图集解析
|
||
- [ ] 详情图集渲染
|
||
- [ ] 接口响应验证
|
||
|
||
### 2. 兼容性测试
|
||
- [ ] 现有工作流不受影响
|
||
- [ ] 现有课程不受影响
|
||
- [ ] API响应格式保持一致
|
||
- [ ] 数据库操作正常
|
||
|
||
## 🆘 常见问题
|
||
|
||
**Q: 详情图集是必填字段吗?**
|
||
A: 不是,详情图集是可选字段,不影响现有功能。
|
||
|
||
**Q: 如何清空详情图集?**
|
||
A: 设置 `detailGallery` 为空字符串 `""` 或 `null`。
|
||
|
||
**Q: 支持的最大图片数量?**
|
||
A: 理论上无限制,但建议2-5张以获得最佳体验。
|
||
|
||
**Q: 上传失败如何处理?**
|
||
A: 实现重试机制,并提供详细的错误信息。
|
||
|
||
---
|
||
|
||
**更新时间**: 2024-12-01
|
||
**版本**: v1.0
|
||
|