Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
corporate-culture-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
王立鹏
corporate-culture-qd
Commits
bd21693c
Commit
bd21693c
authored
Oct 19, 2026
by
lijiabin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
【需求 25171】feat: 后台内容管理各列表新增删除功能,搜索新增标签/创建人/创建时间
parent
2cb5537a
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
210 additions
and
49 deletions
+210
-49
index.ts
src/api/article/index.ts
+11
-0
types.ts
src/api/article/types.ts
+8
-0
index.vue
src/views/backend/contentsMenu/columnManage/index.vue
+12
-8
ContentSearchBar.vue
...iews/backend/contentsMenu/components/ContentSearchBar.vue
+91
-0
useAdminDeleteArticle.ts
.../backend/contentsMenu/components/useAdminDeleteArticle.ts
+22
-0
index.vue
src/views/backend/contentsMenu/interviewManage/index.vue
+12
-8
index.vue
src/views/backend/contentsMenu/postManage/index.vue
+17
-9
index.vue
src/views/backend/contentsMenu/practiceManage/index.vue
+12
-8
index.vue
src/views/backend/contentsMenu/questionManage/index.vue
+12
-8
index.vue
src/views/backend/contentsMenu/videoManage/index.vue
+13
-8
No files found.
src/api/article/index.ts
View file @
bd21693c
...
...
@@ -445,3 +445,14 @@ export const updateArticleShowLatest = (data: UpdateArticleShowLatestDto) => {
data
,
})
}
/**
* 后台内容管理删除文章(目前帖子管理在用)
*/
export
const
deleteArticleByAdmin
=
(
id
:
number
)
=>
{
return
service
.
request
<
boolean
>
({
url
:
'/api/cultureArticle/adminDeleteArticle'
,
method
:
'POST'
,
data
:
{
id
},
})
}
src/api/article/types.ts
View file @
bd21693c
...
...
@@ -18,6 +18,14 @@ export interface ArticleSearchParams extends PageSearchParams {
status
?:
BooleanFlag
// 视频分类id,前台分类栏点击筛选用,只对视频类型生效,不传为全部
categoryId
?:
number
// 后台内容管理的搜索条件
/** 标签 id,多选 */
tagIdList
?:
number
[]
/** 创建人,模糊查询 */
createUserName
?:
string
/** 创建时间范围,秒级时间戳,结束时间要转成当天 23:59:59;清空时设 undefined,不要设 0(0 会变成 create_time <= 0,一条都查不出来) */
createTimeStart
?:
number
createTimeEnd
?:
number
}
/**
...
...
src/views/backend/contentsMenu/columnManage/index.vue
View file @
bd21693c
<
script
setup
lang=
"ts"
>
import
{
Search
,
Upload
}
from
'@element-plus/icons-vue'
import
ContentSearchBar
from
'../components/ContentSearchBar.vue'
import
{
useAdminDeleteArticle
}
from
'../components/useAdminDeleteArticle'
import
{
Upload
}
from
'@element-plus/icons-vue'
import
{
usePageSearch
,
useResetData
}
from
'@/hooks'
import
{
addOrUpdateColumn
}
from
'@/api/backend'
import
{
getArticleList
,
updateArticleRecommendAndSort
,
recallPush
}
from
'@/api'
...
...
@@ -21,6 +23,8 @@ const { loading, list, total, reset, goToPage, changePageSize, refresh, searchPa
},
})
const
handleDelete
=
useAdminDeleteArticle
(
'专栏'
,
()
=>
refresh
())
// 对话框
const
dialogVisible
=
ref
(
false
)
const
dialogTitle
=
computed
(()
=>
(
form
.
value
.
id
?
'编辑'
:
'新增'
))
...
...
@@ -139,13 +143,7 @@ const handleRecallPush = async (row: ArticleItemDto) => {
<div
class=
"official-tag-page"
>
<!-- 搜索栏 -->
<div
class=
"search-section"
>
<el-input
v-model=
"searchParams.title"
placeholder=
"请输入标题"
class=
"w-200px mr-12px"
></el-input>
<el-button
type=
"primary"
:icon=
"Search"
@
click=
"refresh"
>
搜索
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
<ContentSearchBar
v-model=
"searchParams"
@
search=
"refresh"
@
reset=
"reset"
/>
</div>
<!-- 表格区域 -->
...
...
@@ -241,6 +239,12 @@ const handleRecallPush = async (row: ArticleItemDto) => {
<span
v-else
>
{{
pushStatusText
(
0
)
}}
</span>
</
template
>
</el-table-column>
<el-table-column
label=
"操作"
width=
"100"
fixed=
"right"
>
<
template
#
default=
"{ row }"
>
<el-button
type=
"danger"
link
@
click=
"handleDelete(row)"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
</div>
...
...
src/views/backend/contentsMenu/components/ContentSearchBar.vue
0 → 100644
View file @
bd21693c
<
template
>
<div
class=
"content-search-bar"
>
<el-input
v-model=
"params.title"
placeholder=
"请输入标题"
clearable
class=
"filter-item"
style=
"width: 200px"
@
keyup
.
enter=
"emit('search')"
/>
<el-select
v-model=
"params.tagIdList"
multiple
collapse-tags
collapse-tags-tooltip
clearable
placeholder=
"请选择标签"
class=
"filter-item"
style=
"width: 240px"
>
<el-option
v-for=
"tag in tagList"
:key=
"tag.id"
:label=
"tag.title"
:value=
"tag.id"
/>
</el-select>
<el-input
v-model=
"params.createUserName"
placeholder=
"请输入创建人"
clearable
class=
"filter-item"
style=
"width: 200px"
@
keyup
.
enter=
"emit('search')"
/>
<el-date-picker
v-model=
"createTimeRange"
type=
"daterange"
value-format=
"X"
range-separator=
"至"
start-placeholder=
"创建开始日期"
end-placeholder=
"创建结束日期"
class=
"filter-item"
style=
"width: 300px"
/>
<div
class=
"filter-item flex items-center"
>
<el-button
type=
"primary"
:icon=
"Search"
@
click=
"emit('search')"
>
搜索
</el-button>
<el-button
@
click=
"emit('reset')"
>
重置
</el-button>
<slot
/>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
/**
* 后台内容管理各个列表页(专栏/专访/视频/问吧/实践/帖子)共用的搜索栏:
* 标题、标签(多选)、创建人(模糊)、创建时间范围(年月日)。
* 筛选值直接写进传进来的 searchParams,搜索/重置由页面自己处理;页面特有的按钮放默认插槽。
*/
import
{
Search
}
from
'@element-plus/icons-vue'
import
{
storeToRefs
}
from
'pinia'
import
{
useTagsStore
}
from
'@/stores/tags'
import
{
formatSeconds
}
from
'@/utils/app'
import
type
{
ArticleSearchParams
}
from
'@/api'
const
params
=
defineModel
<
ArticleSearchParams
>
({
required
:
true
})
const
emit
=
defineEmits
<
{
search
:
[];
reset
:
[]
}
>
()
const
{
tagList
}
=
storeToRefs
(
useTagsStore
())
const
createTimeRange
=
computed
<
string
[]
|
null
>
({
get
:
()
=>
{
const
{
createTimeStart
,
createTimeEnd
}
=
params
.
value
return
createTimeStart
&&
createTimeEnd
?
[
String
(
createTimeStart
),
String
(
createTimeEnd
)]
:
null
},
set
:
(
range
)
=>
{
// 后端要秒级时间戳,结束时间转成当天 23:59:59;清空时必须是 undefined,设 0 会一条都查不出来
params
.
value
.
createTimeStart
=
range
?
Number
(
range
[
0
])
:
undefined
params
.
value
.
createTimeEnd
=
range
?
formatSeconds
(
Number
(
range
[
1
]))
:
undefined
},
})
</
script
>
<
style
scoped
>
.content-search-bar
{
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
12px
;
align-items
:
center
;
}
/* 选择框/日期选择器自带 flex-grow,不禁掉会把一整行撑满 */
.filter-item
{
flex
:
none
;
}
</
style
>
src/views/backend/contentsMenu/components/useAdminDeleteArticle.ts
0 → 100644
View file @
bd21693c
import
{
deleteArticleByAdmin
}
from
'@/api'
import
{
useMessageBox
}
from
'@/hooks'
import
{
push
}
from
'notivue'
/**
* 后台内容管理各列表页的「删除」:删了前台就看不到了,必须二次确认。
* typeLabel 是页面对应的类型名(帖子/专栏…),用在确认文案里。
*/
export
const
useAdminDeleteArticle
=
(
typeLabel
:
string
,
onDeleted
:
()
=>
void
)
=>
{
const
{
confirm
}
=
useMessageBox
()
return
async
(
row
:
{
id
:
number
;
title
:
string
})
=>
{
await
confirm
({
title
:
'删除确认'
,
message
:
`确定要删除
${
typeLabel
}
《
${
row
.
title
}
》吗?删除后前台将无法查看,且无法恢复。`
,
confirmText
:
'确认删除'
,
type
:
'danger'
,
})
await
deleteArticleByAdmin
(
row
.
id
)
push
.
success
(
'删除成功'
)
onDeleted
()
}
}
src/views/backend/contentsMenu/interviewManage/index.vue
View file @
bd21693c
<
script
setup
lang=
"ts"
>
import
{
Search
,
Upload
}
from
'@element-plus/icons-vue'
import
ContentSearchBar
from
'../components/ContentSearchBar.vue'
import
{
useAdminDeleteArticle
}
from
'../components/useAdminDeleteArticle'
import
{
Upload
}
from
'@element-plus/icons-vue'
import
{
usePageSearch
,
useResetData
}
from
'@/hooks'
import
{
addOrUpdateColumn
}
from
'@/api/backend'
import
{
getArticleList
,
updateArticleRecommendAndSort
,
recallPush
}
from
'@/api'
...
...
@@ -21,6 +23,8 @@ const { loading, list, total, reset, goToPage, changePageSize, refresh, searchPa
},
})
const
handleDelete
=
useAdminDeleteArticle
(
'专访'
,
()
=>
refresh
())
// 对话框
const
dialogVisible
=
ref
(
false
)
const
dialogTitle
=
computed
(()
=>
(
form
.
value
.
id
?
'编辑'
:
'新增'
))
...
...
@@ -140,13 +144,7 @@ const handleRecallPush = async (row: ArticleItemDto) => {
<div
class=
"official-tag-page"
>
<!-- 搜索栏 -->
<div
class=
"search-section"
>
<el-input
v-model=
"searchParams.title"
placeholder=
"请输入标题"
class=
"w-200px mr-12px"
></el-input>
<el-button
type=
"primary"
:icon=
"Search"
@
click=
"refresh"
>
搜索
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
<ContentSearchBar
v-model=
"searchParams"
@
search=
"refresh"
@
reset=
"reset"
/>
</div>
<!-- 表格区域 -->
...
...
@@ -242,6 +240,12 @@ const handleRecallPush = async (row: ArticleItemDto) => {
<span
v-else
>
{{
pushStatusText
(
0
)
}}
</span>
</
template
>
</el-table-column>
<el-table-column
label=
"操作"
width=
"100"
fixed=
"right"
>
<
template
#
default=
"{ row }"
>
<el-button
type=
"danger"
link
@
click=
"handleDelete(row)"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
</div>
...
...
src/views/backend/contentsMenu/postManage/index.vue
View file @
bd21693c
<
script
setup
lang=
"ts"
>
import
{
Search
,
Upload
}
from
'@element-plus/icons-vue'
import
ContentSearchBar
from
'../components/ContentSearchBar.vue'
import
{
useAdminDeleteArticle
}
from
'../components/useAdminDeleteArticle'
import
{
Upload
}
from
'@element-plus/icons-vue'
import
{
usePageSearch
,
useResetData
}
from
'@/hooks'
import
{
addOrUpdateColumn
}
from
'@/api/backend'
import
{
getArticleList
,
updateArticleRecommendAndSort
,
recallPush
}
from
'@/api'
import
{
getArticleList
,
updateArticleRecommendAndSort
,
recallPush
,
}
from
'@/api'
import
type
{
FormInstance
,
FormRules
}
from
'element-plus'
import
type
{
BackendColumnListItemDto
,
AddOrUpdateColumnDto
}
from
'@/api/backend'
import
dayjs
from
'dayjs'
...
...
@@ -21,6 +27,8 @@ const { loading, list, total, reset, goToPage, changePageSize, refresh, searchPa
},
})
const
handleDelete
=
useAdminDeleteArticle
(
'帖子'
,
()
=>
refresh
())
// 对话框
const
dialogVisible
=
ref
(
false
)
const
dialogTitle
=
computed
(()
=>
(
form
.
value
.
id
?
'编辑'
:
'新增'
))
...
...
@@ -139,13 +147,7 @@ const handleRecallPush = async (row: ArticleItemDto) => {
<div
class=
"official-tag-page"
>
<!-- 搜索栏 -->
<div
class=
"search-section"
>
<el-input
v-model=
"searchParams.title"
placeholder=
"请输入标题"
class=
"w-200px mr-12px"
></el-input>
<el-button
type=
"primary"
:icon=
"Search"
@
click=
"refresh"
>
搜索
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
<ContentSearchBar
v-model=
"searchParams"
@
search=
"refresh"
@
reset=
"reset"
/>
</div>
<!-- 表格区域 -->
...
...
@@ -241,6 +243,12 @@ const handleRecallPush = async (row: ArticleItemDto) => {
<span
v-else
>
{{
pushStatusText
(
0
)
}}
</span>
</
template
>
</el-table-column>
<el-table-column
label=
"操作"
width=
"100"
fixed=
"right"
>
<
template
#
default=
"{ row }"
>
<el-button
type=
"danger"
link
@
click=
"handleDelete(row)"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
</div>
...
...
src/views/backend/contentsMenu/practiceManage/index.vue
View file @
bd21693c
<
script
setup
lang=
"ts"
>
import
{
Search
,
Upload
}
from
'@element-plus/icons-vue'
import
ContentSearchBar
from
'../components/ContentSearchBar.vue'
import
{
useAdminDeleteArticle
}
from
'../components/useAdminDeleteArticle'
import
{
Upload
}
from
'@element-plus/icons-vue'
import
{
usePageSearch
,
useResetData
}
from
'@/hooks'
import
{
addOrUpdateColumn
}
from
'@/api/backend'
import
{
getArticleList
,
updateArticleRecommendAndSort
,
getArticleDetail
,
addOrUpdatePractice
}
from
'@/api'
...
...
@@ -24,6 +26,8 @@ const { loading, list, total, reset, goToPage, changePageSize, refresh, searchPa
},
})
const
handleDelete
=
useAdminDeleteArticle
(
'实践'
,
()
=>
refresh
())
// 对话框
const
dialogVisible
=
ref
(
false
)
const
dialogTitle
=
computed
(()
=>
(
form
.
value
.
id
?
'编辑'
:
'新增'
))
...
...
@@ -162,13 +166,7 @@ const handleSubmitPermission = async () => {
<div
class=
"official-tag-page"
>
<!-- 搜索栏 -->
<div
class=
"search-section"
>
<el-input
v-model=
"searchParams.title"
placeholder=
"请输入标题"
class=
"w-200px mr-12px"
></el-input>
<el-button
type=
"primary"
:icon=
"Search"
@
click=
"refresh"
>
搜索
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
<ContentSearchBar
v-model=
"searchParams"
@
search=
"refresh"
@
reset=
"reset"
/>
</div>
<!-- 表格区域 -->
...
...
@@ -246,6 +244,12 @@ const handleSubmitPermission = async () => {
{{
dayjs
(
row
.
createTime
*
1000
).
format
(
'YYYY-MM-DD HH:mm:ss'
)
}}
</
template
>
</el-table-column>
<el-table-column
label=
"操作"
width=
"100"
fixed=
"right"
>
<
template
#
default=
"{ row }"
>
<el-button
type=
"danger"
link
@
click=
"handleDelete(row)"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
</div>
...
...
src/views/backend/contentsMenu/questionManage/index.vue
View file @
bd21693c
<
script
setup
lang=
"ts"
>
import
{
Search
,
Upload
}
from
'@element-plus/icons-vue'
import
ContentSearchBar
from
'../components/ContentSearchBar.vue'
import
{
useAdminDeleteArticle
}
from
'../components/useAdminDeleteArticle'
import
{
Upload
}
from
'@element-plus/icons-vue'
import
{
usePageSearch
,
useResetData
}
from
'@/hooks'
import
{
addOrUpdateColumn
}
from
'@/api/backend'
import
{
getArticleList
,
updateArticleRecommendAndSort
,
recallPush
}
from
'@/api'
...
...
@@ -21,6 +23,8 @@ const { loading, list, total, reset, goToPage, changePageSize, refresh, searchPa
},
})
const
handleDelete
=
useAdminDeleteArticle
(
'问吧'
,
()
=>
refresh
())
// 对话框
const
dialogVisible
=
ref
(
false
)
const
dialogTitle
=
computed
(()
=>
(
form
.
value
.
id
?
'编辑'
:
'新增'
))
...
...
@@ -139,13 +143,7 @@ const handleRecallPush = async (row: ArticleItemDto) => {
<div
class=
"official-tag-page"
>
<!-- 搜索栏 -->
<div
class=
"search-section"
>
<el-input
v-model=
"searchParams.title"
placeholder=
"请输入标题"
class=
"w-200px mr-12px"
></el-input>
<el-button
type=
"primary"
:icon=
"Search"
@
click=
"refresh"
>
搜索
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
<ContentSearchBar
v-model=
"searchParams"
@
search=
"refresh"
@
reset=
"reset"
/>
</div>
<!-- 表格区域 -->
...
...
@@ -241,6 +239,12 @@ const handleRecallPush = async (row: ArticleItemDto) => {
<span
v-else
>
{{
pushStatusText
(
0
)
}}
</span>
</
template
>
</el-table-column>
<el-table-column
label=
"操作"
width=
"100"
fixed=
"right"
>
<
template
#
default=
"{ row }"
>
<el-button
type=
"danger"
link
@
click=
"handleDelete(row)"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
</div>
...
...
src/views/backend/contentsMenu/videoManage/index.vue
View file @
bd21693c
<
script
setup
lang=
"ts"
>
import
{
Search
,
Upload
}
from
'@element-plus/icons-vue'
import
ContentSearchBar
from
'../components/ContentSearchBar.vue'
import
{
useAdminDeleteArticle
}
from
'../components/useAdminDeleteArticle'
import
{
Upload
}
from
'@element-plus/icons-vue'
import
{
usePageSearch
,
useResetData
}
from
'@/hooks'
import
{
addOrUpdateColumn
}
from
'@/api/backend'
import
{
...
...
@@ -31,6 +33,8 @@ const { loading, list, total, reset, goToPage, changePageSize, refresh, searchPa
},
})
const
handleDelete
=
useAdminDeleteArticle
(
'视频'
,
()
=>
refresh
())
// 对话框
const
dialogVisible
=
ref
(
false
)
const
dialogTitle
=
computed
(()
=>
(
form
.
value
.
id
?
'编辑'
:
'新增'
))
...
...
@@ -158,14 +162,9 @@ const handleEditCategory = (row: ArticleItemDto) => {
<div
class=
"official-tag-page"
>
<!-- 搜索栏 -->
<div
class=
"search-section"
>
<el-input
v-model=
"searchParams.title"
placeholder=
"请输入标题"
class=
"w-200px mr-12px"
></el-input>
<el-button
type=
"primary"
:icon=
"Search"
@
click=
"refresh"
>
搜索
</el-button>
<el-button
@
click=
"reset"
>
重置
</el-button>
<ContentSearchBar
v-model=
"searchParams"
@
search=
"refresh"
@
reset=
"reset"
>
<el-button
@
click=
"categoryManageDialogRef?.open()"
>
视频分类管理
</el-button>
</ContentSearchBar>
</div>
<!-- 表格区域 -->
...
...
@@ -305,6 +304,12 @@ const handleEditCategory = (row: ArticleItemDto) => {
<span
v-else
>
{{
pushStatusText
(
0
)
}}
</span>
</
template
>
</el-table-column>
<el-table-column
label=
"操作"
width=
"100"
fixed=
"right"
>
<
template
#
default=
"{ row }"
>
<el-button
type=
"danger"
link
@
click=
"handleDelete(row)"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
</div>
...
...
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