lee-zt 4 weeks ago
commit d5e8ae9f7c

@ -15,6 +15,7 @@
<img v-if="previewAvatar" :src="previewAvatar" alt="新头像预览" class="new-avatar">
</div>
<label class="upload-btn">
<span class="plus-icon">+</span>
选择新头像
<input type="file" accept="image/*" @change="handleAvatarChange" class="file-input">
</label>
@ -143,7 +144,7 @@ import { ref, computed, onMounted } from 'vue';
import { useUserStore } from '@/stores/user';
import request from '@/utils/request';
import { ElMessage } from 'element-plus';
import { useRouter } from 'vue-router'
import { useRouter } from 'vue-router';
const userStore = useUserStore();
const userInfo = computed(() => userStore.userInfo);
@ -384,9 +385,10 @@ onMounted(() => {
max-width: 700px;
margin: 2rem auto;
padding: 2.5rem 2rem 2rem 2rem;
background: #f8f9fa;
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 16px rgba(52, 152, 219, 0.08);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
position: relative;
}
.title {
@ -396,6 +398,8 @@ onMounted(() => {
margin-bottom: 2.2rem;
letter-spacing: 1px;
text-align: center;
border-bottom: 2px solid #ff88aa;
padding-bottom: 10px;
}
.section-title {
@ -428,13 +432,15 @@ onMounted(() => {
}
.new-avatar {
border-color: #3498db;
border-color: #ff88aa;
}
.upload-btn {
display: inline-block;
display: flex;
align-items: center;
gap: 8px;
padding: 0.5rem 1.2rem;
background: linear-gradient(90deg, #3498db 60%, #6dd5fa 100%);
background: #ff88aa;
color: white;
border-radius: 4px;
cursor: pointer;
@ -445,7 +451,12 @@ onMounted(() => {
}
.upload-btn:hover {
background: linear-gradient(90deg, #2980b9 60%, #3498db 100%);
background: #ff6699;
}
.plus-icon {
font-size: 20px;
font-weight: bold;
}
.file-input {
@ -483,17 +494,17 @@ onMounted(() => {
.input, .textarea, select.input {
width: 100%;
padding: 0.75rem;
border: 1px solid #bdc3c7;
border-radius: 4px;
border: 1px solid #e0e0e0;
border-radius: 8px;
font-size: 0.95rem;
transition: border-color 0.3s ease;
background: #fff;
background: #f9f9f9;
}
.input:focus, .textarea:focus, select.input:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.08);
border-color: #ff88aa;
box-shadow: 0 0 0 2px rgba(255, 136, 170, 0.08);
}
.input-error {
@ -514,10 +525,10 @@ onMounted(() => {
.submit-btn {
width: 100%;
padding: 0.85rem;
background: linear-gradient(90deg, #3498db 60%, #6dd5fa 100%);
background: #ff88aa;
color: white;
border: none;
border-radius: 4px;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
@ -527,6 +538,34 @@ onMounted(() => {
}
.submit-btn:hover {
background: linear-gradient(90deg, #2980b9 60%, #3498db 100%);
background: #ff6699;
}
/* 花瓣动画(与背景呼应) */
@keyframes fall {
0% { transform: translateY(-100vh) rotate(0deg); opacity: 1; }
100% { transform: translateY(100vh) rotate(360deg); opacity: 0.5; }
}
.petal {
position: absolute;
width: 10px;
height: 15px;
background: pink;
border-radius: 50% 50% 0 0;
animation: fall 5s linear infinite;
z-index: -1;
}
/* 初始化花瓣可在mounted中动态生成此处简化 */
.change-info-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
animation: none; /* 避免重复动画 */
}
</style>

@ -12,32 +12,47 @@
maxlength="50"
placeholder="请输入标题3-50个字符"
required
class="input-field"
/>
</div>
<!-- 分类选择 -->
<div class="form-row">
<label>分类</label>
<select v-model="form.categoryId" required>
<select v-model="form.categoryId" required class="input-field">
<option disabled value="">请选择分类</option>
<option v-for="cat in categories" :key="cat.id" :value="cat.id">{{ cat.name }}</option>
</select>
</div>
<!-- 图片上传 -->
<!-- 图片上传自定义+号按钮 -->
<div class="form-row">
<label>图片</label>
<input type="file" accept="image/*" @change="onFileChange" />
<label>封面</label>
<div class="upload-wrapper">
<!-- 隐藏的文件选择input -->
<input
type="file"
accept="image/*"
@change="onFileChange"
id="image-upload"
class="hidden-input"
/>
<!-- 自定义+号按钮 -->
<label for="image-upload" class="upload-btn">
<span class="plus-icon">+</span>
</label>
<!-- 图片预览 -->
<div v-if="form.imagePreview" class="img-preview">
<img :src="form.imagePreview" alt="预览" />
<button type="button" @click="removeImage"></button>
<img :src="form.imagePreview" alt="预览" class="preview-img" />
<button type="button" @click="removeImage" class="remove-btn">×</button>
</div>
</div>
</div>
<!-- 匿名发布选项放在内容编辑器下方或合适位置 -->
<div class="form-row">
<label>
<input type="checkbox" v-model="form.status" true-value="1" false-value="0" />
<!-- 匿名发布选项 -->
<div class="form-row anonymous-option">
<label class="anonymous-label">
<input type="checkbox" v-model="form.status" true-value="1" false-value="0" class="anonymous-checkbox" />
匿名发布
</label>
</div>
@ -50,12 +65,13 @@
rows="8"
placeholder="请输入帖子内容支持Markdown语法"
required
class="textarea-field"
></textarea>
</div>
<!-- 提交按钮 -->
<div class="submit-area">
<button type="submit" :disabled="submitting">
<button type="submit" :disabled="submitting" class="submit-btn">
{{ submitting ? '发布中...' : '立即发布' }}
</button>
</div>
@ -102,9 +118,7 @@ export default {
const onFileChange = async (e) => {
const file = e.target.files[0]
if (!file) return
//
form.value.imagePreview = URL.createObjectURL(file)
//
const fd = new FormData()
fd.append('file', file)
try {
@ -132,6 +146,10 @@ export default {
showResult('error', '请填写完整信息')
return
}
if (!form.value.image) {
showResult('error', '请先选择帖子封面')
return
}
if (form.value.title.length < 3 || form.value.title.length > 50) {
showResult('error', '标题长度需3-50字符')
return
@ -152,7 +170,6 @@ export default {
const res = await request.post('/post', postData)
if (res.code === 200) {
showResult('success', '帖子发布成功')
console.log(res.data);
setTimeout(() => {
router.push(`/`)
}, 1200)
@ -187,94 +204,181 @@ export default {
</script>
<style scoped>
.upload-wrapper {
display: flex;
align-items: center;
gap: 15px;
}
.hidden-input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.upload-btn {
width: 80px;
height: 80px;
border: 2px dashed #ff88aa;
border-radius: 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
.upload-btn:hover {
background-color: #fff5f8;
border-color: #ff6699;
}
.plus-icon {
font-size: 32px;
color: #ff88aa;
font-weight: lighter;
}
.img-preview {
position: relative;
display: inline-block;
}
.preview-img {
width: 80px;
height: 80px;
object-fit: cover;
border-radius: 8px;
border: 1px solid #eee;
}
.remove-btn {
position: absolute;
top: -8px;
right: -8px;
width: 20px;
height: 20px;
background: #ff6699;
color: white;
border: none;
border-radius: 50%;
padding: 0;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
}
.post-container {
max-width: 600px;
margin: 30px auto;
padding: 30px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.08);
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
}
.page-title {
text-align: center;
margin-bottom: 24px;
font-size: 1.6em;
font-size: 1.8em;
color: #2c3e50;
border-bottom: 2px solid #ff88aa;
padding-bottom: 10px;
}
.editor-wrapper {
padding: 20px;
}
.form-row {
margin-bottom: 18px;
margin-bottom: 20px;
display: flex;
flex-direction: column;
}
.form-row label {
font-weight: bold;
margin-bottom: 6px;
margin-bottom: 8px;
color: #333;
}
.form-row input[type="text"],
.form-row select,
.form-row textarea {
padding: 8px;
border: 1px solid #dcdcdc;
border-radius: 4px;
.input-field,
.textarea-field {
padding: 10px;
border: 1px solid #e0e0e0;
border-radius: 8px;
font-size: 15px;
transition: border-color 0.3s;
}
.form-row textarea {
resize: vertical;
.input-field:focus,
.textarea-field:focus {
outline: none;
border-color: #ff88aa;
}
.img-preview {
margin-top: 8px;
position: relative;
display: inline-block;
.anonymous-option {
flex-direction: row;
align-items: center;
justify-content: flex-start;
margin-bottom: 15px;
}
.img-preview img {
max-width: 120px;
max-height: 120px;
border-radius: 4px;
border: 1px solid #eee;
.anonymous-label {
display: flex;
align-items: center;
gap: 8px;
color: #666;
}
.img-preview button {
position: absolute;
top: 2px;
right: 2px;
background: #f56c6c;
color: #fff;
border: none;
border-radius: 3px;
padding: 2px 8px;
cursor: pointer;
font-size: 12px;
.anonymous-checkbox {
width: 18px;
height: 18px;
accent-color: #ff88aa;
}
.textarea-field {
resize: vertical;
min-height: 150px;
}
.submit-area {
text-align: center;
margin-top: 24px;
margin-top: 30px;
}
.submit-area button {
background: #409eff;
color: #fff;
.submit-btn {
background: #ff88aa;
color: white;
border: none;
padding: 10px 32px;
border-radius: 4px;
padding: 12px 40px;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
}
.submit-area button:disabled {
background: #b3d8ff;
.submit-btn:disabled {
background: #ffcccc;
cursor: not-allowed;
}
.submit-btn:hover {
background: #ff6699;
}
.result-alert {
margin-top: 20px;
padding: 12px 18px;
border-radius: 4px;
padding: 15px 20px;
border-radius: 8px;
font-size: 15px;
text-align: center;
}
.result-alert.success {
background: #e1f3d8;
color: #3a7a1c;
}
.result-alert.error {
background: #fde2e2;
color: #c0392b;

Loading…
Cancel
Save