Refactor code structure and remove redundant changes
This commit is contained in:
201
pages/channel/components/ChannelHeader.vue
Normal file
201
pages/channel/components/ChannelHeader.vue
Normal file
@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<div class="channel-header">
|
||||
<div class="header-container">
|
||||
<!-- Logo and Title Section -->
|
||||
<div class="logo-title-section">
|
||||
<div class="logo">
|
||||
<img :src="lunTanRes.channelIcon" alt="JRS Logo" />
|
||||
</div>
|
||||
<div class="title-section">
|
||||
<h1 class="main-title">#{{ lunTanRes.channelTitle }}</h1>
|
||||
<div class="action-buttons">
|
||||
<el-button v-if="!lunTanRes.isFollow" type="danger" class="subscribe-btn" @click="handleFollow"
|
||||
><el-icon class="mr-4px color-#fff!"><Plus /></el-icon> 关注
|
||||
</el-button>
|
||||
<el-button v-else type="danger" class="subscribe-btn" @click="handleUnfollow"> 取消关注 </el-button>
|
||||
<el-button type="danger" class="post-btn" @click="handleClick">
|
||||
<el-icon class="mr-4px color-#fff!"><EditPen /></el-icon> 发帖
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- Channel Info -->
|
||||
<div class="channel-info">
|
||||
<span class="info-item">话题介绍</span>
|
||||
<span class="info-item">{{ lunTanRes.channelProfile }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="channel-stats">
|
||||
<div class="stats-item">
|
||||
<span class="stats-label">话题热度</span>
|
||||
<span class="stats-value"><i class="el-icon-arrow-up"></i> 220.4w</span>
|
||||
</div>
|
||||
<div class="stats-item">
|
||||
<span class="stats-label">关注人数</span>
|
||||
<span class="stats-value"><i class="el-icon-arrow-up"></i> {{ lunTanRes.followCount }}人</span>
|
||||
</div>
|
||||
<div class="stats-item">
|
||||
<span class="stats-label">当前有</span>
|
||||
<span class="stats-value"><i class="el-icon-arrow-up"></i> {{ lunTanRes.chatUserCount }}人聊天</span>
|
||||
<span class="stats-value ml-2px cursor-pointer color-#1a65ff!" @click="handleChat">立即加入</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tags -->
|
||||
<div class="channel-tags">
|
||||
<span class="tag-label">标签:</span>
|
||||
<span v-for="(item, index) in lunTanRes.hotTags" :key="index" class="tag-item"
|
||||
>{{ item }}{{ index === lunTanRes.hotTags.length - 1 ? '' : '、' }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 打开群聊窗口 -->
|
||||
<ChatPage
|
||||
v-if="isChat"
|
||||
v-model:is-chat="isChat"
|
||||
:chat-title="lunTanRes.channelTitle"
|
||||
:chat-description="lunTanRes.channelProfile"
|
||||
:channel-id="lunTanRes.channelId"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { Plus, EditPen } from '@element-plus/icons-vue'
|
||||
import { ChannelRespVO } from '@/api/channel/types'
|
||||
import { createChannelFollow, deleteChannelFollow } from '@/api/channel/index'
|
||||
import ChatPage from '@/pages/chat-page/index.vue'
|
||||
import useUserStore from '@/store/user'
|
||||
const userStore = useUserStore()
|
||||
|
||||
const lunTanRes = defineModel<ChannelRespVO>('modelValue', {
|
||||
required: true,
|
||||
})
|
||||
|
||||
const handleClick = () => {
|
||||
window.open('/channel/create?channelId=' + lunTanRes.value.channelId, '_blank')
|
||||
}
|
||||
const handleFollow = () => {
|
||||
createChannelFollow({ channelId: lunTanRes.value.channelId }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
lunTanRes.value.isFollow = true
|
||||
ElMessage.success('关注成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
const handleUnfollow = () => {
|
||||
deleteChannelFollow({ channelId: lunTanRes.value.channelId }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
lunTanRes.value.isFollow = false
|
||||
ElMessage.success('取消关注成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 订阅群聊
|
||||
const isChat = ref(false)
|
||||
const handleChat = async () => {
|
||||
// 登录判断
|
||||
if (!userStore.token) {
|
||||
ElMessage.warning('请先登录')
|
||||
return
|
||||
}
|
||||
await userStore.mqttClient?.subscribe(`zbjk_message_group/${lunTanRes.value.channelId}`)
|
||||
isChat.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.channel-header {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
/* box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); */
|
||||
border: 1px solid #eeeeee;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.logo-title-section {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.logo img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.title-section {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.main-title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.subscribe-btn,
|
||||
.post-btn {
|
||||
font-size: 14px;
|
||||
padding: 0px 12px !important;
|
||||
}
|
||||
|
||||
.channel-info {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.channel-stats {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.stats-value {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.channel-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.tag-label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tag-item {
|
||||
color: #1a65ff;
|
||||
}
|
||||
</style>
|
||||
47
pages/channel/components/HotLlabel.vue
Normal file
47
pages/channel/components/HotLlabel.vue
Normal file
@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="box-border w-320px border border-[#EEEEEE] rounded-8px border-solid bg-[#FFFFFF] px-23px py-25px">
|
||||
<div class="text-16px text-[#333333] font-normal">频道列表</div>
|
||||
<div class="mt-30px">
|
||||
<el-row>
|
||||
<el-col v-for="(item, index) in channelIdList" :key="index" :span="8" class="mb-10px">
|
||||
<span
|
||||
class="cursor-pointer text-14px text-[#999999] font-normal"
|
||||
:class="{ active: channelId === item.channelId }"
|
||||
@click="handleClick(item.channelId)"
|
||||
>{{ item.channelTitle }}</span
|
||||
>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { list } from '@/api/channel/index'
|
||||
|
||||
const channelId = defineModel('modelValue', {
|
||||
required: true,
|
||||
})
|
||||
/** 获取频道列表 */
|
||||
const channelIdList = ref<any>([])
|
||||
const getChannelIdList = () => {
|
||||
list().then((res) => {
|
||||
channelIdList.value = res.data
|
||||
if (channelIdList.value.length > 0) {
|
||||
channelId.value = channelIdList.value[0].channelId
|
||||
}
|
||||
})
|
||||
}
|
||||
getChannelIdList()
|
||||
|
||||
const handleClick = (id: number) => {
|
||||
console.log(id)
|
||||
channelId.value = id
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.active {
|
||||
color: #1a65ff !important;
|
||||
}
|
||||
</style>
|
||||
18
pages/channel/components/LeftContent.vue
Normal file
18
pages/channel/components/LeftContent.vue
Normal file
@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<!-- 用户信息 -->
|
||||
<div class="flex flex-col">
|
||||
<UserInfo></UserInfo>
|
||||
<HotLlabel v-model="channelId" class="mt-18px"></HotLlabel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import UserInfo from './UserInfo.vue'
|
||||
import HotLlabel from './HotLlabel.vue'
|
||||
|
||||
const channelId = defineModel('modelValue', {
|
||||
required: true,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
80
pages/channel/components/RightContent.vue
Normal file
80
pages/channel/components/RightContent.vue
Normal file
@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div class="ml-19px w-100%">
|
||||
<ChannelHeader v-if="Object.keys(lunTanRes).length" v-model="lunTanRes"></ChannelHeader>
|
||||
<div class="mb-13px box-border flex flex-1 flex-col cursor-pointer gap-12px border border-[#EEEEEE] rounded-8px border-solid bg-[#FFFFFF] px-20px py-16px">
|
||||
<div
|
||||
v-for="(item, index) in pageRes.list"
|
||||
:key="index"
|
||||
class="flex justify-between border-b-1px border-b-[#eee] border-b-solid pb-8px"
|
||||
:class="{ 'border-b-0! pb-0px!': index === pageRes.list.length - 1 }"
|
||||
@click="handleClick(item.postsId)"
|
||||
>
|
||||
<div class="flex flex-1 items-center">
|
||||
<div class="ellipsis max-w-70% text-15px text-[#2d3137] font-normal">{{ item.postsTitle }}</div>
|
||||
<span class="ml-10px flex-shrink-0 text-13px color-#96999f">{{ item.likeNum || 0 }}人赞过</span>
|
||||
<span class="ml-10px flex-shrink-0 text-13px color-#96999f">{{ item.commentNum || 0 }}评论</span>
|
||||
<span class="ml-10px flex-shrink-0 text-13px color-#96999f">{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}发布</span>
|
||||
</div>
|
||||
<div class="w-100px flex flex-shrink-0 items-center justify-end">
|
||||
<span class="ellipsis text-13px color-#96999f">{{ item.creatorName }}</span>
|
||||
<!-- 删除 -->
|
||||
<el-button v-if="false" type="danger" size="small" @click="handleDelete(item.postsId)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-pagination
|
||||
v-if="pageRes.list.length > 0"
|
||||
v-model:current-page="pageNo"
|
||||
:page-size="10"
|
||||
layout="prev, pager, next"
|
||||
:total="pageRes.total"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
<!-- 暂无数据 -->
|
||||
<el-empty v-if="!pageRes.list.length" description="暂无数据"></el-empty>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { TpageRes, ChannelRespVO } from '@/api/channel/types'
|
||||
import { postsDelete } from '@/api/channel/index'
|
||||
import ChannelHeader from './ChannelHeader.vue'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const emit = defineEmits(['updatePageNo'])
|
||||
|
||||
const pageRes = defineModel<TpageRes>('modelValue', {
|
||||
required: true,
|
||||
})
|
||||
const lunTanRes = defineModel<ChannelRespVO>('lunTanRes', {
|
||||
required: true,
|
||||
})
|
||||
const pageNo = defineModel<number>('pageNo', {
|
||||
required: true,
|
||||
})
|
||||
|
||||
// const handleTags = (tags: string) => {
|
||||
// if (!tags) return []
|
||||
// return tags.split(',')
|
||||
// }
|
||||
|
||||
const handleCurrentChange = (current: number) => {
|
||||
emit('updatePageNo', current)
|
||||
}
|
||||
|
||||
const handleDelete = (channelId: number) => {
|
||||
console.log(channelId)
|
||||
postsDelete({
|
||||
id: channelId,
|
||||
}).then((res) => {
|
||||
console.log(res)
|
||||
})
|
||||
}
|
||||
|
||||
const handleClick = (channelId: number) => {
|
||||
// 新开窗口
|
||||
window.open(`/chat-detail?channelId=${channelId}`, '_blank')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
49
pages/channel/components/UserInfo.vue
Normal file
49
pages/channel/components/UserInfo.vue
Normal file
@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="box-border h-240px w-320px border border-[#EEEEEE] rounded-8px border-solid bg-[#FFFFFF] px-31px py-25px">
|
||||
<div class="flex">
|
||||
<div>
|
||||
<img src="@/assets/images/user2.png" alt="" srcset="" class="h-47px w-48px rounded-full" />
|
||||
</div>
|
||||
<div class="ml-13px">
|
||||
<div class="text-16px text-[#333333] font-normal">你好</div>
|
||||
<div class="mt-6px text-14px text-[#999999] font-normal">欢迎使用多多图纸~</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-26px flex justify-between text-14px text-[#999999] font-normal">
|
||||
<div class="flex flex-col items-center">
|
||||
<div>下载</div>
|
||||
<div class="mt-4px">-</div>
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<div>查看</div>
|
||||
<div class="mt-4px">-</div>
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<div>点赞</div>
|
||||
<div class="mt-4px">-</div>
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<div>评论</div>
|
||||
<div class="mt-4px">-</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mt-25px h-36px w-260px cursor-pointer rounded-4px bg-[#1A65FF] text-center text-15px text-[#FFFFFF] font-normal line-height-36px"
|
||||
@click="handleClick"
|
||||
>发帖子</div
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import useUserStore from '@/store/user'
|
||||
const userStore = useUserStore()
|
||||
const handleClick = () => {
|
||||
// 判断是否登录
|
||||
if (!userStore.token) {
|
||||
ElMessage.warning('请先登录')
|
||||
return
|
||||
}
|
||||
window.open('/channel/create', '_blank')
|
||||
}
|
||||
</script>
|
||||
367
pages/channel/components/create.vue
Normal file
367
pages/channel/components/create.vue
Normal file
@ -0,0 +1,367 @@
|
||||
<template>
|
||||
<KlNavTab></KlNavTab>
|
||||
<div class="mx-auto w-1440px">
|
||||
<!-- 使用 el-form 重构表单区域 -->
|
||||
<el-form ref="formRef" inline :model="formData" label-width="110px" class="custom-form mb-20px mt-20px border rounded p-20px!">
|
||||
<el-form-item label="标题:" prop="postsTitle" :rules="{ required: true, message: '请输入标题', trigger: 'blur' }">
|
||||
<el-input v-model="formData.postsTitle" placeholder="请输入标题" class="w-300px!" minlength="4" maxlength="40"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类:" class="mb-10px" prop="projectDicId" :rules="{ required: true, message: '请选择分类', trigger: ['blur', 'change'] }">
|
||||
<el-select v-model="formData.projectDicId" placeholder="请选择分类" class="w-300px!">
|
||||
<el-option v-for="item in projectTypeList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="标签:" class="mb-10px" prop="postsTags" :rules="{ required: true, message: '请输入标签', trigger: ['blur', 'change'] }">
|
||||
<el-select
|
||||
v-model="formData.postsTags"
|
||||
:remote-method="remoteMethod"
|
||||
:loading="loading"
|
||||
filterable
|
||||
remote
|
||||
multiple
|
||||
placeholder="请输入搜索标签"
|
||||
class="w-300px!"
|
||||
>
|
||||
<el-option v-for="(item, index) in labelsList" :key="index" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="频道列表:" class="mb-10px" prop="channelId" :rules="{ required: true, message: '请选择频道', trigger: ['blur', 'change'] }">
|
||||
<el-select v-model="formData.channelId" placeholder="请选择频道" class="w-300px!">
|
||||
<el-option v-for="item in channelIdList" :key="item.channelId" :label="item.channelTitle" :value="item.channelId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="上传封面:" prop="postsCover" :rules="{ required: true, message: '请上传封面', trigger: ['blur', 'change'] }">
|
||||
<KlUploader v-model:file-list="formData.postsCover" :limit="1" :size="1" tips="上传图片支持jpg/gif/png格式"> </KlUploader>
|
||||
</el-form-item> -->
|
||||
</el-form>
|
||||
|
||||
<Editor :id="tinymceId" v-model="myValue" :init="init" :disabled="disabled" :placeholder="placeholder" />
|
||||
<!-- 按钮区域 -->
|
||||
<div class="mt-20px flex justify-end">
|
||||
<el-button :loading="post_loading" class="mr-10px" @click="previewContent">预览</el-button>
|
||||
<el-button :loading="post_loading" type="primary" @click="saveContent">发表</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { keywords } from '@/api/upnew/index'
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
import { create, list } from '@/api/channel/index'
|
||||
import { parent } from '@/api/upnew/index'
|
||||
import { upload } from '@/api/common/index' // 自定义上传方法
|
||||
import Editor from '@tinymce/tinymce-vue'
|
||||
import tinymce from 'tinymce/tinymce'
|
||||
import 'tinymce/themes/silver'
|
||||
import 'tinymce/themes/silver/theme'
|
||||
import 'tinymce/models/dom'
|
||||
import 'tinymce/icons/default'
|
||||
import 'tinymce/icons/default/icons'
|
||||
// 引入编辑器插件
|
||||
import 'tinymce/plugins/code' //编辑源码
|
||||
import 'tinymce/plugins/image' //插入编辑图片
|
||||
import 'tinymce/plugins/media' //插入视频
|
||||
import 'tinymce/plugins/link' //超链接
|
||||
import 'tinymce/plugins/preview' //预览
|
||||
// import 'tinymce/plugins/template' //模板
|
||||
import 'tinymce/plugins/table' //表格
|
||||
import 'tinymce/plugins/pagebreak' //分页
|
||||
import 'tinymce/plugins/lists' //列
|
||||
import 'tinymce/plugins/advlist' //列
|
||||
import 'tinymce/plugins/quickbars' //快速工具条
|
||||
import 'tinymce/plugins/wordcount' // 字数统计插件
|
||||
// import '@/assets/tinymce/langs/zh-Hans.js' //下载后的语言包
|
||||
// import 'tinymce/skins/content/default/content.css'
|
||||
// 获取从其他地方传过来的参数
|
||||
const channelId = route.query.channelId as string
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入帖子内容',
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 500,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
plugins: {
|
||||
type: [String, Array],
|
||||
default: 'code image link preview table quickbars pagebreak lists advlist',
|
||||
},
|
||||
toolbar: {
|
||||
type: [String, Array],
|
||||
default:
|
||||
'undo redo codesample bold italic underline strikethrough link alignleft aligncenter alignright alignjustify \
|
||||
bullist numlist outdent indent removeformat forecolor backcolor |formatselect fontselect fontsizeselect | \
|
||||
blocks fontfamily fontsize pagebreak lists image customvideoupload table preview | code selectall',
|
||||
},
|
||||
templates: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
})
|
||||
//用于接收外部传递进来的富文本
|
||||
const myValue = ref(props.value)
|
||||
const tinymceId = ref('vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + ''))
|
||||
const init = reactive({
|
||||
selector: '#' + tinymceId.value, //富文本编辑器的id,
|
||||
language_url: '/tinymce/langs/zh_CN.js', // 语言包的路径,具体路径看自己的项目,文档后面附上中文js文件
|
||||
language: 'zh-Hans', //语言
|
||||
skin_url: '/tinymce/skins/ui/oxide', // skin路径,具体路径看自己的项目
|
||||
content_css: '/tinymce/skins/content/default/content.css',
|
||||
menubar: true, //顶部菜单栏显示
|
||||
statusbar: true, // 底部的状态栏
|
||||
plugins: props.plugins,
|
||||
toolbar: props.toolbar,
|
||||
toolbar_mode: 'sliding',
|
||||
font_formats: 'Arial=arial,helvetica,sans-serif; 宋体=SimSun; 微软雅黑=Microsoft Yahei; Impact=impact,chicago;', //字体
|
||||
paste_convert_word_fake_lists: false, // 插入word文档需要该属性
|
||||
font_size_formats: '12px 14px 16px 18px 22px 24px 36px 72px', //文字大小
|
||||
height: props.height, //编辑器高度
|
||||
placeholder: props.placeholder,
|
||||
branding: false, //是否禁用"Powered by TinyMCE"
|
||||
promotion: false, //禁用升级按钮
|
||||
image_dimensions: false, //去除宽高属性
|
||||
paste_webkit_styles: 'all',
|
||||
paste_merge_formats: true,
|
||||
nonbreaking_force_tab: false,
|
||||
paste_auto_cleanup_on_paste: false,
|
||||
file_picker_types: 'file',
|
||||
resize: true,
|
||||
elementpath: true,
|
||||
content_style: `img {max-width:100%;} body{background-color: #fff;}`, // 直接自定义可编辑区域的css样式
|
||||
templates: props.templates,
|
||||
quickbars_selection_toolbar: 'forecolor backcolor bold italic underline strikethrough link',
|
||||
quickbars_image_toolbar: 'alignleft aligncenter alignright',
|
||||
quickbars_insert_toolbar: false,
|
||||
image_caption: true,
|
||||
image_advtab: true,
|
||||
convert_urls: false,
|
||||
images_upload_url: import.meta.env.VITE_BASE_API,
|
||||
images_upload_handler: function (blobInfo: any, progress: any) {
|
||||
console.log(blobInfo, progress)
|
||||
return new Promise((resolve, reject) => {
|
||||
const data = new FormData()
|
||||
data.append('file', blobInfo.blob())
|
||||
data.append('fieldName', blobInfo.filename())
|
||||
upload('/prod-api/app-api/infra/file/upload', data)
|
||||
.then((res) => {
|
||||
resolve(res.data)
|
||||
})
|
||||
.catch(() => {
|
||||
reject('Image upload failed')
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 添加自定义按钮
|
||||
setup: function (editor: any) {
|
||||
// 注册一个新的视频上传按钮
|
||||
editor.ui.registry.addButton('customvideoupload', {
|
||||
icon: 'embed', // 使用嵌入媒体图标
|
||||
tooltip: '上传视频',
|
||||
onAction: function () {
|
||||
// 创建文件输入元素
|
||||
const input = document.createElement('input')
|
||||
input.setAttribute('type', 'file')
|
||||
input.setAttribute('accept', 'video/*')
|
||||
|
||||
// 处理文件选择事件
|
||||
input.onchange = function () {
|
||||
if (input.files && input.files[0]) {
|
||||
const file = input.files[0]
|
||||
const data = new FormData()
|
||||
data.append('file', file)
|
||||
data.append('fieldName', file.name)
|
||||
|
||||
// 可以在这里添加上传进度显示
|
||||
|
||||
upload('/prod-api/app-api/infra/file/upload', data)
|
||||
.then((res) => {
|
||||
// 插入视频到编辑器
|
||||
editor.insertContent(`
|
||||
<video controls width="400">
|
||||
<source src="${res.data}" type="video/${file.name.split('.').pop()}">
|
||||
您的浏览器不支持视频标签
|
||||
</video>
|
||||
`)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('视频上传失败:', error)
|
||||
// 可以添加错误提示
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 触发文件选择
|
||||
input.click()
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
preview_styles: true,
|
||||
...props.options,
|
||||
})
|
||||
|
||||
// 表单数据
|
||||
const formData = ref({
|
||||
projectDicId: undefined,
|
||||
postsTags: [],
|
||||
postsCover: [] as any,
|
||||
postsTitle: '',
|
||||
channelId: channelId || undefined,
|
||||
})
|
||||
|
||||
const formRef = ref()
|
||||
const post_loading = ref(false)
|
||||
const saveContent = () => {
|
||||
formRef.value.validate().then(() => {
|
||||
if (!myValue.value) {
|
||||
ElMessage.error('请输入帖子内容')
|
||||
return
|
||||
}
|
||||
post_loading.value = true
|
||||
create({
|
||||
postsTitle: formData.value.postsTitle,
|
||||
// postsCover: formData.value.postsCover[0].url,
|
||||
postsTags: formData.value.postsTags.join(','),
|
||||
postsContent: myValue.value,
|
||||
projectDicId: formData.value.projectDicId,
|
||||
channelId: formData.value.channelId,
|
||||
})
|
||||
.then((res) => {
|
||||
console.log(res)
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('发表成功')
|
||||
router.push('/communication/channel')
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
.finally(() => {
|
||||
post_loading.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
const previewContent = () => {
|
||||
// 获取编辑器实例
|
||||
const editor = tinymce.get(tinymceId.value)
|
||||
// 调用编辑器的预览命令
|
||||
if (editor) {
|
||||
editor.execCommand('mcePreview')
|
||||
}
|
||||
}
|
||||
//在onMounted中初始化编辑器
|
||||
onMounted(() => {
|
||||
tinymce.init({})
|
||||
})
|
||||
|
||||
/** 获取频道列表 */
|
||||
const channelIdList = ref<any>([])
|
||||
const getChannelIdList = () => {
|
||||
list().then((res) => {
|
||||
channelIdList.value = res.data
|
||||
})
|
||||
}
|
||||
getChannelIdList()
|
||||
|
||||
const projectTypeList = ref<any>([])
|
||||
/** 获取分类下拉框 */
|
||||
const getParent = () => {
|
||||
parent({
|
||||
type: 1,
|
||||
parentId: 0,
|
||||
}).then((res) => {
|
||||
projectTypeList.value = res.data
|
||||
})
|
||||
}
|
||||
getParent()
|
||||
|
||||
const loading = ref(false)
|
||||
/** 获取标签 */
|
||||
const labelsList = ref<any>([])
|
||||
const remoteMethod = (query: string) => {
|
||||
if (query) {
|
||||
loading.value = true
|
||||
keywords({
|
||||
type: 1,
|
||||
keywords: query,
|
||||
})
|
||||
.then((res) => {
|
||||
labelsList.value = res.data
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
} else {
|
||||
labelsList.value = []
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.custom-form {
|
||||
border: 2px solid #eeeeee;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.upload-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.cover-uploader {
|
||||
:deep(.el-upload) {
|
||||
width: fit-content;
|
||||
}
|
||||
}
|
||||
|
||||
.image-error {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #909399;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.image-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
:deep(.el-form-item__label) {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
:deep(.el-button--primary.is-link) {
|
||||
padding: 0;
|
||||
height: auto;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.text-gray-500 {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.text-12px {
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user