Files
front-pc/pages/channel/create/index.vue
2025-08-28 22:11:31 +08:00

353 lines
10 KiB
Vue
Raw Permalink 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>
<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>
<div style="border: 1px solid #eeeeee; margin-top: 10px" class="rounded">
<WeToolbar style="border-bottom: 1px solid #eeeeee" :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" />
<WeEditor
style="min-height: 300px; overflow-y: hidden"
v-model="valueHtml"
:defaultConfig="editorConfig"
:mode="mode"
@onCreated="handleCreated"
@onChange="handleChange"
@onDestroyed="handleDestroyed"
@onFocus="handleFocus"
@onBlur="handleBlur"
@customAlert="customAlert"
@customPaste="customPaste"
/>
</div>
<!-- 按钮区域 -->
<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 '@wangeditor/editor/dist/css/style.css' // 引入 css
// import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { onBeforeUnmount, shallowRef } from 'vue'
import useUserStore from '~/stores/user'
const userStore = useUserStore()
// 获取从其他地方传过来的参数
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 mode = 'default'
const isClient = ref(false)
if (import.meta.client) {
isClient.value = true
}
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()
// 内容 HTML
const valueHtml = ref('<p></p>')
// 模拟 ajax 异步获取内容
onMounted(() => {
setTimeout(() => {
valueHtml.value = '<p></p>'
}, 1500)
})
const toolbarConfig = {}
const editorConfig = {
placeholder: '请输入内容...',
// 上传图片的配置
MENU_CONF: {
uploadImage: {
server: 'https://tuxixi.net/prod-api/app-api/infra/file/upload',
fieldName: 'file', //这个是参数名字
headers: {
//配置token 接口需要就配 不需要就不用
Authorization: `Bearer ${userStore.token}`,
},
customInsert(res: any, insertFn: any) {
// 这个是获取接口返回的数据
insertFn(res.data) // 从 res 中找到 url也就是接口返回的图片地址然后插入图片
},
},
uploadVideo: {
server: 'https://tuxixi.net/prod-api/app-api/infra/file/upload',
fieldName: 'file', //这个是参数名字
maxFileSize: 1200 * 1024 * 1024, // 1200M
headers: {
//配置token 接口需要就配 不需要就不用
Authorization: `Bearer ${userStore.token}`,
},
customInsert(res: any, insertFn: any) {
// 这个是获取接口返回的数据
insertFn(res.data) // 从 res 中找到 url也就是接口返回的图片地址然后插入图片
},
},
},
}
// 表单数据
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 (!valueHtml.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: valueHtml.value,
projectDicId: formData.value.projectDicId,
channelId: formData.value.channelId,
})
.then((res) => {
console.log(res)
if (res.code === 0) {
ElMessage.success('发表成功')
router.back()
// router.push('/communication/channel')
}
})
.catch((err) => {
console.log(err)
})
.finally(() => {
post_loading.value = false
})
})
}
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
const handleCreated = (editor: any) => {
editorRef.value = editor // 记录 editor 实例,重要!
}
const handleChange = (editor: any) => {
console.log('change:', editor.getHtml())
}
const handleDestroyed = (editor: any) => {
console.log('destroyed', editor)
}
const handleFocus = (editor: any) => {
console.log('focus', editor)
}
const handleBlur = (editor: any) => {
console.log('blur', editor)
}
const customAlert = (info: any, type: any) => {
alert(`【自定义提示】${type} - ${info}`)
}
const customPaste = (editor: any, event: any, callback: any) => {
console.log('ClipboardEvent 粘贴事件对象', event)
// const html = event.clipboardData.getData('text/html') // 获取粘贴的 html
const text = event.clipboardData.getData('text/plain') // 获取粘贴的纯文本
// const rtf = event.clipboardData.getData('text/rtf') // 获取 rtf 数据(如从 word wsp 复制粘贴)
// 自定义插入内容
editor.insertText(text)
// 返回 false ,阻止默认粘贴行为
event.preventDefault()
callback(false) // 返回值注意vue 事件的返回值,不能用 return
// 返回 true ,继续默认的粘贴行为
// callback(true)
}
/** 获取频道列表 */
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>