Files
t/uni_modules/uni-cms/pages/article/edit/edit.vue
T
2025-03-12 20:11:03 +08:00

484 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="uni-container">
<view class="header">
<view class="back">
<view @click="navigateBack" style="margin-left: 15px;display: flex;align-items: center">
<uni-icons type="back" size="24"></uni-icons>
<text>返回</text>
</view>
</view>
</view>
<uni-forms ref="form" :label-width="90" :model="formData" validateTrigger="submit" err-show-type="toast">
<view class="edit-box">
<view class="title">
<textarea
class="uni-input"
v-model="formData.title"
auto-height
placeholder="文章标题"
@input="() => autoSaveContent && autoSaveContent()"
/>
</view>
<editor-component @textchange="onTextChange" @ready="onEditorReady"></editor-component>
<view class="settings">
<uni-forms-item name="excerpt" label="文章摘要">
<textarea
class="excerpt"
placeholder="文章摘要"
auto-height
v-model="formData.excerpt"
@input="() => autoSaveContent && autoSaveContent()"
></textarea>
</uni-forms-item>
<uni-forms-item name="thumbnail" label="封面" required>
<view class="thumb-mode">
<radio-group @change="thumbModeChange">
<radio value="1" class="mode" :checked="thumbMode === 1">单图</radio>
<!-- <radio value="3" class="mode" :checked="thumbMode === 3">三图</radio> -->
<radio value="0" class="mode" :checked="thumbMode === 0">无封面图</radio>
</radio-group>
</view>
<view class="thumbnail-list" v-if="thumbMode > 0">
<view class="thumbnail" v-for="(thumbnail, index) in formData.thumbnail" :key="thumbnail.source">
<template v-if="thumbnail.source">
<view class="remove-thumbnail" @click="() => removeThumbnail(index)">
<uni-icons type="closeempty" size="16" color="#fff"></uni-icons>
</view>
<image :src="thumbnail.src" mode="aspectFill" style="width: 150px; height: 150px;"></image>
</template>
<template v-else>
<uni-icons
@click="() => openInsertImageDrawer(index)"
class="placeholder"
type="plusempty"
size="60"
color="#e8e8e8"
></uni-icons>
</template>
</view>
</view>
<view v-if="thumbMode > 0" style="color: #999; font-size: 13px; margin-top: 10px;">
<text>为了保证最佳效果展示;请上传16:9的封面图片</text>
</view>
</uni-forms-item>
<uni-forms-item v-if="false" name="user_id" label="作者" required>
<uni-data-picker v-model="formData.user_id" style="width: 200px;" collection="uni-id-users"
where="role=='uni-cms-author'||role=='admin'" field="nickname as text, _id as value"></uni-data-picker>
</uni-forms-item>
<uni-forms-item name="category_id" label="分类">
<uni-data-picker v-model="formData.category_id" style="width: 200px;"
collection="uni-cms-categories" field="name as text, _id as value"></uni-data-picker>
</uni-forms-item>
<!-- <uni-forms-item name="is_sticky" label="是否置顶">-->
<!-- <switch @change="binddata('is_sticky', $event.detail.value)" style="transform:scale(0.75)" :checked="formData.is_sticky"></switch>-->
<!-- </uni-forms-item>-->
<!-- <uni-forms-item name="is_essence" label="阅读加精">-->
<!-- <switch @change="binddata('is_essence', $event.detail.value)" style="transform:scale(0.75)" :checked="formData.is_essence"></switch>-->
<!-- </uni-forms-item>-->
<!-- <uni-forms-item name="comment_status" label="开放评论">-->
<!-- <uni-data-checkbox v-model="formData.comment_status"-->
<!-- :localdata="formOptions.comment_status_localdata"></uni-data-checkbox>-->
<!-- </uni-forms-item>-->
</view>
<view class="uni-button-group m" style="padding-bottom: 50px">
<button v-if="formData.article_status === 0" class="uni-button" style="width: 100px;margin-right: 10px;"
@click="submit(0)">保存</button>
<button type="primary" class="uni-button" style="width: 100px;" @click="submit(1)">更新</button>
</view>
</view>
</uni-forms>
<view class="footer">
<view class="wrap">
<view class="left">
<text class="auto-save" v-if="autoSave">
{{ autoSave.status === 1 ? '自动保存中...' : '已自动保存' }}
</text>
<text class="word-count" v-if="wordCount !== null">共 {{ wordCount }} 字</text>
</view>
<view class="right">
<view class="uni-button-group">
<!-- #ifdef H5 -->
<button v-if="formData.article_status === 0" class="uni-button"
style="width: 100px;margin-right: 10px;" @click="previewArticle">预览</button>
<!-- #endif -->
<button v-if="formData.article_status === 0" class="uni-button"
style="width: 100px;margin-right: 10px;" @click="submit(0)">保存</button>
<button type="primary" class="uni-button" style="width: 100px;" @click="submit(1)">{{
formData.article_status === 0 ? '发布' : '更新' }}</button>
</view>
</view>
</view>
</view>
<uni-drawer
class="insert-image-drawer"
v-if="drawerWidth"
ref="insertImageDrawer"
mode="right"
:width="drawerWidth"
>
<uni-media-library
mode="picker"
:selected-count="1"
:media-tabs="['image']"
@onInsert="onInsertCover"
></uni-media-library>
</uni-drawer>
<!-- #ifdef H5 -->
<uni-popup ref="popup" type="dialog" v-if="formDataId">
<article-preview :id="formDataId" @close="() => $refs.popup.close()"></article-preview>
</uni-popup>
<!-- #endif -->
</view>
</template>
<script>
// 引入编辑器组件
import EditorComponent from "@/uni_modules/uni-cms/components/editor/editor.vue"
// 引入Delta转换函数
import { translateInputContent, translateOutputContent } from '@/uni_modules/uni-cms/common/translate-content'
// 引入表单验证器
import { validator } from '@/uni_modules/uni-cms/common/validator/uni-cms-articles.js'
// 自动保存mixin
import autoSaveMixin from '@/uni_modules/uni-cms/common/auto-save-mixin.js';
import {parseImageUrl} from "@/uni_modules/uni-cms/common/parse-image-url";
// 引入文章预览组件
// #ifdef H5
import ArticlePreview from "@/uni_modules/uni-cms/components/preview/preview.vue";
// #endif
// 引入数据库相关
const db = uniCloud.database();
const dbCollectionName = 'uni-cms-articles';
// 根据字段获取验证器
function getValidator(fields) {
let result = {}
for (let key in validator) {
if (fields.includes(key)) {
result[key] = validator[key]
}
}
return result
}
export default {
components: {
EditorComponent,
// #ifdef H5
ArticlePreview
// #endif
},
mixins: [autoSaveMixin],
data() {
// 初始化表单数据
let formData = {
"user_id": "",
"category_id": "",
"title": "",
"content": null,
"excerpt": "",
"article_status": 0,
"is_sticky": null,
"is_essence": null,
"comment_status": null,
"thumbnail": [{}],
"publish_date": null
}
return {
// 初始化表单数据的id
formDataId: '',
// 表单数据
formData,
// 表单选项
formOptions: {
"comment_status_localdata": [
{
"value": 0,
"text": "关闭"
},
{
"value": 1,
"text": "开放"
}
]
},
// 表单验证规则
rules: {
...getValidator(Object.keys(formData))
},
// 编辑器格式
formats: {},
// 字数统计
wordCount: null,
// 插入图片抽屉宽度
drawerWidth: 0,
// 封面图展示方式
thumbMode: 1,
}
},
// 当页面准备好时,设置表单验证规则
onReady() {
this.$refs.form.setRules(this.rules)
},
// 当页面加载时,如果有id则获取表单数据
onLoad(e) {
if (e.id) {
const id = e.id
this.formDataId = id
this.getDetail(id)
}
},
mounted () {
const sysinfo = uni.getSystemInfoSync()
this.drawerWidth = sysinfo.windowWidth * .8
},
methods: {
// 封面图展示方式改变
thumbModeChange (e) {
const coverCount = Number(e.detail.value)
this.formData.thumbnail = Array.from(new Array(coverCount)).map((item, index) => {
if (this.formData.thumbnail[index]) {
return this.formData.thumbnail[index]
} else {
return {}
}
})
this.thumbMode = coverCount
},
/**
* 获取表单数据
* @param {String} id - 文章id
*/
getDetail(id) {
// 显示加载中
uni.showLoading({
mask: true
})
// 从数据库中获取文章数据
db.collection(dbCollectionName).doc(id).field("is_admin,user_id,category_id,title,content,excerpt,article_status,is_sticky,is_essence,comment_status,thumbnail,publish_date").get().then((res) => {
const data = res.result.data[0]
if (data) {
// 将获取到的数据赋值给formData
this.formData = data
// 如果有缩略图,则设置缩略图预览
const thumbnail = typeof this.formData.thumbnail === 'string' ? [this.formData.thumbnail] : this.formData.thumbnail
// 更新封面图展示方式
this.thumbMode = this.formData.thumbnail && this.formData.thumbnail.length ? this.formData.thumbnail.length : 0
// 设置编辑器内容
this.setContents()
// 处理封面图
parseImageUrl(thumbnail).then(res => {
this.formData.thumbnail = res
})
}
}).catch((err) => {
// 显示错误提示
uni.showModal({
content: err.message || '请求服务失败',
showCancel: false
})
}).finally(() => {
// 隐藏加载中
uni.hideLoading()
})
},
/**
* 当编辑器准备好时,设置编辑器内容
* @param {Object} editorCtx - 编辑器上下文
*/
onEditorReady(editorCtx) {
// 如果编辑器上下文不存在,则返回
if (!editorCtx) return
// 将编辑器上下文赋值给组件实例的editorCtx属性
this.editorCtx = editorCtx
// 设置编辑器内容
this.setContents()
},
/**
* 设置编辑器内容
*/
setContents() {
// 检查编辑器上下文是否存在
if (this.editorCtx && this.formData.content) {
// #ifdef H5
// 如果是H5平台,则直接使用formData中的内容
const content = this.formData.content
// #endif
// #ifndef H5
// 如果不是H5平台,则需要将formData中的内容转换为编辑器所需的格式
const content = translateInputContent(this.formData.content)
// #endif
// 设置编辑器内容
this.editorCtx.setContents({
delta: content
})
}
},
/**
* 验证表单并提交
* @param {Number} status - 文章状态
*/
submit(status) {
// 检查文章标题是否存在
if (!this.formData.title) {
// 隐藏加载中
uni.hideLoading()
// 显示文章标题必填的提示
return uni.showToast({
icon: 'none',
title: '文章标题必填',
})
}
if (this.formData.thumbnail) {
for (const thumbnail of this.formData.thumbnail) {
if (!thumbnail.source) {
// 如果封面图为空,提示用户
uni.hideLoading()
return uni.showToast({
icon: 'none',
title: '封面图必填',
})
}
}
}
return new Promise(resolve => {
// 验证表单
this.$refs.form.validate().then((res) => {
// 获取编辑器内容
this.editorCtx.getContents({
success: async (e) => {
// 提交表单
await this.submitForm({
...res,
thumbnail: this.formData.thumbnail.map(item => item.source),
article_status: status,
title: this.formData.title,
content: translateOutputContent(e.delta),
publish_date: this.formData.publish_date ? this.formData.publish_date : Date.now()
})
resolve()
}
})
}).catch((e) => {
console.error(e)
})
})
},
/**
* 提交表单
* @param {Object} value - 表单数据
*/
submitForm(value) {
uni.showLoading({
mask: true
})
// 使用 clientDB 提交数据
return db.collection(dbCollectionName).doc(this.formDataId).update(value).then(() => {
// 提交成功后,根据文章状态弹出不同的提示信息
uni.showToast({
icon: 'none',
title: value.article_status === 0 ? '保存成功' : this.formData.article_status === 0 ? '发布成功' : '更新成功'
})
try {
// 触发父页面事件,刷新数据
this.getOpenerEventChannel().emit('refreshData')
} catch (e) { }
// 清除自动保存内容
this.clearAutoSaveStorage && this.clearAutoSaveStorage()
// 延迟 500ms 后,如果文章状态为已发布,返回上一页。
setTimeout(() => {
if (value.article_status === 1) {
uni.navigateBack()
}
}, 500)
}).catch((err) => {
uni.showModal({
content: err.message || '请求服务失败',
showCancel: false
})
}).finally(() => {
uni.hideLoading()
})
},
/**
* 文本内容变化时更新字数统计
* @param {Object} e - 文本内容变化事件
*/
onTextChange(e) {
this.wordCount = e.detail
// 自动保存
this.autoSaveContent && this.autoSaveContent()
},
/**
* 返回上一页或跳转到文章列表页
*/
navigateBack() {
const pages = getCurrentPages()
if (pages.length > 1) {
uni.navigateBack(-1)
} else {
uni.redirectTo({
url: '/uni_modules/uni-cms/pages/article/list/list'
})
}
},
openInsertImageDrawer(index) {
this.$refs.insertImageDrawer.open()
this.coverIndex = index
},
async onInsertCover(selectMediaItems) {
const coverIndex = this.coverIndex !== null ? this.coverIndex : 0
const image = selectMediaItems[0]
const newThumbnail = [...this.formData.thumbnail]
const parseImages = await parseImageUrl(image.src)
newThumbnail[coverIndex] = parseImages[0]
this.$refs.insertImageDrawer.close()
this.formData.thumbnail = newThumbnail
},
removeThumbnail(index) {
const newThumbnail = [...this.formData.thumbnail]
newThumbnail[index] = {}
this.formData.thumbnail = newThumbnail
this.autoGetCover = false
},
async previewArticle () {
if (this.formDataId) {
// 先保存文章
await this.submit(0)
this.$refs.popup.open()
}
}
}
}
</script>
<style lang="scss">
@import '@/uni_modules/uni-cms/common/style/article-detail.scss';
</style>