Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
corporateCulture-qd
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
王立鹏
corporateCulture-qd
Commits
741b36b7
Commit
741b36b7
authored
Dec 17, 2025
by
lijiabin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
【需求 17679】 feat: 完成帖子(除视频外)二次编辑功能
parent
a25a5702
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
404 additions
and
185 deletions
+404
-185
index.vue
src/components/common/ArticleContent/index.vue
+27
-2
index.vue
src/components/common/WangEditor/index.vue
+7
-10
index.vue
src/layoutCulture/index.vue
+10
-7
index.vue
src/views/articleDetail/index.vue
+2
-2
index.vue
src/views/auditArticle/index.vue
+2
-2
index.vue
src/views/publishLongArticle/index.vue
+304
-160
index.vue
src/views/questionDetail/index.vue
+35
-2
selfPublish.vue
src/views/userPage/components/selfPublish.vue
+17
-0
No files found.
src/components/common/ArticleContent/index.vue
View file @
741b36b7
...
...
@@ -24,6 +24,7 @@
8
</div>
-->
</div>
<div
class=
"flex-1"
>
<div
class=
"flex items-center gap-2"
>
<h3
class=
"font-semibold text-gray-800"
>
{{
articleDetail
?.
createUserName
}}
</h3>
...
...
@@ -38,7 +39,16 @@
·
{{
articleDetail
?.
viewCount
||
0
}}
阅读
</p>
</div>
<!-- 再次编辑按钮 -->
<el-link
v-if=
"isAuthor"
type=
"primary"
:underline=
"false"
@
click=
"router.push(`/publishLongArticle/$
{articleDetail.type}?id=${articleDetail.id}`)"
class="text-sm"
>
编辑
</el-link>
<!-- 优化后的右侧内容 -->
<div
class=
"flex items-center gap-3"
>
<span
...
...
@@ -112,15 +122,30 @@ import type { ArticleItemDto } from '@/api'
import
{
articleTypeListOptions
,
ArticleTypeEnum
}
from
'@/constants'
import
ActionMore
from
'@/components/common/ActionMore/index.vue'
import
{
jumpToUserHomePage
}
from
'@/utils'
import
{
useUserStore
}
from
'@/stores/user'
import
{
storeToRefs
}
from
'pinia'
const
userStore
=
useUserStore
()
const
{
userInfo
}
=
storeToRefs
(
userStore
)
const
router
=
useRouter
()
const
{
articleDetail
}
=
defineProps
<
{
articleDetail
:
ArticleItemDto
isAudit
:
boolean
// 是否是审核页面
}
>
()
const
articleType
=
computed
(()
=>
{
return
articleTypeListOptions
.
find
((
item
)
=>
item
.
value
===
articleDetail
.
type
)?.
label
})
// 是否是作者
const
isAuthor
=
computed
(()
=>
{
return
articleDetail
.
createUserId
===
userInfo
.
value
.
userId
})
// 如果类型是帖子 专栏 和 专访 就是html
const
isHtml
=
computed
(()
=>
{
return
articleDetail
.
content
?.
includes
(
'<'
)
||
articleDetail
.
content
?.
includes
(
'</'
)
return
(
articleDetail
.
type
===
ArticleTypeEnum
.
POST
||
articleDetail
.
type
===
ArticleTypeEnum
.
COLUMN
||
articleDetail
.
type
===
ArticleTypeEnum
.
INTERVIEW
)
})
</
script
>
src/components/common/WangEditor/index.vue
View file @
741b36b7
...
...
@@ -17,8 +17,6 @@
</
template
>
<
script
setup
lang=
"ts"
>
import
'@wangeditor/editor/dist/css/style.css'
// 引入 css
import
{
onBeforeUnmount
,
ref
,
shallowRef
,
onMounted
}
from
'vue'
import
{
Editor
,
Toolbar
}
from
'@wangeditor/editor-for-vue'
import
{
uploadFile
}
from
'@/api'
const
mode
=
'default'
...
...
@@ -29,15 +27,14 @@ const editorRef = shallowRef()
// 内容 HTML
const
valueHtml
=
defineModel
<
string
>
()
// 模拟 ajax 异步获取内容
onMounted
(()
=>
{
setTimeout
(()
=>
{
valueHtml
.
value
=
'<p>模拟 Ajax 异步设置内容</p>'
},
1500
)
})
// 去掉上传视频的功能
const
toolbarConfig
=
{}
const
editorConfig
=
{
placeholder
:
'请输入内容...'
,
MENU_CONF
:
{}
}
toolbarConfig
.
excludeKeys
=
[
'group-video'
,
'group-more-video'
,
'group-more-video'
]
// 去掉上传视频
const
editorConfig
=
{
placeholder
:
'请输入内容...'
,
MENU_CONF
:
{},
}
// 修改 uploadImage 菜单配置
...
...
src/layoutCulture/index.vue
View file @
741b36b7
...
...
@@ -88,7 +88,6 @@
>
专访
</el-dropdown-item
>
</el-dropdown-menu>
<el-dropdown-item
:command=
"ArticleTypeEnum.LONG_ARTICLE"
>
长文章
</el-dropdown-item>
</
template
>
</el-dropdown>
</div>
...
...
@@ -143,23 +142,27 @@ const getSecondLevelKey = (route: RouteLocationNormalizedLoadedGeneric) => {
const
key
=
Object
.
keys
(
route
.
params
).
length
?
pathSegments
.
slice
(
0
,
2
).
join
(
'/'
)
:
pathSegments
.
slice
(
0
,
1
).
join
(
'/'
)
console
.
log
(
key
,
'*********************'
)
//
console.log(key, '*********************')
return
key
}
const
notShowPath
=
[
'/videoDetail'
,
'/articleDetail'
,
'/questionDetail'
]
const
showOnlineTime
=
computed
(()
=>
{
return
!
route
.
path
.
includes
(
'/videoDetail'
)
&&
!
route
.
path
.
includes
(
'/articleDetail'
)
return
notShowPath
.
every
((
path
)
=>
!
route
.
path
.
includes
(
path
)
)
})
const
handlePost
=
async
(
type
:
ArticleTypeEnum
)
=>
{
if
(
type
===
ArticleTypeEnum
.
VIDEO
)
{
router
.
push
(
'/publishVideo'
)
}
else
if
(
type
===
ArticleTypeEnum
.
QUESTION
)
{
router
.
push
(
`/homePage/askTab#tabsRef?t=
${
Date
.
now
()}
`
)
}
else
if
(
type
===
ArticleTypeEnum
.
LONG_ARTICLE
)
{
router
.
push
(
'/publishLongArticle'
)
// router.push(`/homePage/askTab#tabsRef?t=${Date.now()}`)
router
.
push
(
`/publishLongArticle/
${
type
}
`
)
}
else
if
(
type
===
ArticleTypeEnum
.
PRACTICE
)
{
router
.
push
(
`/publishLongArticle/
${
type
}
`
)
// PublishDialogRef.value?.open(type)
}
else
{
PublishDialogRef
.
value
?.
open
(
type
)
router
.
push
(
`/publishLongArticle/
${
type
}
`
)
}
}
const
isDropdownHover
=
ref
(
false
)
...
...
src/views/articleDetail/index.vue
View file @
741b36b7
...
...
@@ -7,7 +7,7 @@
></ActionButtons>
<div
class=
"lg:col-span-3"
>
<!-- 帖子主体 -->
<ArticleContent
:articleDetail=
"articleDetail"
/>
<ArticleContent
:articleDetail=
"articleDetail"
:isAudit=
"false"
/>
<!-- 评论区 -->
<Comment
...
...
@@ -29,7 +29,7 @@ import { ArticleTypeEnum } from '@/constants'
const
commentRef
=
useTemplateRef
<
typeof
Comment
|
null
>
(
'commentRef'
)
const
route
=
useRoute
()
const
id
=
route
.
params
.
id
as
string
const
id
=
Number
(
route
.
params
.
id
)
const
isReal
=
computed
(()
=>
{
return
+
(
...
...
src/views/auditArticle/index.vue
View file @
741b36b7
<
template
>
<div
v-loading=
"loading"
class=
"px-20"
>
<div
v-loading=
"loading"
>
<div
class=
"lg:col-span-3 mb-20"
>
<ArticleContent
:articleDetail=
"articleDetail"
/>
<ArticleContent
:articleDetail=
"articleDetail"
:isAudit=
"true"
/>
</div>
<!-- 底部fixed -->
<div
class=
"fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 shadow-lg"
>
...
...
src/views/publishLongArticle/index.vue
View file @
741b36b7
<
template
>
<div
class=
"min-h-screen bg-[#fff] font-sans"
>
<div
class=
"max-w-7xl mx-auto"
>
<!-- 顶部面包屑或标题(可选) -->
<el-form
ref=
"formRef"
:model=
"form"
:rules=
"rules"
label-position=
"top"
class=
"grid grid-cols-12 gap-6 items-start"
>
<!-- 左侧:沉浸式创作区 (占 9 列) -->
<div
class=
"col-span-12 lg:col-span-9 space-y-6"
>
<div
class=
"bg-white rounded-xl shadow-sm border border-gray-100 p-8 min-h-[80vh]"
>
<!-- 标题输入:模拟大标题风格,去掉边框 -->
<el-form-item
prop=
"title"
class=
"!border-b !border-gray-100"
>
<div
class=
"min-h-screen bg-gradient-to-br pb-10"
>
<!-- 主编辑区域 -->
<div
class=
"bg-white rounded-lg shadow-lg border border-gray-100/50 overflow-hidden"
>
<div
class=
"px-10 py-5"
>
<!-- 标题输入 -->
<el-form-item
prop=
"title"
class=
"mb-8"
>
<el-input
v-model=
"form.title"
placeholder=
"请输入文章标题...
"
class=
"title-input
"
:placeholder=
"`在这里输入你的$
{text}标题...`
"
class="title-input text-3xl font-bold
"
show-word-limit
type="textarea"
:autosize="{ minRows: 1, maxRows: 3 }"
resize="none"
/>
</el-form-item>
<!-- 富文本编辑器 -->
<div
class=
"editor-container"
>
<el-form-item
prop=
"content"
class=
"!border-b !border-gray-100"
>
<WangEditor
v-model=
"form.content"
style=
"height: 800px"
/>
<div>
<el-form-item
prop=
"content"
>
<!-- 问吧和实践不是富文本 -->
<template
v-if=
"
form.type === ArticleTypeEnum.PRACTICE || form.type === ArticleTypeEnum.QUESTION
"
>
<el-input
:placeholder=
"`请输入$
{text}内容`"
v-model="form.content"
type="textarea"
:rows="30"
:maxlength="2000"
show-word-limit
/>
</
template
>
<
template
v-else
>
<!-- 回显回来可能会有bug 所以需要key 重新渲染一下-->
<WangEditor
:key
v-model=
"form.content"
class=
"min-h[90vh]"
/>
</
template
>
</el-form-item>
</div>
</div>
</div>
<!-- 右侧:配置侧边栏 (占 3 列,吸顶) -->
<div
class=
"col-span-12 lg:col-span-3 space-y-4"
>
<!-- 卡片1:基础设置 -->
<div
class=
"bg-white rounded-xl shadow-sm border border-gray-100 p-5"
>
<div
class=
"font-bold text-gray-800 mb-4 flex items-center gap-2"
>
<div
class=
"w-1 h-4 bg-blue-500 rounded-full"
></div>
基础设置
<!-- 底部固定按钮栏 -->
<div
class=
"fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 shadow-lg"
>
<div
class=
"max-w-[1200px] mx-auto px-4 py-4"
>
<div
class=
"flex justify-center gap-4"
>
<el-button
@
click=
"handleClosed"
class=
"rounded-lg min-w[120px]"
>
取消
</el-button>
<el-button
type=
"primary"
:disabled=
"!canPublish"
@
click=
"openDrawer"
class=
"px-6 py-2 bg-blue-500 hover:bg-blue-600 rounded-lg text-white text-sm font-medium shadow-sm hover:shadow-md transition-all duration-200 min-w[120px]"
>
{{ isEdit ? '编辑' : '发布' }}
</el-button>
</div>
</div>
</div>
<!-- 右侧抽屉:发布配置 -->
<el-drawer
v-model=
"drawerVisible"
title=
"文章信息"
direction=
"rtl"
size=
"500px"
:before-close=
"handleDrawerClose"
>
<el-form
ref=
"formRef"
:model=
"form"
:rules=
"rules"
label-position=
"top"
class=
"px-4"
>
<!-- 基础设置 -->
<div
class=
"mb-6"
>
<!-- 文章类型 -->
<el-form-item
label=
"文章类型"
prop=
"type"
>
<el-radio-group
v-model=
"form.type"
class=
"w-full grid grid-cols-3 gap-2"
fill=
"#3b82f6"
<el-button
type=
"primary"
class=
"px-6 py-2 bg-blue-500 hover:bg-blue-600 rounded-lg text-white text-sm font-medium shadow-sm hover:shadow-md transition-all duration-200 min-w[120px]"
>
<el-radio-button
:value=
"ArticleTypeEnum.POST"
>
{{
articleTypeListOptions
.
find
((
item
)
=>
item
.
value
===
type
)?.
label
}}
</el-radio-button>
</el-radio-group>
{{ text }}
</el-button>
</el-form-item>
<!-- 封面图 -->
<!-- 内容图片 实践和问吧需要上传内容图片 然后默认第一张是封面图 -->
<
template
v-if=
"form.type === ArticleTypeEnum.PRACTICE || form.type === ArticleTypeEnum.QUESTION"
>
<el-form-item
label=
"图片"
>
<UploadFile
v-model=
"form.imgUrl"
:limit=
"1"
class=
"w-full"
/>
</el-form-item>
</
template
>
<!-- 帖子 专栏专访 是富文本 需要上传封面图 封面图 -->
<
template
v-else
>
<el-form-item
label=
"封面图"
prop=
"faceUrl"
>
<div
class=
"w-full"
>
<UploadFile
v-model=
"form.faceUrl"
:limit=
"1"
class=
"w-full"
/>
<div
class=
"text-xs text-gray-400 mt-2"
>
建议尺寸 16:9,支持 jpg/png
</div>
</div>
</el-form-item>
</
template
>
<!-- 标签和主标签 除了帖子 都需要上传 -->
<
template
v-if=
"form.type !== ArticleTypeEnum.POST"
>
<el-form-item
label=
"主标签"
prop=
"mainTagId"
>
<SelectTags
v-model=
"form.mainTagId"
class=
"w-full"
/>
</el-form-item>
<el-form-item
label=
"副标签 (最多3个)"
>
<SelectTags
v-model=
"form.tagList"
:filter-tags-fn=
"filterTagsFn"
:max-selected-tags=
"3"
class=
"w-full"
/>
</el-form-item>
</
template
>
</div>
<!-- 卡片2:高级配置 (专栏/专访特有
) -->
<!-- 专栏配置 (条件显示
) -->
<
template
v-if=
"form.type === ArticleTypeEnum.COLUMN || form.type === ArticleTypeEnum.INTERVIEW"
>
<div
class=
"bg-white rounded-xl shadow-sm border border-gray-100 p-5"
>
<div
class=
"font-bold text-gray-800 mb-4 flex items-center gap-2"
>
<div
class=
"w-1 h-4 bg-purple-500 rounded-full"
></div>
专栏配置
</div>
<div
class=
"mb-6"
>
<el-form-item
label=
"所属栏目"
prop=
"relateColumnId"
>
<el-select
v-model=
"form.relateColumnId"
placeholder=
"请选择专栏栏目
"
:placeholder=
"`请选择$
{text}栏目`
"
class="w-full"
>
<el-option
v-for=
"item in c
olumnList"
v-for=
"item in relateC
olumnList"
:key=
"item.id"
:value=
"item.id"
:label=
"item.title"
...
...
@@ -90,112 +134,78 @@
</el-select>
</el-form-item>
<el-form-item
label=
"主标签"
prop=
"mainTagId"
>
<!--
<el-form-item
label=
"主标签"
prop=
"mainTagId"
>
<SelectTags
v-model=
"form.mainTagId"
class=
"w-full"
/>
</el-form-item>
<el-form-item
label=
"副标签
"
>
<el-form-item
label=
"副标签 (最多3个)
"
>
<SelectTags
v-model=
"form.tagList"
:filter-tags-fn=
"filterTagsFn"
:max-selected-tags=
"3"
class=
"w-full"
/>
</el-form-item
>
</el-form-item>
--
>
<el-form-item
label=
"推荐设置"
>
<div
class=
"flex items-center justify-between w-full"
>
<span
class=
"text-gray-600 text-sm"
>
是否推荐
</span>
<el-switch
v-model=
"form.isRecommend"
:active-value=
"BooleanFlag.YES"
:inactive-value=
"BooleanFlag.NO"
/>
</div>
<el-form-item
label=
"是否推荐"
prop=
"isRecommend"
>
<!-- 改为radio单选框 -->
<el-radio-group
v-model=
"form.isRecommend"
>
<el-radio
:value=
"BooleanFlag.YES"
>
是
</el-radio>
<el-radio
:value=
"BooleanFlag.NO"
>
否
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if=
"form.type === ArticleTypeEnum.COLUMN"
>
<div
class=
"flex items-center justify-between w-full"
>
<span
class=
"text-gray-600 text-sm"
>
同步同事吧
</span>
<el-switch
v-model=
"form.isRelateColleague"
:active-value=
"BooleanFlag.YES"
:inactive-value=
"BooleanFlag.NO"
/
>
</div
>
<el-form-item
label=
"同步同事吧"
prop=
"isRelateColleague"
v-if=
"form.type === ArticleTypeEnum.COLUMN"
>
<el-radio-group
v-model=
"form.isRelateColleague"
>
<el-radio
:value=
"BooleanFlag.YES"
>
是
</el-radio>
<el-radio
:value=
"BooleanFlag.NO"
>
否
</el-radio
>
</el-radio-group
>
</el-form-item>
</div>
</
template
>
<!-- 卡片3:发布设置 -->
<div
class=
"bg-white rounded-xl shadow-sm border border-gray-100 p-5"
>
<div
class=
"font-bold text-gray-800 mb-4 flex items-center gap-2"
>
<div
class=
"w-1 h-4 bg-orange-500 rounded-full"
></div>
发布设置
</div>
<el-form-item
prop=
"sendType"
class=
"!mb-2"
>
<el-radio-group
v-model=
"form.sendType"
class=
"flex flex-col gap-3 w-full"
>
<div
class=
"flex items-center p-3 rounded-lg border cursor-pointer transition-all"
:class=
"
form.sendType === SendTypeEnum.IMMEDIATE
? 'border-blue-500 bg-blue-50'
: 'border-gray-200 hover:border-blue-300'
"
@
click=
"form.sendType = SendTypeEnum.IMMEDIATE"
>
<el-radio
:value=
"SendTypeEnum.IMMEDIATE"
class=
"!mr-2"
>
立即发布
</el-radio>
<span
class=
"text-xs text-gray-400 ml-auto"
>
当前时间
</span>
</div>
<div
class=
"flex flex-col p-3 rounded-lg border cursor-pointer transition-all"
:class=
"
form.sendType === SendTypeEnum.SCHEDULED
? 'border-blue-500 bg-blue-50'
: 'border-gray-200 hover:border-blue-300'
"
@
click=
"form.sendType = SendTypeEnum.SCHEDULED"
>
<div
class=
"flex items-center mb-2"
>
<el-radio
:value=
"SendTypeEnum.SCHEDULED"
class=
"!mr-2"
>
定时发布
</el-radio>
</div>
<!-- 发布设置 -->
<div
class=
"mb-6"
>
<el-form-item
label=
"发布时间"
prop=
"sendType"
>
<el-radio-group
v-model=
"form.sendType"
>
<el-radio
:value=
"SendTypeEnum.IMMEDIATE"
>
立即发布
</el-radio>
<el-radio
:value=
"SendTypeEnum.SCHEDULED"
>
定时发布
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if=
"form.sendType === SendTypeEnum.SCHEDULED"
prop=
"sendTime"
>
<el-date-picker
v-if=
"form.sendType === SendTypeEnum.SCHEDULED"
v-model=
"form.sendTime"
type=
"datetime"
placeholder=
"选择时间"
class=
"!w-full"
:disabled-date=
"
(time: Date) => time.getTime() < Date.now() - 1000 * 60 * 60 * 24
"
placeholder=
"选择发布时间"
:disabled-date=
"(time: Date) => time.getTime() < Date.now() - 1000 * 60 * 60 * 24"
value-format=
"X"
size=
"small"
/>
</div>
</el-radio-group>
</el-form-item>
</div>
<div
class=
"mb-6 flex items-center justify-end w-full"
>
<div
class=
"flex gap-3"
>
<el-button
class=
"rounded-lg"
@
click=
"handleClosed"
>
取消
</el-button>
<el-button
class=
"rounded-lg"
@
click=
"handleSubmit(ReleaseStatusTypeEnum.DRAFT)"
>
</el-form>
<!-- 抽屉底部按钮 -->
<
template
#
footer
>
<div
class=
"flex gap-3 justify-end"
>
<el-button
@
click=
"handleDrawerClose"
class=
"rounded-lg"
>
取消
</el-button>
<el-button
@
click=
"handleSubmit(ReleaseStatusTypeEnum.DRAFT)"
class=
"rounded-lg"
>
存草稿
</el-button>
<el-button
:loading=
"loading"
type=
"primary"
@
click=
"handleSubmit(ReleaseStatusTypeEnum.PUBLISH)"
class=
"px-6 py-2 bg-blue-500 hover:bg-blue-600 rounded-lg text-white text-sm font-medium shadow-sm hover:shadow-md transition-all duration-200
"
class=
"px-6 py-2 bg-blue-500 hover:bg-blue-600 rounded-lg text-white text-sm font-medium shadow-sm hover:shadow-md transition-all duration-200 min-w[120px]
"
>
发布
确认
{{
isEdit
?
'编辑'
:
'发布'
}}
</el-button>
</div>
</div>
</div>
</el-form>
</div>
</
template
>
</el-drawer>
</div>
</template>
...
...
@@ -212,22 +222,41 @@ import {
import
UploadFile
from
'@/components/common/UploadFile/index.vue'
import
{
useResetData
}
from
'@/hooks'
import
{
useColumnStore
}
from
'@/stores/column'
import
{
useInterviewStore
}
from
'@/stores/interview'
import
{
storeToRefs
}
from
'pinia'
import
{
addOrUpdateArticle
}
from
'@/api'
// ... (逻辑部分保持不变,直接复用您的即可)
import
{
addOrUpdateArticle
,
addOrUpdatePractice
,
getArticleDetail
}
from
'@/api'
import
{
useRouter
,
useRoute
}
from
'vue-router'
const
columnStore
=
useColumnStore
()
const
{
columnList
}
=
storeToRefs
(
columnStore
)
const
interviewStore
=
useInterviewStore
()
const
{
interviewList
}
=
storeToRefs
(
interviewStore
)
const
loading
=
ref
(
false
)
const
router
=
useRouter
()
const
route
=
useRoute
()
const
type
=
route
.
params
.
type
as
ArticleTypeEnum
const
text
=
articleTypeListOptions
.
find
((
item
)
=>
item
.
value
===
type
)?.
label
const
canPublish
=
computed
(()
=>
{
return
form
.
value
.
title
&&
form
.
value
.
content
})
const
relateColumnList
=
computed
(()
=>
type
===
ArticleTypeEnum
.
COLUMN
?
columnList
.
value
:
interviewList
.
value
,
)
// 抽屉控制
const
drawerVisible
=
ref
(
false
)
const
formRef
=
useTemplateRef
<
FormInstance
>
(
'formRef'
)
const
[
form
,
resetForm
]
=
useResetData
({
type
:
type
,
type
,
title
:
''
,
content
:
''
,
faceUrl
:
''
,
relateColumnId
:
null
,
// 建议初始值设为null或undefined,配合placeholder
imgUrl
:
''
,
relateColumnId
:
null
,
mainTagId
:
''
,
tagList
:
[],
isRelateColleague
:
BooleanFlag
.
NO
,
...
...
@@ -242,64 +271,179 @@ const rules = {
content
:
[{
required
:
true
,
message
:
'请输入文章内容'
,
trigger
:
'blur'
}],
type
:
[{
required
:
true
,
message
:
'请选择文章类型'
,
trigger
:
'blur'
}],
faceUrl
:
[{
required
:
true
,
message
:
'请上传封面图'
,
trigger
:
'blur'
}],
imgUrl
:
[{
required
:
true
,
message
:
'请上传内容图片'
,
trigger
:
'blur'
}],
sendType
:
[{
required
:
true
,
message
:
'请选择发布类型'
,
trigger
:
'blur'
}],
sendTime
:
[{
required
:
true
,
message
:
'请选择发布时间'
,
trigger
:
'
blu
r'
}],
sendTime
:
[{
required
:
true
,
message
:
'请选择发布时间'
,
trigger
:
'
trigge
r'
}],
releaseStatus
:
[{
required
:
true
,
message
:
'请选择发布状态'
,
trigger
:
'blur'
}],
mainTagId
:
[{
required
:
true
,
message
:
'请选择主标签'
,
trigger
:
'blur'
}],
isRecommend
:
[{
required
:
true
,
message
:
'是否推荐'
,
trigger
:
'trigger'
}],
isRelateColleague
:
[{
required
:
true
,
message
:
'是否同步同事吧'
,
trigger
:
'trigger'
}],
relateColumnId
:
[{
required
:
true
,
message
:
'请选择对应的栏目'
,
trigger
:
'trigger'
}],
}
const
filterTagsFn
=
(
allTags
:
any
[])
=>
{
return
allTags
.
filter
((
tag
)
=>
tag
.
id
!==
Number
(
form
.
value
.
mainTagId
))
}
const
handleSubmit
=
async
()
=>
{
const
transFormData
=
(
releaseStatus
:
ReleaseStatusTypeEnum
)
=>
{
const
{
tagList
,
...
data
}
=
form
.
value
data
.
tagList
=
[
data
.
mainTagId
,
...
tagList
].
map
((
item
,
index
)
=>
({
tagId
:
Number
(
item
),
sort
:
index
,
}))
data
.
releaseStatus
=
releaseStatus
if
(
data
.
type
===
ArticleTypeEnum
.
PRACTICE
||
data
.
type
===
ArticleTypeEnum
.
QUESTION
)
{
// 手动设置一下封面图
if
(
data
.
imgUrl
)
{
data
.
faceUrl
=
data
.
imgUrl
.
split
(
','
)[
0
]
}
}
return
data
}
// 打开抽屉
const
openDrawer
=
()
=>
{
drawerVisible
.
value
=
true
}
// 关闭抽屉
const
handleDrawerClose
=
()
=>
{
drawerVisible
.
value
=
false
}
// 取消按钮
const
handleClosed
=
()
=>
{
resetForm
()
router
.
back
()
}
// 提交表单
const
handleSubmit
=
async
(
releaseStatus
:
ReleaseStatusTypeEnum
)
=>
{
try
{
await
formRef
.
value
.
validate
()
const
res
=
await
addOrUpdateArticle
(
form
.
value
)
await
formRef
.
value
?.
validate
()
loading
.
value
=
true
const
res
=
form
.
value
.
type
===
ArticleTypeEnum
.
PRACTICE
?
await
addOrUpdatePractice
(
transFormData
(
releaseStatus
))
:
await
addOrUpdateArticle
(
transFormData
(
releaseStatus
))
console
.
log
(
res
)
drawerVisible
.
value
=
false
resetForm
()
router
.
back
()
// 发布成功后的逻辑...
}
catch
(
error
)
{
console
.
log
(
error
)
}
finally
{
loading
.
value
=
false
}
}
const
key
=
ref
(
0
)
const
isEdit
=
computed
(()
=>
!!
route
.
query
.
id
)
onActivated
(
async
()
=>
{
key
.
value
++
resetForm
()
await
nextTick
()
if
(
isEdit
.
value
)
{
console
.
log
(
route
.
query
.
id
,
'编辑'
)
// 要编辑回显
const
{
data
}
=
await
getArticleDetail
(
route
.
query
.
id
)
// 首先回显基础的信息
// 标题 内容 主标签 副标签 所属栏目 是否推荐 是否同步同事吧 发布时间 发布状态
const
{
id
,
title
,
content
,
relateColumnId
,
isRecommend
,
isRelateColleague
,
sendType
,
sendTime
,
tagIdList
,
}
=
data
form
.
value
=
{
...
form
.
value
,
title
,
content
,
relateColumnId
,
isRecommend
,
isRelateColleague
,
sendType
,
sendTime
,
}
// 回显主副标签
form
.
value
.
mainTagId
=
String
(
tagIdList
[
0
])
||
''
form
.
value
.
tagList
=
tagIdList
.
slice
(
1
)
||
[]
const
{
imgUrl
,
faceUrl
}
=
data
if
(
type
===
ArticleTypeEnum
.
QUESTION
||
type
===
ArticleTypeEnum
.
PRACTICE
)
{
form
.
value
.
imgUrl
=
imgUrl
}
else
{
form
.
value
.
faceUrl
=
faceUrl
}
}
})
</
script
>
<
style
scoped
lang=
"scss"
>
/*
覆盖 Element Plus 默认样式,使其更符合大标题风格
*/
<
style
scoped
>
/*
标题输入框样式
*/
:deep
(
.title-input
.el-textarea__inner
)
{
font-size
:
24px
;
font-weight
:
bold
;
color
:
#333
;
padding
:
0
;
border
:
none
;
box-shadow
:
none
;
padding
:
0
;
font-size
:
2rem
;
font-weight
:
700
;
line-height
:
1.3
;
color
:
#1f2937
;
background
:
transparent
;
&::placeholder
{
color
:
#a8abb2
;
}
box-shadow
:
none
!important
;
}
/* 隐藏 Radio Button 的圆点,改用卡片选择样式时需要 */
:deep
(
.el-radio-button__inner
)
{
border-radius
:
8px
!important
;
border
:
1px
solid
#dcdfe6
;
border-left
:
1px
solid
#dcdfe6
!important
;
:deep
(
.title-input
.el-textarea__inner
:focus
)
{
outline
:
none
;
box-shadow
:
none
!important
;
padding
:
8px
16px
;
width
:
100%
;
}
:deep
(
.el-radio-button
:first-child
.el-radio-button__inner
)
{
border-left
:
1px
solid
#dcdfe6
;
}
:deep
(
.el-radio-button__original-radio
:checked
+
.el-radio-button__inner
)
{
background-color
:
#ecf5ff
;
border-color
:
#409eff
;
color
:
#409eff
;
box-shadow
:
none
;
:deep
(
.title-input
.el-textarea__inner
::placeholder
)
{
color
:
#d1d5db
;
font-weight
:
600
;
}
/*
让侧边栏标签文字稍微小一点
*/
/*
表单项标签样式
*/
:deep
(
.el-form-item__label
)
{
font-weight
:
500
;
color
:
#4b5563
;
color
:
#374151
;
font-size
:
0.875rem
;
}
/* 抽屉内容滚动 */
:deep
(
.el-drawer__body
)
{
padding
:
20px
0
;
}
/* 滚动条美化 */
::-webkit-scrollbar
{
width
:
8px
;
height
:
8px
;
}
::-webkit-scrollbar-track
{
background
:
#f1f5f9
;
border-radius
:
4px
;
}
::-webkit-scrollbar-thumb
{
background
:
#cbd5e1
;
border-radius
:
4px
;
}
::-webkit-scrollbar-thumb:hover
{
background
:
#94a3b8
;
}
</
style
>
src/views/questionDetail/index.vue
View file @
741b36b7
...
...
@@ -6,7 +6,7 @@
class=
"bg-white rounded-lg p-6 shadow-[0_1px_3px_rgba(0,0,0,0.02)] border border-slate-100"
>
<!-- 顶部标签行 -->
<div
class=
"flex flex-wrap gap-2 mb-4"
>
<div
class=
"flex flex-wrap gap-2 mb-4
justify-between
"
>
<span
v-for=
"tag in questionDetail.tagNameList"
:key=
"tag"
...
...
@@ -14,6 +14,18 @@
>
#
{{
tag
}}
</span>
<!-- 后面加一个编辑 -->
<el-link
v-if=
"isAuthor"
type=
"primary"
:underline=
"false"
@
click=
"
router.push(`/publishLongArticle/$
{questionDetail.type}?id=${questionDetail.id}`)
"
class="text-sm"
>
编辑
</el-link>
</div>
<!-- 标题:主要信息,黑重粗 -->
...
...
@@ -78,6 +90,18 @@
</span>
</div>
</div>
<!-- 展示图片相关 -->
<div
v-if=
"questionDetail.imgUrl"
class=
"grid grid-cols-1 md:grid-cols-2 gap-4 mt-6"
>
<el-image
v-for=
"item in questionDetail.imgUrl.split(',')"
:key=
"item"
:src=
"item"
fit=
"cover"
class=
"rounded-lg w-full h-64 hover:scale-105 transition-transform cursor-pointer"
:preview-src-list=
"questionDetail.imgUrl.split(',')"
:preview-teleported=
"true"
/>
</div>
</div>
<!-- 2. 列表控制栏 -->
...
...
@@ -241,14 +265,23 @@ import { usePageSearch } from '@/hooks'
import
Comment
from
'@/components/common/Comment/index.vue'
import
CommentDialog
from
'@/components/common/CommentDialog/index.vue'
import
dayjs
from
'dayjs'
import
{
useUserStore
}
from
'@/stores/user'
import
{
storeToRefs
}
from
'pinia'
const
userStore
=
useUserStore
()
const
{
userInfo
}
=
storeToRefs
(
userStore
)
const
route
=
useRoute
()
const
questionId
=
Number
(
route
.
params
.
id
)
const
router
=
useRouter
(
)
const
questionId
=
Number
(
route
.
params
.
id
)
const
commentRefList
=
ref
<
InstanceType
<
typeof
Comment
>
[]
>
([])
const
questionDetail
=
ref
<
ArticleItemDto
>
({
}
as
ArticleItemDto
)
const
commentDialogRef
=
useTemplateRef
<
typeof
CommentDialog
>
(
'commentDialogRef'
)
const
isAuthor
=
computed
(()
=>
{
return
questionDetail
.
value
.
createUserId
===
userInfo
.
value
.
userId
}
)
const
questionContentRef
=
useTemplateRef
<
HTMLElement
>
(
'questionContentRef'
)
const
isExpand
=
ref
(
false
)
...
...
src/views/userPage/components/selfPublish.vue
View file @
741b36b7
...
...
@@ -53,6 +53,13 @@
@
click=
"jumpToArticleDetailPage(
{ type: item.type, id: item.id })"
>查看
</el-button
>
<el-button
v-if=
"item.type !== ArticleTypeEnum.VIDEO"
type=
"primary"
link
@
click=
"jumpToEditPage(
{ type: item.type, id: item.id })"
>编辑
</el-button
>
<el-button
type=
"danger"
link
@
click=
"handleDelete(item.id)"
>
删除
</el-button>
</div>
</div>
...
...
@@ -93,6 +100,8 @@ import type { TabPaneName } from 'element-plus'
import
{
IS_REAL_KEY
}
from
'@/constants/symbolKey'
import
{
jumpToArticleDetailPage
}
from
'@/utils'
const
router
=
useRouter
()
const
isReal
=
inject
(
IS_REAL_KEY
)
const
filterArticleType
=
computed
(()
=>
{
if
(
isReal
?.
value
===
1
)
{
...
...
@@ -131,6 +140,14 @@ const handleDelete = async (articleId: number) => {
ElMessage
.
success
(
'删除成功'
)
}
const
jumpToEditPage
=
(
item
:
{
type
:
ArticleTypeEnum
;
id
:
number
})
=>
{
if
(
item
.
type
===
ArticleTypeEnum
.
VIDEO
)
{
router
.
push
(
`/publishVideo?id=
${
item
.
id
}
`
)
}
else
{
router
.
push
(
`/publishLongArticle/
${
item
.
type
}
?id=
${
item
.
id
}
`
)
}
}
defineExpose
({
refresh
:
()
=>
{
searchParams
.
value
.
type
=
filterArticleType
.
value
[
0
]
!
.
value
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment