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