Commit b034b191 by lijiabin

【需求 17679】 perf: 优化类型

parent e5c7e6a6
...@@ -16,6 +16,7 @@ import type { ...@@ -16,6 +16,7 @@ import type {
SearchMoreColumnItemDto, SearchMoreColumnItemDto,
SearchMoreVideoParams, SearchMoreVideoParams,
SearchMoreVideoItemDto, SearchMoreVideoItemDto,
SecondCommentItemDto,
} from './types' } from './types'
import type { BackendServicePageResult, PageSearchParams } from '@/utils/request/types' import type { BackendServicePageResult, PageSearchParams } from '@/utils/request/types'
...@@ -316,8 +317,8 @@ export const addComplaint = (data: { articleId: number; reason: string }) => { ...@@ -316,8 +317,8 @@ export const addComplaint = (data: { articleId: number; reason: string }) => {
/** /**
* 问吧-获取回答列表的评论(二级评论) * 问吧-获取回答列表的评论(二级评论)
*/ */
export const getSecondCommentList = (data: { pId: number }) => { export const getSecondCommentList = (data: CommentSearchParams) => {
return service.request<boolean>({ return service.request<BackendServicePageResult<SecondCommentItemDto>>({
url: `/api/cultureComment/getQuestionComment`, url: `/api/cultureComment/getQuestionComment`,
method: 'POST', method: 'POST',
data, data,
......
...@@ -132,7 +132,7 @@ export interface ArticleItemDto { ...@@ -132,7 +132,7 @@ export interface ArticleItemDto {
faceUrl: string faceUrl: string
videoUrl: string videoUrl: string
description: string description: string
createUserId: number createUserId: string
createTime: number createTime: number
viewCount: number viewCount: number
playCount: number playCount: number
...@@ -312,7 +312,7 @@ export interface CommentChildrenSearchParams extends PageSearchParams { ...@@ -312,7 +312,7 @@ export interface CommentChildrenSearchParams extends PageSearchParams {
export interface AddCommentDto { export interface AddCommentDto {
articleId: number | string articleId: number | string
content: string content: string
pId?: number | string pid?: number | string
} }
/** /**
...@@ -336,7 +336,7 @@ export interface CommentItemDto { ...@@ -336,7 +336,7 @@ export interface CommentItemDto {
regionHide: number regionHide: number
replyUser: string replyUser: string
replyName: string replyName: string
userId: number userId: string
isHaveChildren: BooleanFlag isHaveChildren: BooleanFlag
childrenNum: number childrenNum: number
showChildrenPage: boolean showChildrenPage: boolean
...@@ -345,6 +345,7 @@ export interface CommentItemDto { ...@@ -345,6 +345,7 @@ export interface CommentItemDto {
loadingChildren: boolean loadingChildren: boolean
showComment: boolean showComment: boolean
isExpand: boolean isExpand: boolean
childNum: number
} }
/** /**
...@@ -412,3 +413,8 @@ export interface SearchMoreVideoItemDto { ...@@ -412,3 +413,8 @@ export interface SearchMoreVideoItemDto {
videoDuration: string videoDuration: string
viewCount: number viewCount: number
} }
/**
* 获取问吧二级评论的返回参数
*/
export type SecondCommentItemDto = CommentItemDto
...@@ -26,7 +26,7 @@ export interface LoginResponseDto { ...@@ -26,7 +26,7 @@ export interface LoginResponseDto {
credentialsNonExpired: boolean credentialsNonExpired: boolean
accountNonLocked: boolean accountNonLocked: boolean
token: string token: string
userId: number userId: string
hiddenAvatar: string hiddenAvatar: string
hiddenName: string hiddenName: string
signature: string signature: string
......
<template> <template>
<el-dropdown @command="handleMore" trigger="click"> <el-dropdown @command="handleMore" trigger="click">
<!-- <el-button class="p-2 rounded-md cursor-pointer border-none!"> -->
<el-icon class="cursor-pointer"><More /></el-icon> <el-icon class="cursor-pointer"><More /></el-icon>
<!-- </el-button> -->
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item command="举报">举报</el-dropdown-item> <el-dropdown-item command="举报">举报</el-dropdown-item>
...@@ -15,10 +13,6 @@ ...@@ -15,10 +13,6 @@
import { addComplaint } from '@/api' import { addComplaint } from '@/api'
import type { ArticleItemDto } from '@/api/article/types' import type { ArticleItemDto } from '@/api/article/types'
// defineOptions({
// inheritAttrs: false,
// })
const { articleDetail } = defineProps<{ const { articleDetail } = defineProps<{
articleDetail: ArticleItemDto articleDetail: ArticleItemDto
}>() }>()
......
...@@ -348,7 +348,6 @@ const { ...@@ -348,7 +348,6 @@ const {
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'commentSuccess'): void (e: 'commentSuccess'): void
}>() }>()
const router = useRouter()
const total = defineModel<number>('total', { required: true, default: 0 }) const total = defineModel<number>('total', { required: true, default: 0 })
......
...@@ -67,7 +67,6 @@ ...@@ -67,7 +67,6 @@
<img <img
:src="item.avatar" :src="item.avatar"
class="w-10 h-10 rounded-full object-cover cursor-pointer hover:opacity-80 transition-opacity flex-shrink-0" class="w-10 h-10 rounded-full object-cover cursor-pointer hover:opacity-80 transition-opacity flex-shrink-0"
@click="handleUserInfo(item)"
/> />
<div class="flex-1 border-b border-gray-100"> <div class="flex-1 border-b border-gray-100">
...@@ -197,8 +196,7 @@ ...@@ -197,8 +196,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, computed, nextTick } from 'vue' import { ArrowLeft, CaretRight, Position } from '@element-plus/icons-vue'
import { ArrowLeft, Star, StarFilled, CaretRight, Position } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useUserStore } from '@/stores' import { useUserStore } from '@/stores'
...@@ -251,7 +249,7 @@ const { list, total, search, searchParams, goToPage, changePageSize, refresh, lo ...@@ -251,7 +249,7 @@ const { list, total, search, searchParams, goToPage, changePageSize, refresh, lo
immediate: false, immediate: false,
}) })
const open = async (item: CommentItemDto) => { const open = async () => {
// const { data } = await getSecondCommentChildren({ // const { data } = await getSecondCommentChildren({
// pid: item.id, // pid: item.id,
// current: 1, // current: 1,
...@@ -276,10 +274,6 @@ const open = async (item: CommentItemDto) => { ...@@ -276,10 +274,6 @@ const open = async (item: CommentItemDto) => {
visible.value = true visible.value = true
} }
const close = () => {
visible.value = false
}
const formatDate = (time: number) => { const formatDate = (time: number) => {
return dayjs(time * 1000).format('MM-DD HH:mm') return dayjs(time * 1000).format('MM-DD HH:mm')
} }
...@@ -353,11 +347,6 @@ const handleLike = async (item: CommentItemDto) => { ...@@ -353,11 +347,6 @@ const handleLike = async (item: CommentItemDto) => {
} }
} }
const handleUserInfo = (item: CommentItemDto) => {
// 您的跳转逻辑
// router.push(...)
}
defineExpose({ defineExpose({
open, open,
}) })
......
<template>
<button class="btn" v-bind="attrs">测试111</button>
</template>
<script setup lang="ts">
// 二次封装el-button 除去el-button的默认样式 然后保留其他功能
import type { ButtonProps } from 'element-plus'
// const props = defineProps<ButtonProps>()
const attrs = useAttrs()
</script>
<style scoped>
.el-button {
/* 会作用到根元素 */
all: unset !important;
}
</style>
...@@ -46,8 +46,6 @@ const hasReachedLimit = computed(() => fileList.value.length >= props.limit) ...@@ -46,8 +46,6 @@ const hasReachedLimit = computed(() => fileList.value.length >= props.limit)
const showUploadBtn = computed(() => (hasReachedLimit.value ? 'none' : 'flex')) const showUploadBtn = computed(() => (hasReachedLimit.value ? 'none' : 'flex'))
const isInternalUpdate = ref(false) const isInternalUpdate = ref(false)
const uploadProgress = ref(0)
const parseModelValueToUrls = (value: T): string[] => { const parseModelValueToUrls = (value: T): string[] => {
if (!value) return [] if (!value) return []
return Array.isArray(value) ? value.filter(Boolean) : (value as string).split(',').filter(Boolean) return Array.isArray(value) ? value.filter(Boolean) : (value as string).split(',').filter(Boolean)
...@@ -125,15 +123,15 @@ const handleChange: UploadProps['onChange'] = async (uploadFile, uploadFiles) => ...@@ -125,15 +123,15 @@ const handleChange: UploadProps['onChange'] = async (uploadFile, uploadFiles) =>
try { try {
let fileIndex = fileList.value.findIndex((file) => file.uid === uid) let fileIndex = fileList.value.findIndex((file) => file.uid === uid)
if (fileIndex !== -1) { if (fileIndex !== -1) {
fileList.value[fileIndex].status = 'uploading' fileList.value[fileIndex]!.status = 'uploading'
} }
const { data } = await uploadFileApi(uploadFile.raw, (progress) => { const { data } = await uploadFileApi(uploadFile.raw, (progress) => {
console.log('progress', progress) console.log('progress', progress)
}) })
console.log('data', data) console.log('data', data)
const url = data.fileUrl || data.data[0].filePath const url = data.data[0]?.filePath || ''
const name = data.fileName || data.data[0].finalName const name = data.data[0]?.finalName || ''
fileIndex = fileList.value.findIndex((file) => file.uid === uid) fileIndex = fileList.value.findIndex((file) => file.uid === uid)
...@@ -160,7 +158,7 @@ const handleChange: UploadProps['onChange'] = async (uploadFile, uploadFiles) => ...@@ -160,7 +158,7 @@ const handleChange: UploadProps['onChange'] = async (uploadFile, uploadFiles) =>
} }
} }
const handleBeforeRemove: UploadProps['beforeRemove'] = (uploadFile) => { const handleBeforeRemove: UploadProps['beforeRemove'] = () => {
return ElMessageBox.confirm('确定要删除这个文件吗?', '提示', { return ElMessageBox.confirm('确定要删除这个文件吗?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
......
...@@ -183,15 +183,15 @@ const startUpload = async () => { ...@@ -183,15 +183,15 @@ const startUpload = async () => {
// 根据你的 API 返回结构调整 // 根据你的 API 返回结构调整
const videoData: VideoInfo = { const videoData: VideoInfo = {
url: data.data[0].filePath, url: data.data[0]?.filePath || '',
// 暂时写死 // 暂时写死
// url: 'https://soundasia.oss-cn-shenzhen.aliyuncs.com/OA/readName/mp4/2025/11/12/Common/1762918987602.mp4', // url: 'https://soundasia.oss-cn-shenzhen.aliyuncs.com/OA/readName/mp4/2025/11/12/Common/1762918987602.mp4',
name: currentFile.value.name, name: currentFile.value.name,
size: currentFile.value.size, size: currentFile.value.size,
duration: metadata.duration, duration: metadata.duration,
resolution: metadata.resolution, resolution: metadata.resolution,
poster: data.data[0].filePath, poster: data.data[0]?.filePath || '',
fileId: data.data[0].fileId, fileId: data.data[0]?.fileId || '',
} }
videoInfo.value = videoData videoInfo.value = videoData
......
...@@ -28,23 +28,23 @@ const editorRef = shallowRef() ...@@ -28,23 +28,23 @@ const editorRef = shallowRef()
const valueHtml = defineModel<string>() const valueHtml = defineModel<string>()
// 去掉上传视频的功能 // 去掉上传视频的功能
const toolbarConfig = {} const toolbarConfig = {
toolbarConfig.excludeKeys = ['group-video', 'group-more-video', 'group-more-video'] excludeKeys: ['group-video', 'group-more-video', 'group-more-video'],
}
// 去掉上传视频 // 去掉上传视频
const editorConfig = { const editorConfig = {
placeholder: '请输入内容...', placeholder: '请输入内容...',
MENU_CONF: {}, MENU_CONF: {
} uploadImage: {
customUpload: async (file: File, insertFn: (url: string) => void) => {
// 修改 uploadImage 菜单配置 const { data } = await uploadFile(file)
console.log(data)
editorConfig.MENU_CONF['uploadImage'] = { insertFn(data.data[0]?.filePath || '')
customUpload: async (file, insertFn) => { },
const { data } = await uploadFile(file) },
console.log(data)
insertFn(data.data[0].filePath)
}, },
} }
// 组件销毁时,也及时销毁编辑器 // 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => { onBeforeUnmount(() => {
const editor = editorRef.value const editor = editorRef.value
...@@ -52,7 +52,7 @@ onBeforeUnmount(() => { ...@@ -52,7 +52,7 @@ onBeforeUnmount(() => {
editor.destroy() editor.destroy()
}) })
const handleCreated = (editor) => { const handleCreated = (editor: typeof Editor) => {
editorRef.value = editor // 记录 editor 实例,重要! editorRef.value = editor // 记录 editor 实例,重要!
} }
</script> </script>
......
<template>
<md-editor
v-model="value"
preview-theme="arknights"
:class="!preview && height === 'auto' ? 'md_fixed' : ''"
v-bind="isPreview"
:style="{ height }"
@on-upload-img="handleUpload"
></md-editor>
<!-- 图片预览插件 -->
<ElImageViewer v-if="previewShow" :url-list="previewList" :initial-index="previewIndex" @close="closePreview"></ElImageViewer>
</template>
<script setup lang="ts">
import { uploadImage } from '@/api'
import MdEditor from 'md-editor-v3'
import 'md-editor-v3/lib/style.css'
interface IProp {
modelValue?: string
preview?: boolean
height?: string
}
const prop = withDefaults(defineProps<IProp>(), {
modelValue: '',
preview: false,
height: 'auto'
})
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void
}>()
const value = computed({
get() {
return prop.modelValue || ''
},
set(val: string) {
emit('update:modelValue', val)
}
})
// 预览
const isPreview = computed(() => {
if (!prop.preview) return {}
return {
preview: true,
htmlPreview: true,
previewOnly: true
}
})
/**
* 文件上传
*/
type InsertFnType = (urls: string[]) => void
const handleUpload = async (files: File[], callback: InsertFnType) => {
const res = await uploadImage(files)
const url = res.data.originList
// 最后插入图片
callback(url)
}
/**
* 图片预览
*/
const previewShow = ref(false)
const previewList = ref<string[]>([])
const previewIndex = ref(0)
const req = /<img[^>]+src=['"]([^'"]+)['"]+/g
watch(
() => prop.modelValue,
val => {
let temp: RegExpExecArray | null
while ((temp = req.exec(val)) != null) {
previewList.value.push(temp[1])
}
}
)
MdEditor.config({
markedRenderer(renderer) {
renderer.image = (href, title = '', desc = '') => {
if (!href) return ''
let previewHref = href
if (href.indexOf('-small') > -1) {
previewHref = href.replace('-small', '')
} else if (href.indexOf('-mid') > -1) {
previewHref = href.replace('-mid', '')
}
previewList.value.push(previewHref)
return `<img src="${href}" title="${title}" alt="${desc}" style="cursor: zoom-in" >`
}
return renderer
}
})
onMounted(() => {
nextTick(() => {
document.querySelector('.md-preview')?.addEventListener('click', e => {
const el = e.target as HTMLImageElement
if (el?.nodeName == 'IMG') {
let imgSrc = el.src
if (imgSrc.indexOf('-small') > -1) {
imgSrc = imgSrc.replace('-small', '')
} else if (imgSrc.indexOf('-mid') > -1) {
imgSrc = imgSrc.replace('-mid', '')
}
const index = previewList.value.findIndex(v => v === imgSrc)
if (index >= 0) {
previewIndex.value = index
} else {
previewList.value.push(imgSrc)
previewIndex.value = previewList.value.length - 1
}
showPreview()
}
})
})
})
const showPreview = () => {
// PC端预览
previewShow.value = true
}
const closePreview = () => {
previewShow.value = false
}
</script>
<style lang="scss" scoped>
.md_fixed {
overflow: visible;
min-height: 400px;
}
</style>
<style lang="scss">
.md {
font-family: 'Segoe UI Emoji', 'Apple Color Emoji', -apple-system, BlinkMacSystemFont, Segoe UI Variable, Segoe UI, system-ui,
ui-sans-serif, Helvetica, Arial, sans-serif;
}
.md_fixed {
.md-toolbar-wrapper {
position: sticky;
top: 0;
z-index: 10;
background-color: #fff;
box-shadow: 0 6px 6px -6px rgba(0, 0, 0, 0.1);
overflow: visible;
height: auto;
}
.md-toolbar-wrapper .md-toolbar {
position: relative;
min-width: 0;
flex-wrap: wrap;
}
}
.md_fixed .md-toolbar-wrapper .md-toolbar-left,
.md-toolbar-wrapper .md-toolbar-right {
flex-wrap: wrap;
}
.md-preview {
line-height: 1.5;
img {
max-width: 100%;
}
}
</style>
import type { ArticleType, BooleanFlag } from '@/constants' import { ArticleTypeEnum, BooleanFlag } from '@/constants'
import { useUserStore } from '@/stores' import { useUserStore } from '@/stores'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
/** /**
...@@ -64,7 +64,7 @@ export function isCulturePath() { ...@@ -64,7 +64,7 @@ export function isCulturePath() {
} }
// 点击头像跳转用户首页 // 点击头像跳转用户首页
export function jumpToUserHomePage({ userId, isReal }: { userId: number; isReal: BooleanFlag }) { export function jumpToUserHomePage({ userId, isReal }: { userId: string; isReal: BooleanFlag }) {
const userStore = useUserStore() const userStore = useUserStore()
const { userInfo } = storeToRefs(userStore) const { userInfo } = storeToRefs(userStore)
const isSelf = userInfo.value.userId === userId const isSelf = userInfo.value.userId === userId
...@@ -76,10 +76,10 @@ export function jumpToUserHomePage({ userId, isReal }: { userId: number; isReal: ...@@ -76,10 +76,10 @@ export function jumpToUserHomePage({ userId, isReal }: { userId: number; isReal:
} }
// 根据文章类型跳到对应的文章详情页面 // 根据文章类型跳到对应的文章详情页面
export function jumpToArticleDetailPage({ type, id }: { type: ArticleType; id: number }) { export function jumpToArticleDetailPage({ type, id }: { type: ArticleTypeEnum; id: number }) {
if (type === 'video') { if (type === ArticleTypeEnum.VIDEO) {
window.open(`/videoDetail/${id}`) window.open(`/videoDetail/${id}`)
} else if (type === 'question') { } else if (type === ArticleTypeEnum.QUESTION) {
window.open(`/questionDetail/${id}`) window.open(`/questionDetail/${id}`)
} else { } else {
window.open(`/articleDetail/${id}`) window.open(`/articleDetail/${id}`)
......
...@@ -480,7 +480,7 @@ onActivated(async () => { ...@@ -480,7 +480,7 @@ onActivated(async () => {
} }
// 回显主副标签 // 回显主副标签
form.value.mainTagId = String(tagIdList[0]) || '' form.value.mainTagId = tagIdList[0] ? String(tagIdList[0]) : ''
form.value.tagList = tagIdList.slice(1) || [] form.value.tagList = tagIdList.slice(1) || []
const { imgUrl, faceUrl } = data const { imgUrl, faceUrl } = data
......
...@@ -28,15 +28,9 @@ ...@@ -28,15 +28,9 @@
@click="handleBackUser" @click="handleBackUser"
>返回个人账号</el-button >返回个人账号</el-button
> >
<!-- 暂时不加权限 --> <!-- v-if="userInfo.isAdmin || userInfo.isOfficialAccount" -->
<el-button
v-if="userInfo.isAdmin || userInfo.isOfficialAccount" <el-button type="primary" plain size="small" @click="handleAdmin">后台管理</el-button>
type="primary"
plain
size="small"
@click="handleAdmin"
>后台管理</el-button
>
</div> </div>
</div> </div>
......
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