Commit f4265cd7 by lijiabin

【需求 17679】 feat: 优化轮播图等

parent 6c5db609
/// <reference types="vite/client" /> /// <reference types="vite/client" />
import dayjs from 'dayjs'
// 声明虚拟模块
declare module 'virtual:push-update' {
const updateInfo: string
export default updateInfo
}
declare module 'vue' {
interface ComponentCustomProperties {
dayjs: typeof dayjs
}
}
import service from '@/utils/request/index' import service from '@/utils/request/index'
import type { AddOrUpdateTagDto, BackendTagListItemDto, BackendTagSearchParams } from './types' import type {
ArticleSearchParams,
AddOrUpdateCarouselDto,
BackendArticleListItemDto,
BackendCarouselListItemDto,
} from './types'
import type { BackendServicePageResult } from '@/utils/request/types' import type { BackendServicePageResult } from '@/utils/request/types'
// 后台管理标签相关接口 // 后台管理标签相关接口
...@@ -7,7 +12,7 @@ import type { BackendServicePageResult } from '@/utils/request/types' ...@@ -7,7 +12,7 @@ import type { BackendServicePageResult } from '@/utils/request/types'
* 获取轮播图列表 不分页 数量不多 * 获取轮播图列表 不分页 数量不多
*/ */
export const getCarouselList = () => { export const getCarouselList = () => {
return service.request<BackendServicePageResult<BackendTagListItemDto>>({ return service.request<BackendCarouselListItemDto[]>({
url: '/api/cultureCarousel/listNoPage', url: '/api/cultureCarousel/listNoPage',
method: 'POST', method: 'POST',
data: {}, data: {},
...@@ -15,7 +20,7 @@ export const getCarouselList = () => { ...@@ -15,7 +20,7 @@ export const getCarouselList = () => {
} }
/** /**
* 添加轮播 * 添加轮播
*/ */
export const addCarousel = (data: AddOrUpdateCarouselDto) => { export const addCarousel = (data: AddOrUpdateCarouselDto) => {
return service.request({ return service.request({
...@@ -39,9 +44,20 @@ export const editCarousel = (data: AddOrUpdateCarouselDto) => { ...@@ -39,9 +44,20 @@ export const editCarousel = (data: AddOrUpdateCarouselDto) => {
/** /**
* 更改发布状态 * 更改发布状态
*/ */
export const deleteCarousel = (id: number) => { // export const deleteCarousel = (id: number) => {
return service.request<boolean>({ // return service.request<boolean>({
url: `/api/cultureCarousel/updateRelease?id=${id}`, // url: `/api/cultureCarousel/updateRelease?id=${id}`,
// method: 'POST',
// })
// }
/**
* 根据类型获取文章列表
*/
export const getArticleList = (data: ArticleSearchParams) => {
return service.request<BackendServicePageResult<BackendArticleListItemDto>>({
url: `/api/cultureArticle/listByPage`,
method: 'POST', method: 'POST',
data,
}) })
} }
import { ArticleTypeEnum } from '@/constants'
import type { PageSearchParams } from '@/utils/request/types'
export interface ArticleSearchParams extends PageSearchParams {
title?: string
type?: ArticleTypeEnum
}
export interface AddOrUpdateCarouselDto {
articleTitle: string
assetUrl: string
id?: number
isRelease: number
sort: number
type: number
url: string
}
export interface BackendCarouselListItemDto {
articleId: number
articleTitle: string
assetUrl: string
id: number
isRelease: number
lastName: string
lastTime: number
sort: number
type: number
url: string
}
export interface BackendArticleListItemDto {
collectionCount: number
content: string
createTime: number
createUserId: number
description: string
faceUrl: string
hasAddQuestion: boolean
hasCollect: boolean
hasPraised: boolean
id: number
isRecommend: number
isRelateColleague: number
praiseCount: number
releaseStatus: number
replyCount: number
showAvatar: string
showName: string
tagNameList: string[]
title: string
type: ArticleTypeEnum
videoDuration: string
videoUrl: string
viewCount: number
}
...@@ -109,6 +109,13 @@ export function usePageSearch< ...@@ -109,6 +109,13 @@ export function usePageSearch<
search() search()
} }
// 清空搜索数据和列表
const clear = () => {
list.value = []
total.value = 0
resetSearchParams()
}
if (immediate) { if (immediate) {
onMounted(search) onMounted(search)
} }
...@@ -123,5 +130,6 @@ export function usePageSearch< ...@@ -123,5 +130,6 @@ export function usePageSearch<
goToPage, goToPage,
changePageSize, changePageSize,
refresh, refresh,
clear,
} }
} }
...@@ -13,9 +13,9 @@ ...@@ -13,9 +13,9 @@
<!-- 表格区域 --> <!-- 表格区域 -->
<div class="table-section"> <div class="table-section">
<el-table v-loading="loading" :data="list"> <el-table v-loading="loading" :data="list">
<el-table-column type="selection" width="55" /> <!-- <el-table-column type="selection" width="55" /> -->
<el-table-column prop="image" label="资源" min-width="300"> <el-table-column prop="image" label="资源" min-width="300">
<template #default="{ row }"> <template #default="{ row }">
<div class="image-cell"> <div class="image-cell">
<el-image <el-image
...@@ -29,7 +29,13 @@ ...@@ -29,7 +29,13 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="url" label="跳转链接" width="100" align="center" /> <el-table-column prop="url" label="跳转文章标题" width="300" align="center">
<template #default="{ row }">
<el-link :underline="false" type="primary" @click="handleOpenNewTab(row.url)">
{{ row.articleTitle }}
</el-link>
</template>
</el-table-column>
<el-table-column prop="sort" label="排序" width="100" align="center" /> <el-table-column prop="sort" label="排序" width="100" align="center" />
<el-table-column prop="type" label="类型" width="120" align="center"> <el-table-column prop="type" label="类型" width="120" align="center">
...@@ -40,11 +46,10 @@ ...@@ -40,11 +46,10 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="creator" label="操作人/时间" width="200"> <el-table-column prop="creator" label="操作人" width="200">
<template #default="{ row }"> <template #default="{ row }">
<div class="creator-info"> <div class="creator-info">
<div>{{ row.creator }}</div> <div>{{ row.lastName }}</div>
<div class="create-time">{{ row.createTime }}</div>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
...@@ -62,46 +67,43 @@ ...@@ -62,46 +67,43 @@
<el-dialog <el-dialog
v-model="dialogVisible" v-model="dialogVisible"
:title="dialogTitle" :title="dialogTitle"
width="600px" width="500px"
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<el-alert <el-alert
title="以下网址请仔细核对是否正确,若输入错误,会导致跳转错误页面,或者资源加载不出来" title="未选择对应的文章,则不跳转"
type="info" type="info"
:closable="false" :closable="false"
style="margin-bottom: 20px" style="margin-bottom: 20px"
/> />
<el-form ref="formRef" :model="form" :rules="formRules" label-width="100px"> <el-form ref="formRef" :model="form" :rules="formRules" label-width="100px">
<el-form-item label="轮播类型" prop="type"> <!-- <el-form-item label="轮播类型" prop="type">
<el-radio-group v-model="form.type"> <el-radio-group v-model="form.type">
<el-radio :value="0">图片</el-radio> <el-radio :value="0">图片</el-radio>
<el-radio value="1">视频</el-radio> <el-radio :value="1">视频</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item> -->
<el-form-item label="显示资源" prop="image"> <el-form-item label="显示资源" prop="assetUrl">
<div class="upload-section"> <div class="upload-section">
<UploadFile v-model="form.assetUrl" :limit="1" /> <UploadFile v-model="form.assetUrl" :limit="1" />
<div class="upload-hint">上传图片,推荐比例为 W/H: 4 / 1</div> <div class="upload-hint">上传图片,推荐比例为 W/H: 4 / 1</div>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="跳转链接" prop="link"> <el-form-item label="跳转文章标题" prop="url">
<el-input <span class="mr-2">{{ form.articleTitle }}</span>
v-model="form.link" <el-link :underline="false" @click="articleDialogVisible = true">请选择</el-link>
placeholder="不填表示点击不跳转"
maxlength="100"
show-word-limit
/>
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="sort"> <el-form-item label="排序" prop="sort">
<el-input-number <el-input-number
v-model="form.sort" v-model="form.sort"
:min="0" :min="0"
:max="100"
controls-position="right" controls-position="right"
style="width: 100%" style="width: 60%"
/> />
</el-form-item> </el-form-item>
</el-form> </el-form>
...@@ -113,6 +115,62 @@ ...@@ -113,6 +115,62 @@
保存 保存
</el-button> </el-button>
</template> </template>
<el-dialog v-model="articleDialogVisible" title="选择文章" width="850px">
<!-- 类型选择 -->
<div class="flex items-center gap-2 mb-2">
<el-input v-model="searchParams.title" placeholder="请输入文章标题" />
<el-select v-model="searchParams.type" placeholder="请选择文章类型">
<el-option
v-for="item in articleTypeListOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-button type="primary" @click="handleSearchArticle"> 搜索 </el-button>
</div>
<!-- 文章列表 -->
<el-table :data="articleList" style="width: 100%">
<el-table-column prop="title" label="标题">
<template #default="{ row }">
<el-link :underline="false" type="primary" @click="handlePreviewArticle(row)">
{{ row.title }}
</el-link>
</template>
</el-table-column>
<el-table-column prop="type" label="类型" width="100" tooltip-effect="dark">
<template #default="{ row }">
<el-tag type="primary">
{{ articleTypeListOptions.find((item) => item.value === row.type)?.label }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="200">
<template #default="{ row }">
{{ dayjs(row.createTime * 1000 || 0).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column prop="showName" label="创建人" width="150" />
<el-table-column prop="showName" label="操作" width="80">
<template #default="{ row }">
<el-button type="primary" link @click="handleSelectArticle(row)">选择</el-button>
</template>
</el-table-column>
</el-table>
<div v-show="articleList.length" class="flex justify-end mt-2">
<el-pagination
v-model:current-page="searchParams.current"
v-model:page-size="searchParams.size"
:total="total"
:page-sizes="[10, 20, 30]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="changePageSize"
@current-change="goToPage"
/>
</div>
</el-dialog>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
...@@ -120,15 +178,22 @@ ...@@ -120,15 +178,22 @@
<script setup lang="ts"> <script setup lang="ts">
import { Search, Plus, Upload } from '@element-plus/icons-vue' import { Search, Plus, Upload } from '@element-plus/icons-vue'
import { useResetData } from '@/hooks' import { useResetData } from '@/hooks'
import { getCarouselList, addCarousel, editCarousel } from '@/api/backend' import { getCarouselList, addCarousel, editCarousel, getArticleList } from '@/api/backend'
import type { FormInstance, FormRules } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus'
import UploadFile from '@/components/common/UploadFile/index.vue' import UploadFile from '@/components/common/UploadFile/index.vue'
import type { CarouselListItemDto, AddOrUpdateCarouselDto } from '@/api/backend' import type {
AddOrUpdateCarouselDto,
BackendArticleListItemDto,
BackendCarouselListItemDto,
} from '@/api/backend'
import { ArticleTypeEnum, articleTypeListOptions } from '@/constants'
import { usePageSearch } from '@/hooks'
import dayjs from 'dayjs'
// 列表数据 // 列表数据
const loading = ref(false) const loading = ref(false)
const list = ref<CarouselListItemDto[]>([]) const list = ref<BackendCarouselListItemDto[]>([])
// 获取列表 // 获取列表
const getList = async () => { const getList = async () => {
...@@ -150,10 +215,11 @@ const formRef = ref<FormInstance>() ...@@ -150,10 +215,11 @@ const formRef = ref<FormInstance>()
const [form, resetForm] = useResetData<AddOrUpdateCarouselDto>({ const [form, resetForm] = useResetData<AddOrUpdateCarouselDto>({
type: 0, type: 0,
assetUrl: '', assetUrl: '',
link: '', url: '',
sort: 0, sort: 0,
id: undefined, id: undefined,
isRelease: 1, isRelease: 1,
articleTitle: '',
}) })
// 表单验证规则 // 表单验证规则
...@@ -170,21 +236,22 @@ const handleAdd = () => { ...@@ -170,21 +236,22 @@ const handleAdd = () => {
} }
// 编辑 // 编辑
const handleEdit = (row: CarouselListItemDto) => { const handleEdit = (row: BackendCarouselListItemDto) => {
resetForm() resetForm()
form.value = { form.value = {
type: row.type, type: row.type,
assetUrl: row.assetUrl, assetUrl: row.assetUrl,
link: row.link, url: row.url,
sort: row.sort, sort: row.sort,
id: row.id, id: row.id,
isRelease: row.isRelease, isRelease: row.isRelease,
articleTitle: row.articleTitle,
} }
dialogVisible.value = true dialogVisible.value = true
} }
// 删除 // 删除
const handleDelete = async (row: CarouselListItemDto) => { const handleDelete = async (row: BackendCarouselListItemDto) => {
try { try {
await ElMessageBox.confirm('确定要删除该轮播吗?', '提示', { await ElMessageBox.confirm('确定要删除该轮播吗?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
...@@ -193,8 +260,9 @@ const handleDelete = async (row: CarouselListItemDto) => { ...@@ -193,8 +260,9 @@ const handleDelete = async (row: CarouselListItemDto) => {
}) })
await editCarousel({ await editCarousel({
...row,
id: row.id, id: row.id,
isRelease: 0, isRelease: 0, // 状态设置为0
}) })
ElMessage.success('删除成功') ElMessage.success('删除成功')
...@@ -226,7 +294,52 @@ const handleSubmit = async () => { ...@@ -226,7 +294,52 @@ const handleSubmit = async () => {
} }
} }
// 初始化 const articleDialogVisible = ref(false)
const {
list: articleList,
searchParams,
refresh,
total,
changePageSize,
goToPage,
clear,
} = usePageSearch(getArticleList, {
defaultParams: {
title: '',
type: ArticleTypeEnum.POST,
},
immediate: false,
})
const handleSearchArticle = async () => {
refresh()
}
const handleOpenNewTab = (url: string) => {
window.open(url)
}
const handlePreviewArticle = (row: BackendArticleListItemDto) => {
if (row.type === ArticleTypeEnum.VIDEO) {
window.open(`/videoDetail/${row.id}`)
} else {
window.open(`/articleDetail/${row.id}`)
}
}
const handleSelectArticle = (row: BackendArticleListItemDto) => {
if (row.type === ArticleTypeEnum.VIDEO) {
form.value.url = `/videoDetail/${row.id}`
} else {
form.value.url = `/articleDetail/${row.id}`
}
form.value.articleTitle = row.title
// 重置表单列表
clear()
articleDialogVisible.value = false
}
onMounted(() => { onMounted(() => {
getList() getList()
}) })
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
v-model="searchParams.title" v-model="searchParams.title"
placeholder="请输入栏目标题" placeholder="请输入栏目标题"
class="w-200px" class="w-200px"
clearable
></el-input> ></el-input>
<el-select <el-select
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
v-model="searchParams.title" v-model="searchParams.title"
placeholder="请输入栏目标题" placeholder="请输入栏目标题"
class="w-200px" class="w-200px"
clearable
></el-input> ></el-input>
<el-select <el-select
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
v-model="searchParams.title" v-model="searchParams.title"
placeholder="请输入栏目标题" placeholder="请输入栏目标题"
class="w-200px" class="w-200px"
clearable
></el-input> ></el-input>
<el-select <el-select
......
...@@ -3,7 +3,12 @@ ...@@ -3,7 +3,12 @@
<!-- aspect-[16/6] --> <!-- aspect-[16/6] -->
<div class="banner mb-3 w-full aspect-[4/1] overflow-hidden"> <div class="banner mb-3 w-full aspect-[4/1] overflow-hidden">
<el-carousel class="h-full w-full shadow-lg rounded-lg"> <el-carousel class="h-full w-full shadow-lg rounded-lg">
<el-carousel-item v-for="(item, index) in carouselList" :key="index" class="h-full w-full"> <el-carousel-item
v-for="(item, index) in carouselList"
:key="index"
class="h-full w-full"
@click="handleClickCarousel(item)"
>
<el-image :src="item.assetUrl" class="w-full h-full object-cover" /> <el-image :src="item.assetUrl" class="w-full h-full object-cover" />
</el-carousel-item> </el-carousel-item>
</el-carousel> </el-carousel>
...@@ -428,6 +433,12 @@ const publishTopic = () => { ...@@ -428,6 +433,12 @@ const publishTopic = () => {
} }
} }
const handleClickCarousel = (item: CarouselItemDto) => {
if (item.url) {
window.open(item.url)
}
}
const initPage = () => { const initPage = () => {
Promise.allSettled([getCarouselList(), getUserAccountData(), getRecordData()]).then( Promise.allSettled([getCarouselList(), getUserAccountData(), getRecordData()]).then(
([r1, r2, r3]) => { ([r1, r2, r3]) => {
......
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