| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- <template>
- <div class="article-edit">
- <div>
- <div class="editor-wrap">
- <!-- 文章标题、类型 -->
- <ElRow :gutter="10">
- <ElCol :span="18">
- <ElInput
- v-model.trim="articleName"
- placeholder="请输入文章标题(最多100个字符)"
- maxlength="100"
- />
- </ElCol>
- <ElCol :span="6">
- <ElSelect v-model="articleType" placeholder="请选择文章类型" filterable>
- <ElOption
- v-for="item in articleTypes"
- :key="item.id"
- :label="item.name"
- :value="item.id"
- />
- </ElSelect>
- </ElCol>
- </ElRow>
- <!-- 富文本编辑器 -->
- <ArtWangEditor class="el-top" v-model="editorHtml" />
- <div class="form-wrap">
- <h2>发布设置</h2>
- <!-- 图片上传 -->
- <ElForm>
- <ElFormItem label="封面">
- <div class="el-top upload-container">
- <ElUpload
- class="cover-uploader"
- :action="uploadImageUrl"
- :headers="uploadHeaders"
- :show-file-list="false"
- :on-success="onSuccess"
- :on-error="onError"
- :before-upload="beforeUpload"
- >
- <div v-if="!cover" class="upload-placeholder">
- <ElIcon class="upload-icon"><Plus /></ElIcon>
- <div class="upload-text">点击上传封面</div>
- </div>
- <img v-else :src="cover" class="cover-image" />
- </ElUpload>
- <div class="el-upload__tip">建议尺寸 16:9,jpg/png 格式</div>
- </div>
- </ElFormItem>
- <ElFormItem label="可见">
- <ElSwitch v-model="visible" />
- </ElFormItem>
- </ElForm>
- <div style="display: flex; justify-content: flex-end">
- <ElButton type="primary" @click="submit" style="width: 100px">
- {{ pageMode === PageModeEnum.Edit ? '保存' : '发布' }}
- </ElButton>
- </div>
- </div>
- </div>
- </div>
- <!-- <div class="outline-wrap">
- <div class="item" v-for="(item, index) in outlineList" :key="index">
- <p :class="`level${item.level}`">{{ item.text }}</p>
- </div>
- </div> -->
- </div>
- </template>
- <script setup lang="ts">
- import { Plus } from '@element-plus/icons-vue'
- import { ApiStatus } from '@/utils/http/status'
- import { ElMessage } from 'element-plus'
- import { useUserStore } from '@/store/modules/user'
- import EmojiText from '@/utils/ui/emojo'
- import { PageModeEnum } from '@/enums/formEnum'
- import axios from 'axios'
- import { useCommon } from '@/composables/useCommon'
- defineOptions({ name: 'ArticlePublish' })
- const route = useRoute()
- const userStore = useUserStore()
- let { accessToken } = userStore
- // 上传路径
- const uploadImageUrl = `${import.meta.env.VITE_API_URL}/api/common/upload`
- // 传递 token
- const uploadHeaders = { Authorization: accessToken }
- let pageMode: PageModeEnum = PageModeEnum.Add // 页面类型 新增 | 编辑
- const articleName = ref('') // 文章标题
- const articleType = ref() // 文章类型
- const articleTypes = ref() // 类型列表
- const editorHtml = ref('') // 编辑器内容
- const createDate = ref('') // 创建时间
- const cover = ref('') // 图片
- const visible = ref(true) // 可见
- // const outlineList = ref()
- onMounted(() => {
- useCommon().scrollToTop()
- getArticleTypes()
- initPageMode()
- })
- // 初始化页面类型 新增 | 编辑
- const initPageMode = () => {
- const { id } = route.query
- pageMode = id ? PageModeEnum.Edit : PageModeEnum.Add
- if (pageMode === PageModeEnum.Edit && id) {
- initEditArticle()
- } else {
- initAddArticle()
- }
- }
- // 初始化编辑文章的逻辑
- const initEditArticle = () => {
- getArticleDetail()
- }
- // 初始化新增文章逻辑
- const initAddArticle = () => {
- createDate.value = formDate(useNow().value)
- }
- // 获取文章类型
- const getArticleTypes = async () => {
- try {
- const response = await axios.get('https://www.qiniu.lingchen.kim/classify.json')
- if (response.data.code === 200) {
- articleTypes.value = response.data.data
- }
- } catch (error) {
- console.error('Error fetching JSON data:', error)
- }
- // try {
- // const res = await ArticleService.getArticleTypes({})
- // if (res.code === ApiStatus.success) {
- // articleTypes.value = res.data
- // }
- // } catch (err) { }
- }
- const getArticleDetail = async () => {
- const res = await axios.get('https://www.qiniu.lingchen.kim/blog_list.json')
- if (res.data.code === ApiStatus.success) {
- let { title, blog_class, html_content } = res.data.data
- articleName.value = title
- articleType.value = Number(blog_class)
- editorHtml.value = html_content
- }
- }
- // const getOutline = (content: string) => {
- // const regex = /<h([1-3])>(.*?)<\/h\1>/g
- // const headings = []
- // let match
- // while ((match = regex.exec(content)) !== null) {
- // headings.push({ level: match[1], text: match[2] })
- // }
- // outlineList.value = headings
- // }
- // 提交
- const submit = () => {
- if (pageMode === PageModeEnum.Edit) {
- editArticle()
- } else {
- addArticle()
- }
- }
- // 格式化日期
- const formDate = (date: string | Date): string => {
- return useDateFormat(date, 'YYYY-MM-DD').value
- }
- // 验证输入
- const validateArticle = () => {
- if (!articleName.value) {
- ElMessage.error(`请输入文章标题`)
- return false
- }
- if (!articleType.value) {
- ElMessage.error(`请选择文章类型`)
- return false
- }
- if (editorHtml.value === '<p><br></p>') {
- ElMessage.error(`请输入文章内容`)
- return false
- }
- if (!cover.value) {
- ElMessage.error(`请上传图片`)
- return false
- }
- return true
- }
- // 添加文章
- const addArticle = async () => {
- try {
- if (!validateArticle()) return
- editorHtml.value = delCodeTrim(editorHtml.value)
- } catch (err) {
- console.error(err)
- }
- }
- // 编辑文章
- const editArticle = async () => {
- try {
- if (!validateArticle()) return
- editorHtml.value = delCodeTrim(editorHtml.value)
- } catch (err) {
- console.error(err)
- }
- }
- const delCodeTrim = (content: string): string => {
- return content.replace(/(\s*)<\/code>/g, '</code>')
- }
- const onSuccess = (response: any) => {
- cover.value = response.data.url
- ElMessage.success(`图片上传成功 ${EmojiText[200]}`)
- }
- const onError = () => {
- ElMessage.error(`图片上传失败 ${EmojiText[500]}`)
- }
- // 添加上传前的校验
- const beforeUpload = (file: File) => {
- const isImage = file.type.startsWith('image/')
- const isLt2M = file.size / 1024 / 1024 < 2
- if (!isImage) {
- ElMessage.error('只能上传图片文件!')
- return false
- }
- if (!isLt2M) {
- ElMessage.error('图片大小不能超过 2MB!')
- return false
- }
- return true
- }
- </script>
- <style lang="scss" scoped>
- .article-edit {
- .editor-wrap {
- max-width: 1000px;
- margin: 20px auto;
- .el-top {
- margin-top: 10px;
- }
- .form-wrap {
- padding: 20px;
- margin-top: 20px;
- background-color: var(--art-main-bg-color);
- border: 1px solid var(--art-border-color);
- border-radius: calc(var(--custom-radius) / 2 + 2px) !important;
- h2 {
- margin-bottom: 20px;
- font-size: 20px;
- font-weight: 500;
- }
- }
- }
- .outline-wrap {
- box-sizing: border-box;
- width: 280px;
- padding: 20px;
- border: 1px solid #e3e3e3;
- border-radius: 8px;
- .item {
- p {
- height: 30px;
- font-size: 13px;
- line-height: 30px;
- cursor: pointer;
- }
- .level3 {
- padding-left: 10px;
- }
- }
- }
- .upload-container {
- .cover-uploader {
- position: relative;
- overflow: hidden;
- cursor: pointer;
- border-radius: 6px;
- transition: var(--el-transition-duration);
- &:hover {
- border-color: var(--el-color-primary);
- }
- .upload-placeholder {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- width: 260px;
- height: 160px;
- border: 1px dashed #d9d9d9;
- border-radius: 6px;
- .upload-icon {
- font-size: 28px;
- color: #8c939d;
- }
- .upload-text {
- margin-top: 8px;
- font-size: 14px;
- color: #8c939d;
- }
- }
- .cover-image {
- display: block;
- width: 260px;
- height: 160px;
- object-fit: cover;
- }
- }
- .el-upload__tip {
- margin-top: 8px;
- font-size: 12px;
- color: #666;
- }
- }
- }
- </style>
|