You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

623 lines
16 KiB

<template>
<view class="publish-container">
<!-- 内容区域 -->
<view class="content-scroll">
<!-- 图片区域 -->
<view class="image-section">
<uni-file-picker
v-model="selectedImages"
mode="grid"
file-mediatype="image"
file-extname="png,jpg,jpeg"
:auto-upload="false"
@select="onImageSelect"
@delete="onImageDelete"
></uni-file-picker>
</view>
<!-- 标题区域 -->
<view class="title-section">
<input
class="title-input"
v-model="noteForm.title"
placeholder="请输入标题..."
maxlength="100"
auto-height
/>
</view>
<!-- 详情区域 -->
<view class="content-section">
<textarea
class="content-input"
v-model="noteForm.content"
placeholder="分享你的想法..."
maxlength="2000"
auto-height
/>
</view>
<!-- 快速标签区域 -->
<view class="quick-tags-section">
<view class="section-title">快速标签</view>
<scroll-view
class="quick-tags-scroll"
scroll-x="true"
show-scrollbar="false"
>
<view class="quick-tags-list">
<view
class="quick-tag-item"
v-for="tag in quickTags"
:key="tag"
@click="insertQuickTag(tag)"
>
#{{ tag.name }}
</view>
</view>
</scroll-view>
</view>
<!-- 已添加标签 -->
<view class="selected-tags-section" v-if="noteForm.tags.length">
<scroll-view
class="selected-tags-scroll"
scroll-x
:show-scrollbar="false"
>
<view class="selected-tags-list">
<view
class="selected-tag-item"
v-for="(tag, index) in noteForm.tags"
:key="index"
@click="removeTag(index)"
>
<view class="tag-text">#{{ tag.name }}</view>
<view class="tag-close">×</view>
</view>
</view>
</scroll-view>
</view>
<!-- 底部占位 -->
<view class="bottom-placeholder"></view>
</view>
<!-- 底部发布按钮 -->
<view class="bottom-publish">
<button class="publish-btn" @click="publishNote">
{{ isEditMode ? "保存修改" : "发布笔记" }}
</button>
</view>
</view>
</template>
<script>
export default {
name: "PublishNote",
data() {
return {
selectedImages: [],
noteForm: {
id: "", // 笔记ID,编辑模式下有值
title: "",
content: "",
tags: [], // 存储选中的标签对象 {id, name}
images: [],
},
quickTags: [], // 从接口获取的标签列表
imageStyles: {
height: "200rpx", // 边框高度
width: "200rpx", // 边框宽度
},
isEditMode: false, // 是否是编辑模式
};
},
computed: {
canPublish() {
return (
(this.noteForm.title.trim() || this.noteForm.content.trim()) &&
this.selectedImages &&
this.selectedImages.length > 0
);
},
},
onLoad(options) {
// 先获取标签列表
this.getTagList().then(() => {
// 检查是否有noteId参数,如果有则是编辑模式
if (options && options.id) {
this.noteForm.id = options.id;
this.isEditMode = true;
this.loadNoteDetail();
}
});
},
methods: {
// 获取标签列表
async getTagList() {
return new Promise(async (resolve) => {
try {
const res = await this.Post({}, "/framework/tag/list", "DES");
if (res && res.data) {
this.quickTags = res.data;
}
} catch (error) {
console.error("获取标签列表失败:", error);
// 如果接口失败,可以设置默认标签
this.quickTags = [
{ id: "1", name: "DES" },
{ id: "2", name: "AGENT" },
{ id: "3", name: "时间银行" },
{ id: "4", name: "阅读体验" },
{ id: "5", name: "时间力" },
];
}
resolve();
});
},
// 返回上一页
goBack() {
if (this.canPublish) {
uni.showModal({
title: "确认退出",
content: "退出后内容将不会保存,确定要退出吗?",
success: (res) => {
if (res.confirm) {
uni.navigateBack();
}
},
});
} else {
uni.navigateBack();
}
},
// 图片选择事件
onImageSelect(e) {
console.log("选择图片·:", e);
// selectedImages已经通过v-model自动更新,立即上传新选择的图片
this.uploadNewImages(e.tempFiles || []);
},
// 图片删除事件
onImageDelete(e) {
let index = e.index;
// selectedImages已经通过v-model自动更新,需要同步更新noteForm.images
// 根据当前selectedImages的数量来调整noteForm.images
if (this.noteForm.images && this.noteForm.images.length > index) {
this.noteForm.images.splice(index, 1);
}
},
// 插入快速标签
insertQuickTag(tag) {
// 检查是否已经添加了该标签
const existingTag = this.noteForm.tags.find((t) => t.id == tag.id);
if (!existingTag) {
this.noteForm.tags.unshift(tag);
}
},
// 移除标签
removeTag(index) {
this.noteForm.tags.splice(index, 1);
},
// 上传单个图片文件
async uploadSingleImage(file) {
return new Promise((resolve, reject) => {
const token = uni.getStorageSync("userInfo")
? JSON.parse(uni.getStorageSync("userInfo")).token
: "";
uni.uploadFile({
url: this.NEWAPIURL_DES + "/system/oss/upload", // 替换为你的上传接口
filePath: file.url || file.path,
name: "file",
header: {
token: token || "",
// 添加必要的请求头,如token等
},
success: (res) => {
console.log(res);
try {
const data = JSON.parse(res.data);
if (data.code === 200) {
resolve(data.data.url); // 返回服务器返回的图片URL
} else {
reject(new Error(data.message || "上传失败"));
}
} catch (e) {
reject(new Error("解析响应失败"));
}
},
fail: (err) => {
reject(err);
},
});
});
},
// 上传新选择的图片
async uploadNewImages(newFiles) {
if (!newFiles || newFiles.length === 0) {
return;
}
try {
uni.showLoading({ title: "上传图片中..." });
const uploadPromises = newFiles.map((file) =>
this.uploadSingleImage(file)
);
const uploadedUrls = await Promise.all(uploadPromises);
// 将上传成功的URL添加到noteForm.images中
this.noteForm.images = [
...(this.noteForm.images || []),
...uploadedUrls,
];
uni.hideLoading();
} catch (error) {
uni.hideLoading();
uni.showToast({
title: error.message || "图片上传失败",
icon: "none",
});
}
},
// 批量上传图片(保留原方法,用于其他场景)
async uploadImages() {
if (!this.selectedImages || this.selectedImages.length === 0) {
return [];
}
const uploadPromises = this.selectedImages.map((file) =>
this.uploadSingleImage(file)
);
try {
const uploadedUrls = await Promise.all(uploadPromises);
return uploadedUrls;
} catch (error) {
throw new Error("图片上传失败: " + error.message);
}
},
// 发布或更新笔记
async publishNote() {
console.log(this.noteForm, "0000");
if (!this.noteForm.images || this.noteForm.images.length === 0) {
uni.showToast({
title: "请至少选择一张图片",
icon: "none",
});
return;
}
if (!this.noteForm.title.trim() && !this.noteForm.content.trim()) {
uni.showToast({
title: "请添加标题或内容",
icon: "none",
});
return;
}
try {
// 根据模式显示不同的加载提示
uni.showLoading({ title: this.isEditMode ? "保存中..." : "发布中..." });
// 图片已经在选择时上传,直接提交表单
await this.submitNote(this.noteForm);
uni.hideLoading();
uni.showToast({
title: this.isEditMode ? "修改成功" : "发布成功",
icon: "none",
duration: 2000,
});
// 延迟跳转,让用户看到成功提示
setTimeout(() => {
uni.navigateBack();
}, 800);
} catch (error) {
uni.hideLoading();
uni.showToast({
title:
error.data.msg ||
(this.isEditMode ? "修改失败,请重试" : "发布失败,请重试"),
icon: "none",
});
}
},
// 加载笔记详情
async loadNoteDetail() {
try {
uni.showLoading({ title: "加载中..." });
const res = await this.Post(
{ noteId: this.noteForm.id },
"/framework/note/getInfo/" + this.noteForm.id,
"DES"
);
if (res.code === 200 && res.data) {
const noteData = res.data;
// 填充表单数据
this.noteForm.title = noteData.title || "";
this.noteForm.content = noteData.content || "";
// 处理图片
if (noteData.coverImage) {
const imageUrls = noteData.coverImage.split(",");
this.noteForm.images = imageUrls;
// 为uni-file-picker准备数据
this.selectedImages = imageUrls.map((url) => ({
url: url,
extname: url.split(".").pop(),
name: url.split("/").pop(),
}));
}
// 处理标签
if (noteData.tagNames && noteData.tagIds) {
// 处理同时有tagNames和tagIds的情况
const tagNames = noteData.tagNames.split(",");
const tagIds = noteData.tagIds.split(",");
this.noteForm.tags = tagIds.map((id, index) => ({
id: id,
name: tagNames[index] || "",
}));
} else if (noteData.tags) {
// 处理只有tags字段的情况(格式为"3,4"这样的ids集合)
const tagIds = noteData.tags.split(",");
// 从quickTags中查找匹配的标签名称
this.noteForm.tags = tagIds.map((id) => {
const matchedTag = this.quickTags.find((tag) => tag.id == id);
if (matchedTag) {
return {
id: id,
name: matchedTag.name,
};
} else {
console.warn(`未找到ID为${id}的标签,请确保标签列表已正确加载`);
return {
id: id,
name: `标签${id}`, // 提供更友好的默认名称
};
}
});
// 如果没有找到任何匹配的标签,记录日志
if (
this.noteForm.tags.some(
(tag) => !this.quickTags.find((qt) => qt.id === tag.id)
)
) {
console.warn("部分标签未在标签列表中找到,可能需要刷新标签列表");
}
}
} else {
uni.showToast({
title: res.msg || "获取笔记详情失败",
icon: "none",
});
}
} catch (error) {
console.error("加载笔记详情失败:", error);
uni.showToast({
title: "获取笔记详情失败",
icon: "none",
});
} finally {
uni.hideLoading();
}
},
// 提交笔记
async submitNote(noteData) {
// 格式化数据以符合API要求
const formData = {
title: noteData.title,
content: noteData.content,
tags: noteData.tags.map((tag) => tag.id).join(","), // 标签ID逗号分隔
coverImage: noteData.images.join(","), // 图片URL逗号分隔
method: "POST",
};
// 如果是编辑模式,添加笔记ID
if (this.isEditMode && this.noteForm.id) {
formData.id = this.noteForm.id;
formData.method = "PUT";
return this.Post(formData, "/framework/note/editNote", "DES");
} else {
return this.Post(formData, "/framework/note/addNote", "DES");
}
},
},
};
</script>
<style lang="scss" scoped>
.publish-container {
min-height: 100vh;
background: #fff;
display: flex;
flex-direction: column;
}
// 内容区域
.content-scroll {
flex: 1;
padding: 0 30rpx;
}
// 图片区域
.image-section {
margin: 32rpx 0;
padding-bottom: 32rpx;
border-bottom: 1rpx solid #f0f0f0;
}
// 标题区域
.title-section {
margin: 32rpx 0;
padding-bottom: 32rpx;
border-bottom: 1rpx solid #f0f0f0;
.title-input {
width: 100%;
min-height: 60rpx;
font-size: 36rpx;
font-weight: 600;
line-height: 1.4;
color: #333;
border: none;
padding: 0;
resize: none;
}
}
// 详情区域
.content-section {
margin: 32rpx 0;
padding-bottom: 32rpx;
border-bottom: 1rpx solid #f0f0f0;
.content-input {
width: 100%;
min-height: 300rpx;
font-size: 28rpx;
line-height: 1.6;
color: #333;
border: none;
padding: 0;
resize: none;
}
}
// 快速标签区域
.quick-tags-section {
margin: 24rpx 0;
.section-title {
font-size: 28rpx;
color: #666;
margin-bottom: 25rpx;
}
.quick-tags-scroll {
width: 100%;
white-space: nowrap;
}
.quick-tags-list {
display: inline-flex;
gap: 16rpx;
padding-right: 32rpx;
width: max-content;
.quick-tag-item {
flex-shrink: 0;
padding: 16rpx 24rpx;
background: #f8f9fa;
border-radius: 32rpx;
font-size: 28rpx;
color: #666;
cursor: pointer;
transition: all 0.3s ease;
white-space: nowrap;
&:active {
background: #e9ecef;
transform: scale(0.96);
}
}
}
}
// 已选标签区域
.selected-tags-section {
margin: 24rpx 0;
padding-top: 24rpx;
border-top: 1rpx solid #f0f0f0;
.selected-tags-scroll {
width: 100%;
white-space: nowrap;
}
.selected-tags-list {
display: inline-flex;
gap: 16rpx;
padding-right: 32rpx;
width: max-content;
.selected-tag-item {
flex-shrink: 0;
display: flex;
align-items: center;
gap: 8rpx;
padding: 12rpx 20rpx;
background: linear-gradient(135deg, #fffdb7e6 0%, #97fffab5 100%);
border-radius: 32rpx;
cursor: pointer;
white-space: nowrap;
.tag-text {
font-size: 26rpx;
color: #333;
}
.tag-close {
font-size: 24rpx;
color: #333;
opacity: 0.8;
margin-left: 8rpx;
}
}
}
}
// 底部发布按钮
.bottom-publish {
padding: 24rpx 32rpx;
background: #fff;
border-top: 1rpx solid #f0f0f0;
padding-bottom: max(24rpx, env(safe-area-inset-bottom));
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 100;
.publish-btn {
width: 100%;
height: 88rpx;
color: #333333;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 600;
border: none;
transition: all 0.3s ease;
background: linear-gradient(135deg, #fffdb7e6 0%, #97fffab5 100%);
&:disabled {
background: linear-gradient(135deg, #ccc 0%, #ccc 100%);
}
&:not(:disabled):active {
transform: scale(0.98);
}
}
}
.bottom-placeholder {
height: 180rpx;
padding-bottom: calc(24rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
}
</style>