mirror of
https://gitee.com/xiaonuobase/snowy.git
synced 2026-10-06 06:52:58 +08:00
【底座】用户头像、通知公告封面等上传限定只能图片,DevFileController增加限定图片、文档等4个文件上传接口,优化通知公告管理页面、业务首页中通知公告详情的展示样式
This commit is contained in:
@@ -1,56 +1,71 @@
|
||||
<template>
|
||||
<xn-form-container title="详情" :width="1000" v-model:open="open" :destroy-on-close="true" @close="onClose">
|
||||
<a-descriptions bordered>
|
||||
<a-descriptions-item label="标题">{{ formData.title }}</a-descriptions-item>
|
||||
<a-descriptions-item label="类型">
|
||||
<a-tag :bordered="false" :color="$TOOL.dictTypeColor('BIZ_NOTICE_TYPE', formData.type)">
|
||||
{{ $TOOL.dictTypeData('BIZ_NOTICE_TYPE', formData.type) }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="封面图">
|
||||
<div v-if="formData.image">
|
||||
<a-image :src="formData.image" style="width: 100px; height: 50px; margin-bottom: -10px; margin-top: -10px" />
|
||||
<xn-form-container title="通知公告详情" :width="900" v-model:open="open" :destroy-on-close="true" @close="onClose">
|
||||
<div class="notice-detail-container">
|
||||
<!-- 文章头部 -->
|
||||
<div class="notice-header">
|
||||
<h1 class="notice-title">{{ formData.title }}</h1>
|
||||
<div class="notice-meta">
|
||||
<span class="meta-item">
|
||||
<a-tag :bordered="false" :color="$TOOL.dictTypeColor('BIZ_NOTICE_TYPE', formData.type)">
|
||||
{{ $TOOL.dictTypeData('BIZ_NOTICE_TYPE', formData.type) }}
|
||||
</a-tag>
|
||||
</span>
|
||||
<span class="meta-item" v-if="formData.createUserName">
|
||||
<UserOutlined />
|
||||
{{ formData.createUserName }}
|
||||
</span>
|
||||
<span class="meta-item" v-if="formData.createTime">
|
||||
<ClockCircleOutlined />
|
||||
{{ $TOOL.parseTime(formData.createTime, '{y}-{m}-{d} {h}:{i}') }}
|
||||
</span>
|
||||
</div>
|
||||
<span v-else>未上传</span>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="内容"><div v-html="formData.content"></div></a-descriptions-item>
|
||||
</a-descriptions>
|
||||
<a-descriptions bordered :column="2" class="mt-2">
|
||||
<a-descriptions-item label="摘要">{{ formData.digest }}</a-descriptions-item>
|
||||
<a-descriptions-item label="备注"
|
||||
><span>{{ formData.remark }}</span></a-descriptions-item
|
||||
>
|
||||
<a-descriptions-item label="排序"
|
||||
><span>{{ formData.sortCode }}</span></a-descriptions-item
|
||||
>
|
||||
<a-descriptions-item label="发布位置">
|
||||
<div v-if="formData.place">
|
||||
<a-tag v-for="textValue in JSON.parse(formData.place)" :key="textValue" :color="$TOOL.dictTypeColor('BIZ_NOTICE_PLACE', textValue)">
|
||||
{{ $TOOL.dictTypeData('BIZ_NOTICE_PLACE', textValue) }}
|
||||
</a-tag>
|
||||
</div>
|
||||
|
||||
<!-- 摘要 -->
|
||||
<div v-if="formData.digest" class="notice-digest">
|
||||
{{ formData.digest }}
|
||||
</div>
|
||||
|
||||
<!-- 封面图 -->
|
||||
<div v-if="formData.image" class="notice-cover">
|
||||
<a-image :src="formData.image" :preview="true" />
|
||||
</div>
|
||||
|
||||
<!-- 正文内容 -->
|
||||
<div class="notice-content" v-html="formData.content"></div>
|
||||
|
||||
<!-- 附加信息 -->
|
||||
<div class="notice-footer">
|
||||
<a-divider />
|
||||
<div class="footer-info">
|
||||
<div v-if="formData.place" class="info-row">
|
||||
<span class="info-label">发布位置:</span>
|
||||
<a-tag
|
||||
v-for="textValue in JSON.parse(formData.place)"
|
||||
:key="textValue"
|
||||
:color="$TOOL.dictTypeColor('BIZ_NOTICE_PLACE', textValue)"
|
||||
>
|
||||
{{ $TOOL.dictTypeData('BIZ_NOTICE_PLACE', textValue) }}
|
||||
</a-tag>
|
||||
</div>
|
||||
<div v-if="formData.remark" class="info-row">
|
||||
<span class="info-label">备注:</span>
|
||||
<span>{{ formData.remark }}</span>
|
||||
</div>
|
||||
<div v-if="formData.updateUserName && formData.updateTime" class="info-row meta-secondary">
|
||||
<span>最后更新:{{ formData.updateUserName }} · {{ $TOOL.parseTime(formData.updateTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="创建人"
|
||||
><span>{{ formData.createUserName }}</span></a-descriptions-item
|
||||
>
|
||||
<a-descriptions-item label="创建时间"
|
||||
><span>{{ formData.createTime }}</span></a-descriptions-item
|
||||
>
|
||||
<a-descriptions-item label="修改人"
|
||||
><span>{{ formData.updateUserName }}</span></a-descriptions-item
|
||||
>
|
||||
<a-descriptions-item label="修改时间"
|
||||
><span>{{ formData.updateTime }}</span></a-descriptions-item
|
||||
>
|
||||
</a-descriptions>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
|
||||
<a-button type="primary" @click="onClose" :loading="submitLoading">确定</a-button>
|
||||
<a-button type="primary" @click="onClose">关闭</a-button>
|
||||
</template>
|
||||
</xn-form-container>
|
||||
</template>
|
||||
|
||||
<script setup name="bizNoticeDetail">
|
||||
import { UserOutlined, ClockCircleOutlined } from '@ant-design/icons-vue'
|
||||
import bizIndexApi from '@/api/biz/bizIndexApi'
|
||||
import { message } from 'ant-design-vue'
|
||||
// 抽屉状态
|
||||
@@ -58,7 +73,6 @@
|
||||
const emit = defineEmits({ successful: null })
|
||||
// 表单数据
|
||||
const formData = ref({})
|
||||
const submitLoading = ref(false)
|
||||
// 打开抽屉
|
||||
const onOpen = (id) => {
|
||||
open.value = true
|
||||
@@ -83,3 +97,150 @@
|
||||
onOpen
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.notice-detail-container {
|
||||
max-width: 100%;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.notice-header {
|
||||
text-align: center;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.notice-title {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
margin: 0 0 16px 0;
|
||||
color: rgba(0, 0, 0, 0.88);
|
||||
}
|
||||
|
||||
.notice-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 24px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.meta-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.notice-digest {
|
||||
background: #f5f5f5;
|
||||
padding: 16px 20px;
|
||||
border-left: 4px solid #1890ff;
|
||||
margin-bottom: 24px;
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
|
||||
.notice-cover {
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
|
||||
:deep(.ant-image) {
|
||||
max-width: 100%;
|
||||
max-height: 400px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(img) {
|
||||
max-width: 100%;
|
||||
max-height: 400px;
|
||||
height: auto;
|
||||
border-radius: 4px;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.notice-content {
|
||||
font-size: 16px;
|
||||
line-height: 1.8;
|
||||
color: rgba(0, 0, 0, 0.88);
|
||||
margin-bottom: 32px;
|
||||
|
||||
:deep(p) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
:deep(img) {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin: 16px auto;
|
||||
}
|
||||
|
||||
:deep(h1),
|
||||
:deep(h2),
|
||||
:deep(h3),
|
||||
:deep(h4),
|
||||
:deep(h5),
|
||||
:deep(h6) {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(ul),
|
||||
:deep(ol) {
|
||||
padding-left: 28px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
:deep(blockquote) {
|
||||
border-left: 4px solid #e8e8e8;
|
||||
padding-left: 16px;
|
||||
margin: 16px 0;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
|
||||
:deep(code) {
|
||||
background: #f5f5f5;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
:deep(pre) {
|
||||
background: #f5f5f5;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.notice-footer {
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.footer-info {
|
||||
.info-row {
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
|
||||
&.meta-secondary {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,60 +1,71 @@
|
||||
<template>
|
||||
<xn-form-container title="详情" :width="1000" v-model:open="open" :destroy-on-close="true" @close="onClose">
|
||||
<a-descriptions bordered>
|
||||
<a-descriptions-item label="标题">{{ formData.title }}</a-descriptions-item>
|
||||
<a-descriptions-item label="类型">
|
||||
<a-tag :bordered="false" :color="$TOOL.dictTypeColor('BIZ_NOTICE_TYPE', formData.type)">
|
||||
{{ $TOOL.dictTypeData('BIZ_NOTICE_TYPE', formData.type) }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="封面图">
|
||||
<div v-if="formData.image">
|
||||
<a-image :src="formData.image" style="width: 100px; height: 50px; margin-bottom: -10px; margin-top: -10px" />
|
||||
<xn-form-container title="通知公告详情" :width="900" v-model:open="open" :destroy-on-close="true" @close="onClose">
|
||||
<div class="notice-detail-container">
|
||||
<!-- 文章头部 -->
|
||||
<div class="notice-header">
|
||||
<h1 class="notice-title">{{ formData.title }}</h1>
|
||||
<div class="notice-meta">
|
||||
<span class="meta-item">
|
||||
<a-tag :bordered="false" :color="$TOOL.dictTypeColor('BIZ_NOTICE_TYPE', formData.type)">
|
||||
{{ $TOOL.dictTypeData('BIZ_NOTICE_TYPE', formData.type) }}
|
||||
</a-tag>
|
||||
</span>
|
||||
<span class="meta-item" v-if="formData.createUserName">
|
||||
<UserOutlined />
|
||||
{{ formData.createUserName }}
|
||||
</span>
|
||||
<span class="meta-item" v-if="formData.createTime">
|
||||
<ClockCircleOutlined />
|
||||
{{ $TOOL.parseTime(formData.createTime, '{y}-{m}-{d} {h}:{i}') }}
|
||||
</span>
|
||||
</div>
|
||||
<span v-else>未上传</span>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="内容"><div v-html="formData.content"></div></a-descriptions-item>
|
||||
</a-descriptions>
|
||||
<a-descriptions bordered :column="1" class="mt-2">
|
||||
<a-descriptions-item label="摘要">{{ formData.digest }}</a-descriptions-item>
|
||||
<a-descriptions-item label="备注"
|
||||
><span>{{ formData.remark }}</span></a-descriptions-item
|
||||
>
|
||||
<a-descriptions-item label="排序"
|
||||
><span>{{ formData.sortCode }}</span></a-descriptions-item
|
||||
>
|
||||
<a-descriptions-item label="发布位置">
|
||||
<div v-if="formData.place">
|
||||
<a-tag
|
||||
v-for="textValue in JSON.parse(formData.place)"
|
||||
:key="textValue"
|
||||
:color="$TOOL.dictTypeColor('BIZ_NOTICE_PLACE', textValue)"
|
||||
>
|
||||
{{ $TOOL.dictTypeData('BIZ_NOTICE_PLACE', textValue) }}
|
||||
</a-tag>
|
||||
</div>
|
||||
|
||||
<!-- 摘要 -->
|
||||
<div v-if="formData.digest" class="notice-digest">
|
||||
{{ formData.digest }}
|
||||
</div>
|
||||
|
||||
<!-- 封面图 -->
|
||||
<div v-if="formData.image" class="notice-cover">
|
||||
<a-image :src="formData.image" :preview="true" />
|
||||
</div>
|
||||
|
||||
<!-- 正文内容 -->
|
||||
<div class="notice-content" v-html="formData.content"></div>
|
||||
|
||||
<!-- 附加信息 -->
|
||||
<div class="notice-footer">
|
||||
<a-divider />
|
||||
<div class="footer-info">
|
||||
<div v-if="formData.place" class="info-row">
|
||||
<span class="info-label">发布位置:</span>
|
||||
<a-tag
|
||||
v-for="textValue in JSON.parse(formData.place)"
|
||||
:key="textValue"
|
||||
:color="$TOOL.dictTypeColor('BIZ_NOTICE_PLACE', textValue)"
|
||||
>
|
||||
{{ $TOOL.dictTypeData('BIZ_NOTICE_PLACE', textValue) }}
|
||||
</a-tag>
|
||||
</div>
|
||||
<div v-if="formData.remark" class="info-row">
|
||||
<span class="info-label">备注:</span>
|
||||
<span>{{ formData.remark }}</span>
|
||||
</div>
|
||||
<div v-if="formData.updateUserName && formData.updateTime" class="info-row meta-secondary">
|
||||
<span>最后更新:{{ formData.updateUserName }} · {{ $TOOL.parseTime(formData.updateTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="创建人"
|
||||
><span>{{ formData.createUserName }}</span></a-descriptions-item
|
||||
>
|
||||
<a-descriptions-item label="创建时间"
|
||||
><span>{{ formData.createTime }}</span></a-descriptions-item
|
||||
>
|
||||
<a-descriptions-item label="修改人"
|
||||
><span>{{ formData.updateUserName }}</span></a-descriptions-item
|
||||
>
|
||||
<a-descriptions-item label="修改时间"
|
||||
><span>{{ formData.updateTime }}</span></a-descriptions-item
|
||||
>
|
||||
</a-descriptions>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
|
||||
<a-button type="primary" @click="onClose" :loading="submitLoading">确定</a-button>
|
||||
<a-button type="primary" @click="onClose">关闭</a-button>
|
||||
</template>
|
||||
</xn-form-container>
|
||||
</template>
|
||||
|
||||
<script setup name="bizNoticeDetail">
|
||||
import { UserOutlined, ClockCircleOutlined } from '@ant-design/icons-vue'
|
||||
import bizNoticeApi from '@/api/biz/bizNoticeApi'
|
||||
import { message } from 'ant-design-vue'
|
||||
// 抽屉状态
|
||||
@@ -62,7 +73,6 @@
|
||||
const emit = defineEmits({ successful: null })
|
||||
// 表单数据
|
||||
const formData = ref({})
|
||||
const submitLoading = ref(false)
|
||||
// 打开抽屉
|
||||
const onOpen = (id) => {
|
||||
open.value = true
|
||||
@@ -87,3 +97,150 @@
|
||||
onOpen
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.notice-detail-container {
|
||||
max-width: 100%;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.notice-header {
|
||||
text-align: center;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.notice-title {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
margin: 0 0 16px 0;
|
||||
color: rgba(0, 0, 0, 0.88);
|
||||
}
|
||||
|
||||
.notice-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 24px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.meta-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.notice-digest {
|
||||
background: #f5f5f5;
|
||||
padding: 16px 20px;
|
||||
border-left: 4px solid #1890ff;
|
||||
margin-bottom: 24px;
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
|
||||
.notice-cover {
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
|
||||
:deep(.ant-image) {
|
||||
max-width: 100%;
|
||||
max-height: 400px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(img) {
|
||||
max-width: 100%;
|
||||
max-height: 400px;
|
||||
height: auto;
|
||||
border-radius: 4px;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.notice-content {
|
||||
font-size: 16px;
|
||||
line-height: 1.8;
|
||||
color: rgba(0, 0, 0, 0.88);
|
||||
margin-bottom: 32px;
|
||||
|
||||
:deep(p) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
:deep(img) {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin: 16px auto;
|
||||
}
|
||||
|
||||
:deep(h1),
|
||||
:deep(h2),
|
||||
:deep(h3),
|
||||
:deep(h4),
|
||||
:deep(h5),
|
||||
:deep(h6) {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(ul),
|
||||
:deep(ol) {
|
||||
padding-left: 28px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
:deep(blockquote) {
|
||||
border-left: 4px solid #e8e8e8;
|
||||
padding-left: 16px;
|
||||
margin: 16px 0;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
|
||||
:deep(code) {
|
||||
background: #f5f5f5;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
:deep(pre) {
|
||||
background: #f5f5f5;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.notice-footer {
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.footer-info {
|
||||
.info-row {
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
|
||||
&.meta-secondary {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -25,7 +25,11 @@
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||
<a-form-item label="封面图:" name="image">
|
||||
<xn-upload v-model:value="formData.image" uploadMode="image" />
|
||||
<xn-upload
|
||||
v-model:value="formData.image"
|
||||
uploadMode="image"
|
||||
uploadDynamicReturnUrlApi="/dev/file/uploadImageDynamicReturnUrl"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||
|
||||
@@ -18,7 +18,11 @@
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :md="24" :lg="6" :xl="6">
|
||||
<a-form-item label="图片:" name="image">
|
||||
<xn-upload v-model:value="formData.image" uploadMode="image" />
|
||||
<xn-upload
|
||||
v-model:value="formData.image"
|
||||
uploadMode="image"
|
||||
uploadDynamicReturnUrlApi="/dev/file/uploadImageDynamicReturnUrl"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||
|
||||
+1
-2
@@ -24,7 +24,6 @@ import vip.xiaonuo.biz.modular.index.param.BizIndexNoticeIdParam;
|
||||
import vip.xiaonuo.biz.modular.index.param.BizIndexNoticeListParam;
|
||||
import vip.xiaonuo.biz.modular.index.param.BizIndexSlideshowListParam;
|
||||
import vip.xiaonuo.biz.modular.index.result.BizIndexNoticeListResult;
|
||||
import vip.xiaonuo.biz.modular.index.result.BizIndexSlideshowDetailResult;
|
||||
import vip.xiaonuo.biz.modular.index.result.BizIndexSlideshowListResult;
|
||||
import vip.xiaonuo.biz.modular.index.service.BizIndexService;
|
||||
import vip.xiaonuo.biz.modular.notice.entity.BizNotice;
|
||||
@@ -90,7 +89,7 @@ public class BizIndexController {
|
||||
*/
|
||||
@Operation(summary = "获取通知公告详情")
|
||||
@GetMapping("/biz/index/notice/detail")
|
||||
public CommonResult<BizIndexSlideshowDetailResult> noticeDetailById(@Valid BizIndexNoticeIdParam bizIndexNoticeIdParam) {
|
||||
public CommonResult<BizNotice> noticeDetailById(@Valid BizIndexNoticeIdParam bizIndexNoticeIdParam) {
|
||||
return CommonResult.data(bizIndexService.noticeDetailById(bizIndexNoticeIdParam));
|
||||
}
|
||||
}
|
||||
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.biz.modular.index.result;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 轮播图结果
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2024/07/13 00:31
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class BizIndexSlideshowDetailResult {
|
||||
|
||||
/** 主键 */
|
||||
@TableId
|
||||
@Schema(description = "主键")
|
||||
private String id;
|
||||
|
||||
/** 标题 */
|
||||
@Schema(description = "标题")
|
||||
private String title;
|
||||
|
||||
/** 封面图 */
|
||||
@Schema(description = "封面图")
|
||||
private String image;
|
||||
|
||||
/** 内容 */
|
||||
@Schema(description = "内容")
|
||||
private String content;
|
||||
|
||||
/** 摘要 */
|
||||
@Schema(description = "摘要")
|
||||
private String digest;
|
||||
|
||||
/** 类型 */
|
||||
@Schema(description = "类型")
|
||||
private String type;
|
||||
|
||||
/** 状态 */
|
||||
@Schema(description = "状态")
|
||||
private String status;
|
||||
|
||||
/** 备注 */
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
/** 排序 */
|
||||
@Schema(description = "排序")
|
||||
private Integer sortCode;
|
||||
|
||||
/** 扩展信息 */
|
||||
@Schema(description = "扩展信息")
|
||||
private String extJson;
|
||||
}
|
||||
+1
-2
@@ -18,7 +18,6 @@ import vip.xiaonuo.biz.modular.index.param.BizIndexNoticeIdParam;
|
||||
import vip.xiaonuo.biz.modular.index.param.BizIndexNoticeListParam;
|
||||
import vip.xiaonuo.biz.modular.index.param.BizIndexSlideshowListParam;
|
||||
import vip.xiaonuo.biz.modular.index.result.BizIndexNoticeListResult;
|
||||
import vip.xiaonuo.biz.modular.index.result.BizIndexSlideshowDetailResult;
|
||||
import vip.xiaonuo.biz.modular.index.result.BizIndexSlideshowListResult;
|
||||
import vip.xiaonuo.biz.modular.notice.entity.BizNotice;
|
||||
|
||||
@@ -62,5 +61,5 @@ public interface BizIndexService {
|
||||
* @author yubaoshan
|
||||
* @date 2024/07/13 00:31
|
||||
*/
|
||||
BizIndexSlideshowDetailResult noticeDetailById(BizIndexNoticeIdParam bizIndexNoticeIdParam);
|
||||
BizNotice noticeDetailById(BizIndexNoticeIdParam bizIndexNoticeIdParam);
|
||||
}
|
||||
|
||||
+2
-6
@@ -12,7 +12,6 @@
|
||||
*/
|
||||
package vip.xiaonuo.biz.modular.index.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
@@ -23,7 +22,6 @@ import vip.xiaonuo.biz.modular.index.param.BizIndexNoticeIdParam;
|
||||
import vip.xiaonuo.biz.modular.index.param.BizIndexNoticeListParam;
|
||||
import vip.xiaonuo.biz.modular.index.param.BizIndexSlideshowListParam;
|
||||
import vip.xiaonuo.biz.modular.index.result.BizIndexNoticeListResult;
|
||||
import vip.xiaonuo.biz.modular.index.result.BizIndexSlideshowDetailResult;
|
||||
import vip.xiaonuo.biz.modular.index.result.BizIndexSlideshowListResult;
|
||||
import vip.xiaonuo.biz.modular.index.service.BizIndexService;
|
||||
import vip.xiaonuo.biz.modular.notice.entity.BizNotice;
|
||||
@@ -76,9 +74,7 @@ public class BizIndexServiceImpl implements BizIndexService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BizIndexSlideshowDetailResult noticeDetailById(BizIndexNoticeIdParam bizIndexNoticeIdParam) {
|
||||
BizIndexSlideshowDetailResult result = new BizIndexSlideshowDetailResult();
|
||||
BeanUtil.copyProperties(bizNoticeService.getById(bizIndexNoticeIdParam.getId()), result);
|
||||
return result;
|
||||
public BizNotice noticeDetailById(BizIndexNoticeIdParam bizIndexNoticeIdParam) {
|
||||
return bizNoticeService.getById(bizIndexNoticeIdParam.getId());
|
||||
}
|
||||
}
|
||||
|
||||
+61
@@ -36,6 +36,7 @@ import vip.xiaonuo.dev.modular.file.service.DevFileService;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -84,6 +85,66 @@ public class DevFileController {
|
||||
return CommonResult.data(devFileService.uploadReturnUrl(devConfigApi.getValueByKey(SNOWY_SYS_DEFAULT_FILE_ENGINE_KEY), file));
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态上传图片文件返回id(仅允许图片格式)
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2025/6/26 14:01
|
||||
**/
|
||||
@Operation(summary = "动态上传图片文件返回id")
|
||||
@CommonLog("动态上传图片文件返回id")
|
||||
@PostMapping("/dev/file/uploadImageDynamicReturnId")
|
||||
public CommonResult<String> uploadImageDynamicReturnId(@RequestPart("file") MultipartFile file) {
|
||||
List<String> allowedExt = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "webp");
|
||||
return CommonResult.data(devFileService.uploadReturnIdWithValidation(
|
||||
devConfigApi.getValueByKey(SNOWY_SYS_DEFAULT_FILE_ENGINE_KEY), file, allowedExt));
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态上传图片文件返回url(仅允许图片格式)
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2025/6/26 14:01
|
||||
**/
|
||||
@Operation(summary = "动态上传图片文件返回url")
|
||||
@CommonLog("动态上传图片文件返回url")
|
||||
@PostMapping("/dev/file/uploadImageDynamicReturnUrl")
|
||||
public CommonResult<String> uploadImageDynamicReturnUrl(@RequestPart("file") MultipartFile file) {
|
||||
List<String> allowedExt = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "webp");
|
||||
return CommonResult.data(devFileService.uploadReturnUrlWithValidation(
|
||||
devConfigApi.getValueByKey(SNOWY_SYS_DEFAULT_FILE_ENGINE_KEY), file, allowedExt));
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态上传文档文件返回id(仅允许文档格式)
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2025/6/26 14:01
|
||||
**/
|
||||
@Operation(summary = "动态上传文档文件返回id")
|
||||
@CommonLog("动态上传文档文件返回id")
|
||||
@PostMapping("/dev/file/uploadDocumentDynamicReturnId")
|
||||
public CommonResult<String> uploadDocumentDynamicReturnId(@RequestPart("file") MultipartFile file) {
|
||||
List<String> allowedExt = Arrays.asList("pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt");
|
||||
return CommonResult.data(devFileService.uploadReturnIdWithValidation(
|
||||
devConfigApi.getValueByKey(SNOWY_SYS_DEFAULT_FILE_ENGINE_KEY), file, allowedExt));
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态上传文档文件返回url(仅允许文档格式)
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2025/6/26 14:01
|
||||
**/
|
||||
@Operation(summary = "动态上传文档文件返回url")
|
||||
@CommonLog("动态上传文档文件返回url")
|
||||
@PostMapping("/dev/file/uploadDocumentDynamicReturnUrl")
|
||||
public CommonResult<String> uploadDocumentDynamicReturnUrl(@RequestPart("file") MultipartFile file) {
|
||||
List<String> allowedExt = Arrays.asList("pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt");
|
||||
return CommonResult.data(devFileService.uploadReturnUrlWithValidation(
|
||||
devConfigApi.getValueByKey(SNOWY_SYS_DEFAULT_FILE_ENGINE_KEY), file, allowedExt));
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地文件上传,返回文件id
|
||||
*
|
||||
|
||||
+16
@@ -42,6 +42,14 @@ public interface DevFileService extends IService<DevFile> {
|
||||
**/
|
||||
String uploadReturnId(String engine, MultipartFile file);
|
||||
|
||||
/**
|
||||
* MultipartFile文件上传,返回文件id(带后缀校验)
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2025/6/26 15:53
|
||||
**/
|
||||
String uploadReturnIdWithValidation(String engine, MultipartFile file, List<String> allowedExtensions);
|
||||
|
||||
/**
|
||||
* MultipartFile文件上传,返回文件Url
|
||||
*
|
||||
@@ -50,6 +58,14 @@ public interface DevFileService extends IService<DevFile> {
|
||||
**/
|
||||
String uploadReturnUrl(String engine, MultipartFile file);
|
||||
|
||||
/**
|
||||
* MultipartFile文件上传,返回文件Url(带后缀校验)
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2025/6/26 15:53
|
||||
**/
|
||||
String uploadReturnUrlWithValidation(String engine, MultipartFile file, List<String> allowedExtensions);
|
||||
|
||||
/**
|
||||
* 文件分页列表接口
|
||||
*
|
||||
|
||||
+42
@@ -13,6 +13,7 @@
|
||||
package vip.xiaonuo.dev.modular.file.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollStreamUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.img.ImgUtil;
|
||||
@@ -81,11 +82,23 @@ public class DevFileServiceImpl extends ServiceImpl<DevFileMapper, DevFile> impl
|
||||
return this.storageFile(engine, file, true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadReturnIdWithValidation(String engine, MultipartFile file, List<String> allowedExtensions) {
|
||||
validateFileExtension(file, allowedExtensions);
|
||||
return this.storageFile(engine, file, true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadReturnUrl(String engine, MultipartFile file) {
|
||||
return this.storageFile(engine, file, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadReturnUrlWithValidation(String engine, MultipartFile file, List<String> allowedExtensions) {
|
||||
validateFileExtension(file, allowedExtensions);
|
||||
return this.storageFile(engine, file, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<DevFile> page(DevFilePageParam devFilePageParam) {
|
||||
QueryWrapper<DevFile> queryWrapper = new QueryWrapper<DevFile>().checkSqlInjection();
|
||||
@@ -421,4 +434,33 @@ public class DevFileServiceImpl extends ServiceImpl<DevFileMapper, DevFile> impl
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证文件后缀名
|
||||
*
|
||||
* @param file 上传的文件
|
||||
* @param allowedExtensions 允许的后缀列表(不含点号,如:jpg, png)
|
||||
*/
|
||||
private void validateFileExtension(MultipartFile file, List<String> allowedExtensions) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new CommonException("上传文件不能为空");
|
||||
}
|
||||
|
||||
String originalFileName = file.getOriginalFilename();
|
||||
if (StrUtil.isEmpty(originalFileName)) {
|
||||
throw new CommonException("文件名不能为空");
|
||||
}
|
||||
|
||||
// 获取文件后缀(小写)
|
||||
String fileSuffix = FileUtil.getSuffix(originalFileName).toLowerCase();
|
||||
if (StrUtil.isEmpty(fileSuffix)) {
|
||||
throw new CommonException("文件必须有后缀名");
|
||||
}
|
||||
|
||||
// 校验后缀是否在白名单中
|
||||
if (CollUtil.isEmpty(allowedExtensions) || !allowedExtensions.contains(fileSuffix)) {
|
||||
throw new CommonException("不允许上传该文件类型,仅支持:{}",
|
||||
CollUtil.isEmpty(allowedExtensions) ? "无" : String.join("、", allowedExtensions));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-1
@@ -1111,7 +1111,17 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
public String updateAvatar(MultipartFile file) {
|
||||
SysUser sysUser = this.queryEntity(StpUtil.getLoginIdAsString());
|
||||
try {
|
||||
String suffix = Objects.requireNonNull(FileUtil.getSuffix(file.getOriginalFilename())).toLowerCase();
|
||||
// 验证文件后缀(只允许图片格式)
|
||||
String suffix = FileUtil.getSuffix(file.getOriginalFilename());
|
||||
if (StrUtil.isEmpty(suffix)) {
|
||||
throw new CommonException("文件必须有后缀名");
|
||||
}
|
||||
suffix = suffix.toLowerCase();
|
||||
List<String> allowedExt = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "webp");
|
||||
if (!allowedExt.contains(suffix)) {
|
||||
throw new CommonException("头像仅支持图片格式:jpg、jpeg、png、gif、bmp、webp");
|
||||
}
|
||||
|
||||
BufferedImage image = ImgUtil.toImage(file.getBytes());
|
||||
String base64;
|
||||
if(image.getWidth() <= 200 && image.getHeight() <= 200) {
|
||||
|
||||
Reference in New Issue
Block a user