Commit c5535c8d by lijiabin

【需求 17679】 feat: 优化发布等

parent f4265cd7
......@@ -2,7 +2,7 @@
<div class="bg-white p-6 mb-6 rounded-lg shadow-sm">
<div class="flex-1 bg-white rounded-lg border border-gray-200">
<!-- 主输入区域 -->
<div class="flex gap-3 mb-4 items-start">
<div class="flex gap-3 mb-2 items-start">
<!-- 用户头像 -->
<el-avatar :size="48" :src="userAvatar" class="flex-shrink-0">
<el-icon><User /></el-icon>
......@@ -78,11 +78,14 @@
<div class="flex items-center justify-between pl-15">
<!-- 左侧工具按钮 -->
<div class="flex items-center gap-1">
<el-tooltip content="添加标签" placement="top">
<el-tooltip content="添加标签" placement="top" :visible="visibleTagTooltip">
<el-button
ref="tagButtonRef"
text
class="w-10 h-10 text-gray-500 hover:bg-gray-100 hover:text-gray-700 rounded-lg"
@click="handleAddTag"
@mouseenter="visibleTagTooltip = true"
@mouseleave="visibleTagTooltip = false"
>
<el-icon size="18"><CollectionTag /></el-icon>
</el-button>
......@@ -130,6 +133,7 @@
<el-button
type="primary"
:disabled="disabledSubmit"
class="px-6 py-2 bg-blue-500 hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed rounded-lg text-white text-sm font-medium shadow-sm hover:shadow-md transition-all duration-200"
@click="handlePublish(ReleaseStatusTypeEnum.PUBLISH)"
>
......@@ -143,6 +147,10 @@
v-model:tagList="form.tagList"
ref="selectTagsDialogRef"
/>
<el-tour v-model="openTour" :mask="false" placement="left-start" type="primary">
<el-tour-step :target="tagButtonRef?.$el" description="在这里选择标签" placement="top" />
</el-tour>
</div>
</template>
<!-- 发布实践 或者 问吧 的 发布框 -->
......@@ -158,6 +166,9 @@ import { Close } from '@element-plus/icons-vue'
import { addOrUpdatePractice, addOrUpdateArticle } from '@/api'
import type { AddOrUpdatePracticeDto } from '@/api'
import type { BooleanFlag } from '@/constants'
import type { ElButton } from 'element-plus'
import { useAnimate } from '@vueuse/core'
import { isVisible } from 'element-plus/es/utils/index.mjs'
type ArticleType = ArticleTypeEnum.QUESTION | ArticleTypeEnum.PRACTICE
......@@ -194,6 +205,31 @@ const selectTagsDialogRef =
const fileInputRef = useTemplateRef<HTMLInputElement>('fileInputRef')
const openTour = ref(false)
const disabledSubmit = computed(() => {
return !form.value.title || !form.value.content
})
const tagButtonRef = useTemplateRef<InstanceType<typeof ElButton>>('tagButtonRef')
const tagButtonEl = computed(() => tagButtonRef.value?.$el)
const visibleTagTooltip = ref(false)
const DURATION = 500
const { play } = useAnimate(
tagButtonEl,
[
{ transform: 'translateY(0)' },
{ transform: 'translateY(-10px)' },
{ transform: 'translateY(0)' },
],
{
duration: DURATION,
easing: 'ease-in-out',
immediate: false,
},
)
const [form, resetForm] = useResetData({
title: '',
content: '',
......@@ -230,16 +266,14 @@ const handleDeleteImg = (img: string) => {
}
const validateForm = () => {
if (!form.value.title) {
ElMessage.error('请输入标题')
return false
}
if (!form.value.content) {
ElMessage.error('请输入内容')
return false
}
if (!form.value.mainTagId) {
ElMessage.error('请选择主标签')
ElMessage.warning({
message: '请选择主标签',
offset: 200,
})
play()
visibleTagTooltip.value = true
return false
}
......
......@@ -37,7 +37,17 @@ export function registerRouterGuards(router: Router) {
path: to.path, // 重定向到首页 去除code 重定向到首页 没有code 不会进入到这里了
replace: true,
}
} else {
}
// else if (
// to.fullPath.includes('/homePage/askTab#tabsRef?t=') &&
// !from.fullPath.includes('/homePage')
// ) {
// return {
// path: to.path,
// // replace: true,
// }
// }
else {
return true
}
})
......
<template>
<div class="bg-white p-6 mb-6 rounded-lg shadow-sm">
<div class="flex-1 bg-white rounded-lg border border-gray-200">
<!-- 主输入区域 -->
<div class="flex gap-3 mb-4 items-start">
<!-- 用户头像 -->
<el-avatar :size="48" :src="userInfo.avatar" class="flex-shrink-0">
<el-icon><User /></el-icon>
</el-avatar>
<!-- 输入区域 -->
<div class="flex-1">
<!-- 话题标签输入 -->
<div class="mb-4">
<el-input v-model="form.title" placeholder="实践标题" class="tag-input" clearable />
</div>
<!-- 主要内容输入 -->
<div class="relative mb-3">
<el-input
type="textarea"
placeholder="请输入实践内容"
:rows="3"
:maxlength="500"
resize="none"
class="main-textarea"
v-model="form.content"
/>
<!-- 字符计数 -->
<div class="absolute bottom-3 right-3 text-xs text-gray-400">1/30</div>
</div>
<!-- 标签内容 -->
<div class="mb-2">
<!-- 选择的标签内容 -->
<div class="flex items-center gap-2">
<span v-if="mainTagText" class="text-sm text-gray-500"
>主标签:
<el-tag>{{ mainTagText }}</el-tag>
</span>
<span v-if="subTagTextList.length > 0" class="text-sm text-gray-500"
>副标签:
<el-tag class="mr-2" v-for="tag in subTagTextList" :key="tag">{{ tag }}</el-tag>
</span>
</div>
</div>
<!-- 图片相关 -->
<div v-if="form.imgUrl.length" class="flex flex-wrap gap-2">
<!-- 删除图片 -->
<div
class="relative w-20 h-20 rounded-lg overflow-hidden group"
v-for="img in form.imgUrl"
:key="img"
>
<div
class="absolute top-1 right-1 z-10 w-5 h-5 flex items-center justify-center bg-black/60 rounded-full cursor-pointer opacity-0 group-hover:opacity-100 transition-all duration-200 hover:bg-black/80 hover:scale-110"
@click="handleDeleteImg(img)"
>
<el-icon class="text-white text-xs">
<Close />
</el-icon>
</div>
<el-image
:src="img"
class="w-full h-full rounded-lg border border-gray-200"
fit="cover"
/>
</div>
</div>
</div>
</div>
<!-- 工具栏 -->
<div class="flex items-center justify-between pl-15">
<!-- 左侧工具按钮 -->
<div class="flex items-center gap-1">
<el-tooltip content="添加标签" placement="top">
<el-button
text
class="w-10 h-10 text-gray-500 hover:bg-gray-100 hover:text-gray-700 rounded-lg"
@click="handleAddTag"
>
<el-icon size="18"><CollectionTag /></el-icon>
</el-button>
</el-tooltip>
<!-- 隐藏上传文件的input -->
<input type="file" class="hidden" ref="fileInputRef" @change="handleFileChange" />
<el-tooltip content="添加图片" placement="top">
<el-button
text
class="w-10 h-10 text-gray-500 hover:bg-gray-100 hover:text-gray-700 rounded-lg"
@click="fileInputRef?.click()"
>
<el-icon size="18"><Picture /></el-icon>
</el-button>
</el-tooltip>
<!-- <el-tooltip content="添加视频" placement="top">
<el-button
text
class="w-10 h-10 text-gray-500 hover:bg-gray-100 hover:text-gray-700 rounded-lg"
>
<el-icon size="18"><VideoPlay /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="添加附件" placement="top">
<el-button
text
class="w-10 h-10 text-gray-500 hover:bg-gray-100 hover:text-gray-700 rounded-lg"
>
<el-icon size="18"><Paperclip /></el-icon>
</el-button>
</el-tooltip> -->
</div>
<!-- 右侧操作按钮 -->
<div class="flex items-center gap-3">
<el-button
class="px-4 py-2 text-gray-600 hover:text-gray-800 hover:bg-gray-50 rounded-lg border border-gray-200 text-sm"
@click="handlePublish(ReleaseStatusTypeEnum.DRAFT)"
>
存草稿
</el-button>
<el-button
type="primary"
class="px-6 py-2 bg-blue-500 hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed rounded-lg text-white text-sm font-medium shadow-sm hover:shadow-md transition-all duration-200"
@click="handlePublish(ReleaseStatusTypeEnum.PUBLISH)"
>
发布实践
</el-button>
</div>
</div>
</div>
<SelectTagsDialog
v-model:mainTagId="form.mainTagId"
v-model:tagList="form.tagList"
ref="selectTagsDialogRef"
/>
</div>
</template>
<script setup lang="ts">
import { useUserStore } from '@/stores'
import { storeToRefs } from 'pinia'
import SelectTagsDialog from './selectTagsDialog.vue'
import { useResetData } from '@/hooks'
import { ReleaseStatusTypeEnum, SendTypeEnum } from '@/constants'
import { useTagsStore } from '@/stores'
import { uploadFile } from '@/api'
import { Close } from '@element-plus/icons-vue'
import { addOrUpdatePractice } from '@/api'
import type { AddOrUpdatePracticeDto } from '@/api/practice/types'
const tagsStore = useTagsStore()
const { tagList } = storeToRefs(tagsStore)
const userStore = useUserStore()
const { userInfo } = storeToRefs(userStore)
const selectTagsDialogRef =
useTemplateRef<InstanceType<typeof SelectTagsDialog>>('selectTagsDialogRef')
const fileInputRef = useTemplateRef<HTMLInputElement>('fileInputRef')
const [form, resetForm] = useResetData({
title: '',
content: '',
imgUrl: [],
releaseStatus: ReleaseStatusTypeEnum.PUBLISH,
mainTagId: '',
tagList: [],
sendType: SendTypeEnum.IMMEDIATE,
sendTime: '',
})
const mainTagText = computed(() => {
return tagList.value.find((tag) => tag.id === Number(form.value.mainTagId))?.title
})
const subTagTextList = computed(() => {
return form.value.tagList.map((tag) => tagList.value.find((t) => t.id === tag)?.title)
})
const handleAddTag = () => {
selectTagsDialogRef.value?.open()
}
const handleFileChange = async (e: Event) => {
const file = (e.target as HTMLInputElement).files?.[0]
if (file) {
const { data } = await uploadFile(file)
form.value.imgUrl.push(data.data[0].filePath)
}
}
const handleDeleteImg = (img: string) => {
form.value.imgUrl = form.value.imgUrl.filter((item) => item !== img)
}
const validateForm = () => {
if (!form.value.title) {
ElMessage.error('请输入实践标题')
return false
}
if (!form.value.content) {
ElMessage.error('请输入实践内容')
return false
}
if (!form.value.mainTagId) {
ElMessage.error('请选择主标签')
return false
}
return true
}
const transformForm = (releaseStatus: ReleaseStatusTypeEnum): AddOrUpdatePracticeDto => {
return {
...form.value,
releaseStatus,
faceUrl: form.value.imgUrl[0] || '',
imgUrl: form.value.imgUrl.join(','),
tagList: [form.value.mainTagId, ...form.value.tagList].map((item, index) => ({
sort: index,
tagId: Number(item),
})),
}
}
const handlePublish = async (releaseStatus: ReleaseStatusTypeEnum) => {
if (!validateForm()) return
await addOrUpdatePractice(transformForm(releaseStatus))
ElMessage.success(releaseStatus === ReleaseStatusTypeEnum.PUBLISH ? '发布成功' : '存草稿成功')
resetForm()
}
</script>
<style scoped></style>
<template>
<el-dialog v-model="dialogVisible" title="选择标签" width="500px" :close-on-click-modal="false">
<div class="space-y-6 px-2">
<div class="flex items-start gap-4">
<div class="text-sm text-gray-700 w-16 flex-shrink-0">主标签</div>
<div class="flex-1">
<SelectTags v-model="mainTagId" />
</div>
</div>
<div class="flex items-start gap-4">
<div class="text-sm text-gray-700 w-16 flex-shrink-0">副标签</div>
<div class="flex-1">
<SelectTags
v-model="subTagIdList"
:max-selected-tags="3"
:filter-tags-fn="filterTagsFn"
/>
</div>
</div>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import SelectTags from '@/components/common/SelectTags/index.vue'
import type { TagItemDto } from '@/api'
const dialogVisible = ref(false)
const mainTagId = defineModel<string>('mainTagId', { required: true })
const subTagIdList = defineModel<number[]>('tagList', { required: true })
const open = () => {
dialogVisible.value = true
}
const filterTagsFn = (allTags: TagItemDto[]) => {
return allTags.filter((tag) => tag.id !== Number(mainTagId.value))
}
defineExpose({
open,
})
</script>
<style scoped></style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment