Commit 5b4d9245 by lijiabin

【需求 17679】 wip: 继续完善页面等

parent 797a579b
......@@ -175,10 +175,7 @@ export const deleteArticle = (id: number) => {
*/
export const deleteComment = (commentId: number) => {
return service.request({
url: `/api/cultureComment/deleteComment`,
url: `/api/cultureComment/deleteComment?commentId=${commentId}`,
method: 'POST',
data: {
commentId,
},
})
}
......@@ -8,18 +8,7 @@ import type { BackendServicePageResult } from '@/utils/request/types'
*/
export const getCaseList = (data: PageSearchParams) => {
return service.request<BackendServicePageResult<BackendTagListItemDto>>({
url: '/api/cultureCase/caseAuditByPage',
method: 'POST',
data,
})
}
/**
* 添加轮播图
*/
export const addCarousel = (data: AddOrUpdateCarouselDto) => {
return service.request({
url: '/api/cultureCarousel/addCarousel',
url: '/api/cultureCase/caseListByPage',
method: 'POST',
data,
})
......
......@@ -4,12 +4,54 @@ import type { BackendServicePageResult } from '@/utils/request/types'
// 后台管理 积分商城相关接口
/**
* 商品领用列表
* 商品配置列表
*/
export const getExchangeList = (params: BackendTagSearchParams) => {
export const getShopItemList = (params: BackendTagSearchParams) => {
return service.request<BackendServicePageResult<BackendTagListItemDto>>({
url: '/api/culture/shop/order/exchangeList',
url: '/api/culture/shop/item/productList',
method: 'POST',
data: params,
})
}
/**
* 新增/编辑商品
*/
export const addOrUpdateShopItem = (data: AddOrUpdateTagDto) => {
return service.request<BackendServicePageResult<BackendTagListItemDto>>({
url: '/api/culture/shop/item/addOrUpdate',
method: 'POST',
data,
})
}
/*
* 删除商品
*/
export const deleteShopItem = (id: number) => {
return service.request({
url: `/api/culture/shop/item/deleteProduct?id=${id}`,
method: 'POST',
})
}
/**
* 后台商品领用列表
*/
export const getBackendExchangeList = (data: BackendTagSearchParams) => {
return service.request<BackendServicePageResult<BackendTagListItemDto>>({
url: '/api/culture/shop/order/background/productList',
method: 'POST',
data,
})
}
/**
* 发放 取消发放
*/
export const issueProduct = (data) => {
return service.request({
url: `/api/culture/shop/order/issueProduct`,
method: 'POST',
data,
})
}
......@@ -8,6 +8,7 @@ import type {
YaBiData,
ExchangeYabiRecordItemDto,
ExchangeGoodsRecordSearchParams,
BackendShopItemDto,
} from './types'
/**
* 获取用户亚币相关数据
......@@ -24,7 +25,7 @@ export const getYaBiData = () => {
* 积分商城商品列表
*/
export const getShopItemList = (data: ShopSearchParams) => {
return service.request<BackendServicePageResult<ShopItemDto>>({
return service.request<BackendServicePageResult<BackendShopItemDto>>({
url: '/api/culture/shop/item/pageList',
method: 'POST',
data,
......
......@@ -12,18 +12,19 @@ export interface ShopSearchParams extends PageSearchParams {
/**
* 积分商城商品类型
*/
export interface ShopItemDto {
id: number
export interface BackendShopItemDto {
createTime: number
description: string
enable: number
enable: BooleanFlag
id: number
imageUrl: string
itemType: number
itemType: ShopGoodsTypeEnum
itemTypeName: string
name: string
price: number
region: string
soldOut: boolean
sotrOrder: number
sortOrder: number
stock: number
}
......
......@@ -51,7 +51,7 @@
<div class="flex-1">
<div ref="commentInputRef">
<el-input
v-model="comment"
v-model="myComment"
type="textarea"
placeholder="写下你的评论..."
:rows="3"
......@@ -68,8 +68,8 @@
</div>
<button
class="cursor-pointer disabled:opacity-50 px-6 py-2 bg-gradient-to-r from-blue-500 to-purple-500 text-white rounded-full text-sm hover:shadow-lg transition-all"
:disabled="!comment.trim() || loading"
@click="handleComment()"
:disabled="!myComment.trim() || loading"
@click="handleMyComment()"
>
发表
</button>
......@@ -81,7 +81,7 @@
<!-- 评论列表 -->
<div v-loading="loading" class="divide-y divide-gray-100" v-if="list.length">
<div v-for="item in list" :key="item.id">
<div class="p-4 hover:bg-gray-50/50 transition-colors">
<div class="p-4 transition-colors">
<div class="flex gap-3">
<img :src="item.avatar" alt="" class="w-10 h-10 rounded-full object-cover" />
<div class="flex-1">
......@@ -134,9 +134,6 @@
<div class="flex-1">
<div class="flex items-center gap-2 mb-1">
<span class="font-medium text-sm text-gray-800">{{ child.replyUser }}</span>
<span class="text-xs text-gray-500">{{
dayjs(child.createTime * 1000).format('YYYY-MM-DD HH:mm:ss')
}}</span>
</div>
<p class="text-sm text-gray-700">
{{ child.content }}
......@@ -169,13 +166,13 @@
</div>
</div>
</div>
<div v-show="showCommentBox(item)" class="flex gap-3">
<div v-show="showCommentBox(item)" class="flex gap-3 mt-4">
<img :src="userInfo?.avatar" alt="" class="w-10 h-10 rounded-full object-cover" />
<div class="flex-1">
<el-input
v-model="comment"
type="textarea"
placeholder="写下你的评论..."
:placeholder="replyPlaceholder"
:rows="3"
></el-input>
<div class="flex justify-between items-center mt-3">
......@@ -190,7 +187,7 @@
<button
class="cursor-pointer disabled:opacity-50 px-6 py-2 bg-gradient-to-r from-blue-500 to-purple-500 text-white rounded-full text-sm hover:shadow-lg transition-all"
:disabled="!comment.trim()"
@click="handleComment(item.id)"
@click="handleComment"
>
发表
</button>
......@@ -235,6 +232,8 @@ const { id } = defineProps<{
id: number | string
}>()
const total = defineModel<number>('total', { required: true, default: 0 })
const userStore = useUserStore()
const { userInfo } = storeToRefs(userStore)
......@@ -247,7 +246,7 @@ const { triggerAnimation } = useHintAnimation(commentInputRef, {
classes: ['scale-bounce', 'highlight', 'shake-x'],
})
const { list, searchParams, goToPage, total, loading, changePageSize, refresh } = usePageSearch(
const { list, searchParams, goToPage, loading, changePageSize, refresh } = usePageSearch(
getCommentList,
{
defaultParams: {
......@@ -263,7 +262,16 @@ const handleCurrentChange = async (e: number) => {
triggerAnimation()
}, 500)
}
// 自己发出的评论
const myComment = ref('')
// 回复别人的
const comment = ref('')
// 回复别人placeholder
const replyPlaceholder = ref('回复@')
const currentCommentId = ref(-1)
const handleLickComment = async (item: CommentItemDto) => {
await addOrCancelCommentLike(item.id)
......@@ -278,66 +286,40 @@ const handleLickComment = async (item: CommentItemDto) => {
}
}
const currentParentCommentId = ref(0)
const currentSonCommentId = ref(0)
const handleReply = (item: CommentItemDto) => {
if (item.pid) {
// 点击的是子评论
if (currentSonCommentId.value) {
// 置为空
if (currentSonCommentId.value !== item.id) {
currentSonCommentId.value = item.id
currentParentCommentId.value = item.pid
} else {
currentSonCommentId.value = 0
currentParentCommentId.value = 0
}
} else {
currentSonCommentId.value = item.id
currentParentCommentId.value = item.pid
}
} else {
// 点击的是父评论
if (currentParentCommentId.value) {
// 置为空
if (currentParentCommentId.value !== item.id) {
currentParentCommentId.value = item.id
} else {
currentParentCommentId.value = 0
}
} else {
currentParentCommentId.value = item.id
currentSonCommentId.value = 0
}
}
console.log(item)
replyPlaceholder.value = `回复@${item.replyUser}:`
comment.value = ''
console.log('parent', currentParentCommentId.value, 'son', currentSonCommentId.value)
currentCommentId.value = item.id
}
const showCommentBox = (item: CommentItemDto) => {
if (currentParentCommentId.value && currentSonCommentId.value) {
// 说明在评论子评论
return (
item.id === currentParentCommentId.value &&
item.children?.some((i) => i.id === currentSonCommentId.value)
)
} else if (currentParentCommentId.value) {
// 说明在评论父评论
return item.id === currentParentCommentId.value
}
return (
currentCommentId.value === item.id || item.children.some((i) => i.id === currentCommentId.value)
)
}
const handleComment = async (pid?: number) => {
const handleMyComment = async () => {
await addComment({
articleId: id,
content: myComment.value,
})
ElMessage.success('发表评论成功')
refresh()
myComment.value = ''
total.value++
}
const handleComment = async () => {
console.log(comment.value)
const res = await addComment({
articleId: id,
content: comment.value,
...(pid ? { pid } : {}),
...(currentCommentId.value ? { pid: currentCommentId.value } : {}),
})
console.log(res)
ElMessage.success('发表评论成功')
refresh()
comment.value = ''
total.value++
}
defineExpose({
......
......@@ -9,9 +9,9 @@
:on-remove="handleRemove"
:on-change="handleChange"
:before-remove="handleBeforeRemove"
:on-exceed="handleExceed"
:multiple="multiple"
:limit="limit"
:disabled="hasReachedLimit && !multiple"
class="custom-upload"
>
<el-icon><Plus /></el-icon>
......@@ -30,7 +30,7 @@ import type { UploadFileProps } from './types'
const props = withDefaults(defineProps<UploadFileProps>(), {
limit: 2,
multiple: true,
multiple: false,
})
const modelValue = defineModel<T>({
......@@ -44,8 +44,11 @@ const dialogVisible = ref(false)
const isArrayType = computed(() => Array.isArray(modelValue.value))
const hasReachedLimit = computed(() => fileList.value.length >= props.limit)
const showUploadBtn = computed(() => (hasReachedLimit.value ? 'none' : 'flex'))
const isInternalUpdate = ref(false)
const uploadProgress = ref(0)
const parseModelValueToUrls = (value: T): string[] => {
if (!value) return []
return Array.isArray(value) ? value.filter(Boolean) : (value as string).split(',').filter(Boolean)
......@@ -98,8 +101,18 @@ watch(
{ deep: true },
)
const handleExceed: UploadProps['onExceed'] = (uploadFiles) => {
console.log('uploadFiles', uploadFiles)
if (uploadFiles.length > props.limit) {
ElMessage.error(`最多上传 ${props.limit} 个文件`)
return
}
}
const handleChange: UploadProps['onChange'] = async (uploadFile, uploadFiles) => {
console.log('uploadFiles', uploadFiles)
if (uploadFiles.length > props.limit) {
debugger
ElMessage.error(`最多上传 ${props.limit} 个文件`)
const index = fileList.value.findIndex((file) => file.uid === uploadFile.uid)
if (index !== -1) {
......@@ -117,7 +130,9 @@ const handleChange: UploadProps['onChange'] = async (uploadFile, uploadFiles) =>
fileList.value[fileIndex].status = 'uploading'
}
const { data } = await uploadFileApi(uploadFile.raw)
const { data } = await uploadFileApi(uploadFile.raw, (progress) => {
console.log('progress', progress)
})
console.log('data', data)
const url = data.fileUrl || data.data[0].filePath
const name = data.fileName || data.data[0].finalName
......@@ -181,6 +196,9 @@ defineExpose({
</script>
<style scoped>
.custom-upload :deep(.el-upload--picture-card) {
display: v-bind(showUploadBtn);
}
/* 方案1: 适中尺寸(推荐) */
.custom-upload :deep(.el-upload--picture-card) {
width: 100px;
......
......@@ -88,6 +88,10 @@ const { maxSize = 500, acceptFormats = ['mp4', 'avi', 'mov', 'wmv', 'flv'] } =
const modelValue = defineModel<string>('modelValue', { required: true })
const videoInfo = defineModel<VideoInfo | null>('videoInfo', { required: false, default: null })
const emit = defineEmits<{
uploadSuccess: [{ file: File; url: string; videoDuration: string }]
}>()
// 响应式数据
const uploadRef = ref()
const uploading = ref(false)
......@@ -180,6 +184,7 @@ const startUpload = async () => {
// 根据你的 API 返回结构调整
const videoData: VideoInfo = {
// url: data.fileUrl,
// 暂时写死
url: 'https://soundasia.oss-cn-shenzhen.aliyuncs.com/OA/readName/mp4/2025/11/12/Common/1762918987602.mp4',
name: currentFile.value.name,
size: currentFile.value.size,
......@@ -194,6 +199,12 @@ const startUpload = async () => {
// 设置url
modelValue.value = videoData.url
// 把 二进制源文件传出来
emit('uploadSuccess', {
file: currentFile.value,
url: videoData.url,
videoDuration: videoData.duration,
})
ElMessage.success('视频上传成功!')
} catch (error) {
uploading.value = false
......
......@@ -117,7 +117,7 @@ export default defineComponent((_, { expose }) => {
<el-form-item label="专栏栏目选择" prop="relateColumnId">
<el-select v-model={form.value.relateColumnId} placeholder="请选择专栏栏目">
{columnList.value.map((item) => (
<el-option value={item.id}>{item.title}</el-option>
<el-option value={item.id} label={item.title}></el-option>
))}
</el-select>
</el-form-item>
......
<template>
<div class="layout-culture pb-11 h-full bg-[linear-gradient(to_bottom,#F0FBFD_0%,#ECEFFF_100%)]">
<div
class="header flex px-40 items-center justify-between bg-white mb-1 shadow-sm fixed top-0 left-0 right-0 z-10 w-100vw"
class="header flex px-40 items-center justify-between bg-white mb-1 shadow-sm fixed top-0 left-0 right-0 z-100 w-100vw"
>
<!-- Logo区域 -->
<div @click="router.push('/')" class="flex items-center flex-shrink-0 min-w-0 cursor-pointer">
......@@ -80,7 +80,9 @@
</div>
</div>
<div class="flex-1 w-full flex items-center justify-center">
<div class="container max-h-none px-20 lg:px-20 2xl:px-30 transition-all duration-300">
<div
class="container max-h-none px-20 lg:px-20 2xl:px-30 transition-all duration-300 min-h-[calc(100vh-96px)]"
>
<router-view v-slot="{ Component, route }">
<transition name="fade" mode="out-in">
<!-- 注释不能放到keep-alive下面 route是最终的路由信息 Component是当前n级路由的组件 二级路由 homePage videoDetail -->
......
......@@ -9,6 +9,14 @@ export const useUserStore = defineStore('user', () => {
const token = ref(localStorage.getItem('token') || '')
// 获取用户信息
const fetchUserInfo = async () => {
// {
// email: 'lijiabin@yswg.com.cn',
// password: 'Lijiabin123.',
// }
// {
// email: 'wangshouyong@yswg.com.cn',
// password: 'Wsy123456!',
// }
const { data } = await loginByEmail({
email: 'lijiabin@yswg.com.cn',
password: 'Lijiabin123.',
......
......@@ -82,45 +82,4 @@ export default class DhRequest {
return Promise.reject(error as BackendServiceResult<T>)
}
}
// async get<T>(
// url: string,
// config: AxiosRequestConfig,
// ): Promise<AxiosResponse<BackendServiceResult<T>>> {
// return this.request<T>({
// url,
// method: 'GET',
// ...config,
// })
// }
// async post<T>(
// url: string,
// config: AxiosRequestConfig,
// ): Promise<AxiosResponse<BackendServiceResult<T>>> {
// return this.request<T>({
// url,
// method: 'POST',
// ...config,
// })
// }
// async put<T>(
// url: string,
// config: AxiosRequestConfig,
// ): Promise<AxiosResponse<BackendServiceResult<T>>> {
// return this.request<T>({
// url,
// method: 'PUT',
// ...config,
// })
// }
// async delete<T>(
// url: string,
// config: AxiosRequestConfig,
// ): Promise<AxiosResponse<BackendServiceResult<T>>> {
// return this.request<T>({
// url,
// method: 'DELETE',
// ...config,
// })
// }
}
......@@ -45,20 +45,36 @@
>
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="period" label="案例编号" width="100"></el-table-column>
<el-table-column prop="number" label="案例编号" width="100"></el-table-column>
<el-table-column prop="approvalRegion" label="案例标题" width="180"></el-table-column>
<el-table-column prop="title" label="案例标题" width="180"></el-table-column>
<el-table-column prop="approvalType" label="案例文本内容" width="120"></el-table-column>
<el-table-column prop="content" label="案例文本内容" width="120"></el-table-column>
<el-table-column label="文化关键词" align="center">
<el-table-column prop="receivableAmount" label="主关键词" width="100"></el-table-column>
<el-table-column prop="receivedAmount" label="次关键词1" width="100"></el-table-column>
<el-table-column prop="overdueAmount" label="次关键词2" width="100"></el-table-column>
<el-table-column prop="overdueAmount" label="次关键词3" width="100"></el-table-column>
<el-table-column prop="cultureKeywordMain" label="主关键词" width="120">
<template #default="{ row }">
<el-tag type="primary"> {{ row.cultureKeywordMain }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="cultureKeywordSecond" label="次关键词1" width="100">
<template #default="{ row }">
<el-tag type="primary"> {{ row.cultureKeywordSecond[0] || '' }} </el-tag>
</template>
</el-table-column>
<el-table-column prop="cultureKeywordSecond" label="次关键词2" width="120">
<template #default="{ row }">
<el-tag type="primary"> {{ row.cultureKeywordSecond[1] || '' }} </el-tag>
</template>
</el-table-column>
<el-table-column prop="cultureKeywordSecond" label="次关键词3" width="100">
<template #default="{ row }">
<el-tag type="primary"> {{ row.cultureKeywordSecond[2] || '' }} </el-tag>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="年度主推关键词(玄天)" align="center">
<el-table-column label="年度主推关键词(选填)" align="center">
<el-table-column prop="annualReceivable" label="主关键词" width="100"></el-table-column>
<el-table-column prop="annualOverdue" label="次关键词" width="100"></el-table-column>
</el-table-column>
......
......@@ -4,8 +4,8 @@
<div class="search-section">
<div class="flex-1 flex gap-2">
<el-input
v-model="searchParams.title"
placeholder="请输入栏目标题"
v-model="searchParams.itemName"
placeholder="请输入商品名称"
class="w-200px"
></el-input>
......@@ -15,21 +15,23 @@
class="search-select"
clearable
>
<el-option label="发布" :value="1" />
<el-option label="隐藏" :value="0" />
<el-option label="待发货" :value="0" />
<el-option label="已发货" :value="1" />
<el-option label="已取消" :value="2" />
</el-select>
<el-select
v-model="searchParams.source"
placeholder="请选择来源"
class="search-select"
clearable
>
<el-option label="商城" :value="1" />
<el-option label="大转盘" :value="2" />
<el-option label="每日抽奖" :value="3" />
</el-select>
<el-button type="primary" :icon="Search" @click="refresh">搜索</el-button>
<el-button @click="reset">重置</el-button>
</div>
<div class="flex justify-end">
<el-button type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增
</el-button>
<el-button type="primary" @click="handleBatchPublish"> 批量发布/隐藏 </el-button>
<el-button type="danger" @click="handleBatchDelete"> 批量删除 </el-button>
</div>
</div>
<!-- 表格区域 -->
......@@ -43,40 +45,58 @@
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55"> </el-table-column>
<el-table-column prop="title" label="产品编号" min-width="200" />
<el-table-column prop="content" label="商品名称" min-width="200" />
<el-table-column prop="faceUrl" label="图片" width="300">
<el-table-column prop="itemName" label="商品名称" min-width="200" />
<el-table-column prop="imageUrl" label="图片" width="300">
<template #default="{ row }">
<el-image
:preview-teleported="true"
:src="row.faceUrl"
:src="row.imageUrl"
class="w-20 h-20 object-cover"
:preview-src-list="[row.faceUrl]"
:preview-src-list="[row.imageUrl]"
/>
</template>
</el-table-column>
<el-table-column prop="videoUrl" label="来源" min-width="200"> </el-table-column>
<el-table-column prop="releaseStatus" label="兑换数量" min-width="200"> </el-table-column>
<el-table-column prop="source" label="来源" min-width="200">
<template #default="{ row }">
{{ row.source === 1 ? '商城' : row.source === 2 ? '大转盘' : '每日抽奖' }}
</template>
</el-table-column>
<el-table-column prop="num" label="兑换数量" min-width="200"> </el-table-column>
<el-table-column prop="createTime" label="兑换时间" min-width="200">
<template #default="{ row }">
{{ dayjs(row.createTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column prop="showName" label="兑换人" min-width="200" />
<el-table-column prop="showName" label="发放人" min-width="200" />
<el-table-column prop="userName" label="兑换人" min-width="200" />
<el-table-column prop="issuerName" label="发放人" min-width="200" />
<el-table-column prop="memo" label="发放备注" min-width="200" />
<el-table-column prop="createTime" label="发放时间" min-width="200">
<el-table-column prop="issueTime" label="发放时间" min-width="200">
<template #default="{ row }">
{{ dayjs(row.createTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column prop="showName" label="发放状态" min-width="200" />
<el-table-column prop="status" label="发放状态" min-width="200">
<template #default="{ row }">
{{ row.status === 0 ? '待发货' : row.status === 1 ? '已发货' : '已取消' }}
</template>
</el-table-column>
<el-table-column prop="showName" label="发放人备注" min-width="200" />
<el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="handleEdit(row)">编辑</el-button>
<el-button type="danger" link @click="handleDelete(row)">删除</el-button>
<el-button v-if="row.status === 0" type="primary" link @click="handleIssue(row)">
发放
</el-button>
<el-button
v-if="row.status === 1"
type="warning"
link
@click="handleCancelIssue(row)"
>
取消发放
</el-button>
<!-- <el-button type="danger" link @click="handleDelete(row)">删除</el-button> -->
</template>
</el-table-column>
</el-table>
......@@ -131,15 +151,23 @@
<script setup lang="ts">
import { Search, Plus, Upload } from '@element-plus/icons-vue'
import { usePageSearch, useResetData } from '@/hooks'
import { addOrUpdateColumn, deleteColumn, hideColumn, getExchangeList } from '@/api/backend'
import {
addOrUpdateColumn,
deleteColumn,
hideColumn,
getExchangeList,
getBackendExchangeList,
issueProduct,
} from '@/api/backend'
import { updateArticleRecommend } from '@/api'
import type { FormInstance, FormRules } from 'element-plus'
import type { BackendColumnListItemDto, AddOrUpdateColumnDto } from '@/api/backend'
import dayjs from 'dayjs'
import { ArticleTypeEnum } from '@/constants'
import { sortByOriginalOrder } from 'element-plus/es/components/cascader-panel/src/utils.mjs'
const { loading, list, total, reset, goToPage, changePageSize, refresh, searchParams, search } =
usePageSearch(getExchangeList, {
usePageSearch(getBackendExchangeList, {
defaultParams: {
type: ArticleTypeEnum.VIDEO,
},
......@@ -192,24 +220,34 @@ const handleIsRecommendChange = async (row: ArticleItemDto) => {
refresh()
}
// 删除
const handleDelete = async (row: BackendColumnListItemDto) => {
try {
await ElMessageBox.confirm(`确定要删除标签"${row.title}"吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
await deleteColumn([row.id])
// 取消发放
const handleCancelIssue = async (row: BackendColumnListItemDto) => {
await ElMessageBox.confirm('确定要取消发放吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
})
await issueProduct({
id: row.id,
status: 0,
})
ElMessage.success('取消发放成功')
refresh()
}
ElMessage.success('删除成功')
refresh()
} catch (error) {
if (error !== 'cancel') {
ElMessage.error('删除失败')
}
}
// 发放
const handleIssue = async (row: BackendColumnListItemDto) => {
// 弹出message输入框 填写发放备注
const memo = await ElMessageBox.prompt('请输入发放备注', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
})
await issueProduct({
id: row.id,
memo: memo.value,
status: 1,
})
ElMessage.success('发放成功')
refresh()
}
// 提交表单
......
......@@ -23,13 +23,14 @@
</div>
</div>
</div>
<div v-loading="loading">
<div v-if="list.length" v-loading="loading">
<!-- 第一页的特殊布局 -->
<div v-show="searchParams.current === 1">
<!-- 前三个特殊布局 -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8">
<!-- 第一个视频 - 占据两列 -->
<div
v-show="list.length >= 1"
@click="router.push(`/videoDetail/${list[0]?.id}`)"
class="lg:col-span-2 group relative rounded-lg overflow-hidden bg-white shadow-sm hover:shadow-xl transition-all duration-500 cursor-pointer"
>
......@@ -121,9 +122,9 @@
<!-- 右侧两个视频 -->
<div class="flex flex-col gap-6">
<div
v-for="(item, index) in [list[1], list[2]]"
v-for="(item, index) in list.slice(1, 3)"
:key="index"
class="group relative rounded-lg overflow-hidden bg-white shadow-sm hover:shadow-lg transition-all duration-300 cursor-pointer flex-1"
class="group relative rounded-lg overflow-hidden bg-white shadow-sm hover:shadow-lg transition-all duration-300 cursor-pointer"
@click="router.push(`/videoDetail/${item?.id}`)"
>
<div class="relative overflow-hidden">
......@@ -132,13 +133,6 @@
class="w-full h-44 object-cover group-hover:scale-105 transition-transform duration-500"
/>
<div class="absolute inset-0 bg-gradient-to-t from-black/40 to-transparent"></div>
<!-- 标签 -->
<!-- <div
class="absolute top-3 left-3 bg-gradient-to-r from-indigo-500 to-purple-500 text-white px-2.5 py-1 rounded-full text-xs font-semibold"
>
{{ item.tagNameList[0] }}
</div> -->
<div
v-if="item?.isRecommend"
class="absolute top-0 left-0 w-15 h-7 z-1000 bg-#FFF9B9 flex items-center justify-center border-2px border-solid border-#f4f0eb rounded-tl-lg rounded-br-lg"
......@@ -208,7 +202,7 @@
</div>
<!-- 剩余视频 - 标准网格 -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
<div v-show="list.length > 3" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
<div
@click="router.push(`/videoDetail/${item.id}`)"
v-for="item in list.slice(3)"
......@@ -416,6 +410,11 @@
</div>
</div>
</div>
<template v-else>
<div class="flex items-center justify-center h-full">
<el-empty description="暂无数据" />
</div>
</template>
</div>
</template>
......
......@@ -4,7 +4,6 @@
import { ShopGoodsTypeEnum, regionListOptions } from '@/constants'
import type { ExchangeGoodsParams, ShopItemDto } from '@/api'
import type { SetupContext } from 'vue'
import ask from '@/assets/img/culture/ask.png'
type ExchangeContentProps = {
item: ShopItemDto
......@@ -25,11 +24,11 @@ export default function ExchangeContent(
<div class="relative">
<div class="w-32 h-32 bg-gradient-to-br from-orange-100 to-pink-100 rounded-3xl flex items-center justify-center shadow-lg">
<div class="w-20 h-20 bg-white rounded-lg flex items-center justify-center shadow-sm">
<img src={ask} alt={item.name} class="w-16 h-16 object-contain" />
<img src={item.imageUrl} alt={item.name} class="w-16 h-16 object-contain" />
</div>
</div>
<div class="absolute -top-2 -right-2 w-7 h-7 bg-blue-500 rounded-full flex items-center justify-center shadow-md">
<span class="text-white text-sm font-medium">6</span>
<span class="text-white text-sm font-medium">{item.stock}</span>
</div>
</div>
</div>
......@@ -74,6 +73,7 @@ export default function ExchangeContent(
<label class="text-gray-700 text-sm font-medium mb-2 block">选择数量</label>
<el-input-number
min={1}
max={item.stock}
modelValue={modelValue.num}
onUpdate:modelValue={(value: number) =>
context.emit('update:modelValue', { ...modelValue, num: value })
......
......@@ -42,13 +42,13 @@
</template>
</el-table-column>
<el-table-column prop="price" label="商品单价" width="100" align="center" />
<!-- <el-table-column prop="price" label="商品单价" width="100" align="center" /> -->
<el-table-column prop="num" label="兑换数量" width="100" align="center" />
<el-table-column label="扣除YA币" width="110" align="center">
<template #default="scope">
<span class="text-red-500 font-semibold"> -{{ scope.row.price * scope.row.num }} </span>
<span class="text-red-500 font-semibold"> -{{ scope.row.price }} </span>
</template>
</el-table-column>
......
......@@ -45,9 +45,9 @@
>
<div class="w-24 h-24 mb-3 flex items-center justify-center">
<img
src="@/assets/img/culture/ask.png"
:src="item.imageUrl"
alt=""
class="w-full h-full object-contain group-hover:scale-110 transition-transform duration-300"
class="rounded-lg w-full h-full object-contain group-hover:scale-110 transition-transform duration-300"
/>
</div>
<div
......@@ -116,9 +116,9 @@
>
<div class="w-24 h-24 mb-3 flex items-center justify-center">
<img
src="@/assets/img/culture/ask.png"
:src="item.imageUrl"
alt=""
class="w-full h-full object-contain group-hover:scale-110 transition-transform duration-300"
class="rounded-lg w-full h-full object-contain group-hover:scale-110 transition-transform duration-300"
/>
</div>
<div
......
<template>
<div
class="fixed right-50 top-70% -translate-y-50% flex flex-col items-center content-center gap-5"
class="fixed 2xl:right-50 lg:right-20 right-10 top-70% -translate-y-50% flex flex-col items-center content-center gap-5"
>
<div class="flex flex-col bg-white rounded-8px shadow-md overflow-hidden">
<div
......
......@@ -57,9 +57,13 @@
<div class="flex gap-4">
<el-button @click="handleCancel"> 取消 </el-button>
<!-- <el-button @click="handlePreview"> 预览 </el-button> -->
<el-button type="info" plain @click="handleSubmit"> 存草稿 </el-button>
<el-button type="info" plain @click="handleSubmit(ReleaseStatusTypeEnum.DRAFT)">
存草稿
</el-button>
</div>
<el-button type="primary" @click="handleSubmit"> 提交 </el-button>
<el-button type="primary" @click="handleSubmit(ReleaseStatusTypeEnum.PUBLISH)">
提交
</el-button>
</div>
</el-form>
</div>
......@@ -80,11 +84,6 @@ const router = useRouter()
const formRef = useTemplateRef<FormInstance>('formRef')
type FormData = Omit<AddOrUpdateCaseDto, 'tagRelationDtoList'> & {
mainTagId: string
subTagIds: number[]
}
const rules = {
title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
content: [{ required: true, message: '请输入内容', trigger: 'blur' }],
......@@ -98,7 +97,7 @@ const [form, resetForm] = useResetData({
mainTagId: '',
subTagIds: [],
isSync: BooleanFlag.NO,
releaseStatus: ReleaseStatusTypeEnum.DRAFT,
releaseStatus: ReleaseStatusTypeEnum.PUBLISH,
})
// 取消
......@@ -115,10 +114,11 @@ const handleCancel = () => {
// 保存草稿
const transformData = (formData: FormData): AddOrUpdateCaseDto => {
const { mainTagId, subTagIds, ...rest } = formData
const transformData = (releaseStatus: ReleaseStatusTypeEnum): AddOrUpdateCaseDto => {
const { mainTagId, subTagIds, ...rest } = form.value
const obj: AddOrUpdateCaseDto = {
...rest,
releaseStatus,
tagRelationDtoList: [],
}
// 添加标签内容
......@@ -138,9 +138,9 @@ const transformData = (formData: FormData): AddOrUpdateCaseDto => {
}
// 提交
const handleSubmit = async () => {
const handleSubmit = async (releaseStatus: ReleaseStatusTypeEnum) => {
await formRef.value?.validate()
const res = await addOrUpdateCase(transformData(form.value))
const res = await addOrUpdateCase({ ...transformData(releaseStatus) })
if (res) {
ElMessage.success('提交成功')
resetForm()
......
......@@ -16,7 +16,6 @@
<el-icon class="text-2xl text-gray-300"><Document /></el-icon>
</div>
<div class="text-gray-500 text-lg mb-2">暂无内容</div>
<div class="text-gray-400 text-sm">{{ getEmptyText() }}</div>
</div>
<div v-else class="space-y-4">
......@@ -42,7 +41,7 @@
<!-- Meta Info -->
<div class="flex items-center text-gray-400 text-sm ml-4">
<el-button type="primary" link>编辑</el-button>
<el-button type="danger" link>删除</el-button>
<el-button type="danger" link @click="handleDelete(item.id)">删除</el-button>
</div>
</div>
</div>
......@@ -70,8 +69,7 @@
<script lang="ts" setup>
import { Document } from '@element-plus/icons-vue'
import { getSelfCommentList } from '@/api'
import { getSelfCommentList, deleteComment } from '@/api'
import { usePageSearch } from '@/hooks'
import { commentTypeListOptions } from '@/constants/options'
import dayjs from 'dayjs'
......@@ -82,7 +80,6 @@ const toggleTab = (key: number) => {
refresh()
}
// State
const { list, loading, searchParams, total, refresh, goToPage, changePageSize } = usePageSearch(
getSelfCommentList,
{
......@@ -92,22 +89,14 @@ const { list, loading, searchParams, total, refresh, goToPage, changePageSize }
},
)
// Computed
const paginatedList = computed(() => {
const start = (searchParams.value.current - 1) * searchParams.value.size
const end = start + searchParams.value.size
return list.value.slice(start, end)
})
const getEmptyText = () => {
const emptyTexts: Record<string, string> = {
posts: '还没有发布任何帖子',
videos: '还没有上传任何视频',
questions: '还没有提出任何问题',
articles: '还没有发表任何专栏文章',
practice: '还没有分享任何实践经验',
interviews: '还没有参与任何专访',
}
return emptyTexts[searchParams.type] || '暂无数据'
const handleDelete = async (id: number) => {
await ElMessageBox.confirm('确定删除该评论吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
await deleteComment(id)
ElMessage.success('删除成功')
refresh()
}
</script>
......@@ -38,7 +38,7 @@
<div class="flex items-center text-gray-400 text-sm ml-4">
<el-button type="primary" link @click="handleView(item)">查看</el-button>
<el-button type="danger" link>删除</el-button>
<el-button type="danger" link @click="handleDelete(item.id)">删除</el-button>
</div>
</div>
</div>
......@@ -66,7 +66,7 @@
<script lang="ts" setup>
import { Document } from '@element-plus/icons-vue'
import { getSelfPublishList } from '@/api'
import { getSelfPublishList, deleteArticle } from '@/api'
import { usePageSearch } from '@/hooks'
import { articleTypeListOptions } from '@/constants/options'
import dayjs from 'dayjs'
......@@ -96,4 +96,14 @@ const handleView = (item: SelfPublishDetailDto) => {
router.push(`/postDetail/${item.id}`)
}
}
const handleDelete = async (articleId: number) => {
await ElMessageBox.confirm('确定删除该吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
await deleteArticle(articleId)
refresh()
ElMessage.success('删除成功')
}
</script>
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