试一下

This commit is contained in:
dmz 2025-12-05 23:08:56 +08:00
parent 6bc968ca6f
commit 6e02d90999
15 changed files with 1416 additions and 0 deletions

View File

@ -0,0 +1,17 @@
import js from '@eslint/js'
import pluginVue from 'eslint-plugin-vue'
export default [
{
name: 'app/files-to-lint',
files: ['**/*.{js,mjs,jsx,vue}'],
},
{
name: 'app/files-to-ignore',
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'],
},
js.configs.recommended,
...pluginVue.configs['flat/essential'],
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,41 @@
import axios from '@/utils/request'
// 查询轮播图列表
export function listCarousel(query) {
return axios.get('/carousel/list',{
params: {
...query
}
})
}
// 查询全部轮播图列表
export function listAllCarousel(query) {
return axios.get('/carousel/listAll', {
params: {
...query
}
})
}
// 查询轮播图详细
export function getCarousel(id) {
return axios.get('/carousel/info/' + id)
}
// 新增轮播图
export function addCarousel(data) {
return axios.post('/carousel/add', {
...data
})
}
// 修改轮播图
export function updateCarousel(data) {
return axios.put('/carousel', data)
}
// 删除轮播图
export function delCarousel(id) {
return axios.delete('/carousel/' + id)
}

View File

@ -0,0 +1,2 @@
import request from '@/utils/request'

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -0,0 +1,152 @@
<template>
<div>
<QuillEditor ref="myQuillEditor"
theme="snow"
v-model:content="content"
:options="data.editorOption"
contentType="html"
@update:content="setValue()"
/>
<!-- 使用自定义图片上传 -->
<el-upload
action="/api/files/upload"
:data="{filePath: filePath}"
:on-success="handleUploadSuccess"
name="file"
:show-file-list="false"
style="display: none"
ref="uploadRef"
>
</el-upload>
</div>
</template>
<script setup>
import { QuillEditor } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css'
import { reactive, onMounted, ref, toRaw, getCurrentInstance, onUpdated, watch } from 'vue'
import { ElMessage } from 'element-plus'
const { proxy } = getCurrentInstance()
const props = defineProps(['value', 'filePath'])
const emit = defineEmits(['updateValue'])
const content = ref('')
const myQuillEditor = ref()
const filePath = ref('')
const data = reactive({
content: '',
editorOption: {
modules: {
toolbar: [
['bold', 'italic', 'underline', 'strike'],
[{ 'size': ['small', false, 'large', 'huge'] }],
[{ 'font': [] }],
[{ 'align': [] }],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
[{ 'indent': '-1' }, { 'indent': '+1' }],
[{ 'header': 1 }, { 'header': 2 }],
['image'],
[{ 'direction': 'rtl' }],
[{ 'color': [] }, { 'background': [] }]
]
},
placeholder: '请输入内容...'
}
})
const uploadRef = ref('')
const imgHandler = (state) => {
if (state) {
uploadRef.value.$el.querySelector('input').click();
}
}
//
const setValue = () => {
if (myQuillEditor.value) {
try {
const text = toRaw(myQuillEditor.value).getHTML();
emit('updateValue', text);
} catch (e) {
console.warn('获取HTML内容时出错:', e);
}
}
}
function handleUploadSuccess(res, file) {
//
if (res && myQuillEditor.value) {
try {
//
const quill = toRaw(myQuillEditor.value).getQuill();
//
const selection = quill.getSelection();
//
const length = selection ? selection.index : quill.getLength();
// res.url
quill.insertEmbed(length, "image", proxy.getFilePrefix + res);
//
try {
quill.setSelection(Math.min(length + 1, quill.getLength()));
} catch (e) {
//
console.warn('设置光标位置时出错:', e);
}
} catch (e) {
console.error('插入图片时出错:', e);
ElMessage.error("图片插入失败");
}
} else {
ElMessage.error("图片插入失败");
}
}
onUpdated(() => {
//
if (myQuillEditor.value && props.value !== undefined) {
const quill = toRaw(myQuillEditor.value).getQuill();
const currentHtml = toRaw(myQuillEditor.value).getHTML();
//
if ((props.value === '' || props.value === null || props.value === '<p></p>') && currentHtml !== '<p><br></p>') {
quill.setText('');
} else if (props.value && props.value !== currentHtml && props.value !== '<p></p>') {
try {
toRaw(myQuillEditor.value).setHTML(props.value);
} catch (e) {
console.warn('设置HTML内容时出错:', e);
}
}
}
})
//
onMounted(() => {
if (myQuillEditor.value) {
content.value = props.value || '';
const quill = toRaw(myQuillEditor.value).getQuill();
//
try {
quill.getModule('toolbar').addHandler('image', imgHandler);
} catch (e) {
console.warn('添加工具栏处理程序时出错:', e);
}
//
if (props.value && props.value !== '<p></p>') {
try {
toRaw(myQuillEditor.value).setHTML(props.value);
} catch (e) {
console.warn('初始化HTML内容时出错:', e);
}
}
filePath.value = props.filePath;
}
})
</script>
<style scoped>
</style>

View File

@ -0,0 +1,276 @@
<template>
<el-container>
<el-header class="top-header">
<div class="header-left">
<div class="logo">邺城博物馆</div>
</div>
<div class="header-center">
<el-menu
mode="horizontal"
:default-active="$route.path"
class="nav-menu"
:router="true"
background-color="#717171"
>
<template v-for="menu in touristMenuList" :key="menu.route">
<el-menu-item :index="menu.route" :route="menu.route">{{ menu.label }}</el-menu-item>
</template>
</el-menu>
</div>
<div class="header-right">
<div class="user" v-if="isLogged">
<div class="avator">
<el-dropdown trigger="click">
<div class="img">
<img style="width: 100%; height: 100%;" :src="getFilePrefix + avatar" alt="" />
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="handleMyReverse">我的预约</el-dropdown-item>
<el-dropdown-item @click="handleMyCollection">我的收藏</el-dropdown-item>
<el-dropdown-item @click="handleUpdatePwd">修改密码</el-dropdown-item>
<el-dropdown-item @click="handleUserInfo">个人信息</el-dropdown-item>
<el-dropdown-item @click="handleLogout">
<span style="color: brown;">退出登录</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
<div class="login-btn" v-else>
<el-button type="text" @click="goLogin">登录</el-button>
</div>
</div>
</el-header>
<el-main>
<RouterView />
</el-main>
<el-footer class="site-footer">
<div class="footer-text">© {{ year }} 邺城博物馆 欢迎参观</div>
</el-footer>
</el-container>
<el-dialog title="修改密码" v-model="updatePwdOpen" width="400px" append-to-body>
<el-form ref="updatePwdRef" :model="updatePwdForm" :rules="updatePwdRules" label-width="80px">
<el-form-item label="旧密码" prop="oldPwd">
<el-input type="password" v-model="updatePwdForm.oldPwd" placeholder="请输入旧密码" />
</el-form-item>
<el-form-item label="新密码" prop="newPwd">
<el-input type="password" v-model="updatePwdForm.newPwd" placeholder="请输入新密码" />
</el-form-item>
<el-form-item label="确认密码" prop="confirmPwd">
<el-input type="password" v-model="updatePwdForm.confirmPwd" placeholder="请输入确认密码" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitUpdatePwd"> </el-button>
<el-button @click="handleCancelUpdatePwd"> </el-button>
</div>
</template>
</el-dialog>
<!-- </el-container> -->
</template>
<script setup>
import { ref, reactive, toRefs, getCurrentInstance, watch } from 'vue'
import { RouterView } from 'vue-router'
import { removeToken } from '@/utils/token'
import { updatePwd } from '@/api/sysUser';
import Cookies from 'js-cookie';
import { House, Collection, Calendar, Message, Star, User } from '@element-plus/icons-vue'
const avatar = ref(Cookies.get('avatar'))
const { proxy } = getCurrentInstance()
const getFilePrefix = proxy.getFilePrefix
const year = new Date().getFullYear()
const isLogged = ref(!!Cookies.get('role'))
const touristMenuList = ref([
{ label: '首页', route: '/tourist/home', icon: House },
{ label: '藏品', route: '/tourist/relic', icon: Collection },
{ label: '预约', route: '/tourist/reservation', icon: Calendar },
{ label: '公告', route: '/tourist/announcement', icon: Message },
// { label: '', route: '/tourist/userInfo', icon: User }
])
//
const confirmPwdRule = (rule, value, callback) => {
if (!value) { //
callback(new Error('请输入确认密码'))
} else if (updatePwdForm.value.newPwd !== value) { //
callback(new Error('两次输入的密码不一致'))
} else { //
callback()
}
}
const updatePwdOpen = ref(false)
const data = reactive({
updatePwdForm: {
oldPwd: '',
newPwd: '',
confirmPwd: ''
},
updatePwdRules: {
oldPwd: [
{ required: true, message: '请输入旧密码', trigger: 'blur' }
],
newPwd: [
{ required: true, message: '请输入新密码', trigger: 'blur' }
],
confirmPwd: [
{ required: true, validator: confirmPwdRule, trigger: 'blur' }
]
}
})
const { updatePwdForm, updatePwdRules } = toRefs(data)
/** 我的预约操作 */
function handleMyReverse() {
proxy.$router.push('/tourist/myReservation')
}
/** 我的收藏操作 */
function handleMyCollection() {
proxy.$router.push('/tourist/myRelicCollection')
}
/** 修改密码操作 */
function handleUpdatePwd() {
updatePwdOpen.value = true
}
/** 提交修改密码 */
function submitUpdatePwd() {
proxy.$refs.updatePwdRef.validate((valid) => {
if (valid) {
updatePwd(updatePwdForm.value).then(res => {
if (res.code == 200) {
proxy.$message.success(res.msg)
proxy.$refs.updatePwdRef.resetFields()
updatePwdOpen.value = false
} else {
proxy.$message.error(res.msg)
}
})
}
})
}
/** 取消修改密码 */
function handleCancelUpdatePwd() {
proxy.$refs.updatePwdRef.resetFields()
updatePwdOpen.value = false
}
/** 个人信息操作 */
function handleUserInfo() {
proxy.$router.push('/tourist/userInfo')
}
/** 退出登录操作 */
function handleLogout() {
console.log('退出登录')
proxy.$confirm('确定要退出系统吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
console.log('确定退出')
// logout
// token
removeToken()
Cookies.remove('role')
Cookies.remove('avatar')
Cookies.remove('username')
Cookies.remove('nickname')
isLogged.value = false
avatar.value = ''
//
proxy.$router.push('/login')
})
.catch(() => {
console.log('取消退出')
})
}
function goLogin() {
proxy.$router.push('/login')
}
watch(() => proxy.$route.fullPath, () => {
isLogged.value = !!Cookies.get('role')
})
</script>
<style scoped>
.top-header {
height: 60px;
display: flex;
align-items: center;
padding: 0 12px;
background: #1f1f1f;
color: #fff;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
}
.header-left { flex: 0 0 120px; }
.logo { font-size: 18px; font-weight: 700; letter-spacing: 2px; }
.header-center { flex: 1 1 70%; display: flex; justify-content: center; }
.header-right { flex: 0 0 80px; display: flex; align-items: center; justify-content: flex-end; }
.nav-menu { background: transparent; border-bottom: none; }
:deep(.nav-menu .el-menu-item) { color: #eaeaea; }
:deep(.nav-menu .el-menu-item.is-active) { color: #ffd04b; }
:deep(.nav-menu .el-menu-item:hover) { background: transparent; color: #ffffff; }
:deep(.nav-menu) { width: 100%; justify-content: center; }
:deep(.nav-menu .el-menu-item) { padding: 0 14px; min-width: auto; }
:deep(.nav-menu .el-menu-item:last-child) { display: inline-flex !important; }
:deep(.nav-menu .el-menu-item) { width: 150px; }
.top-header .user .img {
width: 40px;
height: 40px;
margin-top: 10px;
background-color: #ffffff;
border: 1px solid #b2b2b2;
box-sizing: border-box;
border-radius: 50%;
overflow: hidden;
transition: all 0.3s;
}
.top-header .user .img:hover {
cursor: pointer;
box-shadow: 0px 0px 20px #ffffff;
}
.el-main {
background: #f2f2f2;
min-height: calc(100vh - 54px);
padding: 0;
margin-top: 60px;
}
.site-footer {
height: 54px;
background: #1a1a1a;
color: #cfcfcf;
display: flex;
align-items: center;
justify-content: center;
border-top: 1px solid #2a2a2a;
}
.footer-text { font-size: 13px; letter-spacing: 1px; }
</style>

View File

@ -0,0 +1,32 @@
import moment from "moment";
/**
* 参数处理
* @param {*} params 参数
*/
export function tansParams(params) {
let result = ''
for (const propName of Object.keys(params)) {
const value = params[propName];
const part = encodeURIComponent(propName) + "=";
if (value !== null && value !== "" && typeof (value) !== "undefined") {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
const params = propName + '[' + key + ']';
const subPart = encodeURIComponent(params) + "=";
result += subPart + encodeURIComponent(value[key]) + "&";
}
}
} else {
result += part + encodeURIComponent(value) + "&";
}
}
}
return result
}
// 日期格式化
export function parseTime(time, pattern) {
return moment(time).format(pattern || 'YYYY-MM-DD HH:mm:ss')
}

View File

@ -0,0 +1,338 @@
<template>
<div class="detail-container">
<div class="top-bar">
<el-button link size="large" :icon="ArrowLeft" @click="goBack">返回</el-button>
</div>
<div class="detail-body" v-loading="loading">
<div class="media">
<div class="cover">
<el-image v-if="detail?.coverImageUrl" :src="getFilePrefix + detail.coverImageUrl" :preview-src-list="[getFilePrefix + detail.coverImageUrl]" preview-teleported fit="cover" />
<div v-else class="cover-empty"></div>
<div v-if="detail?.modelUrl" class="badge360" @click="openModel">360</div>
</div>
</div>
<div class="info">
<div class="title">{{ detail?.name || '—' }}</div>
<!-- <div class="meta">
<span class="cat">{{ detail?.categoryInfo?.name || detail?.categoryId || '—' }}</span>
<span v-if="detail?.material" class="tag">{{ detail.material }}</span>
<span v-if="detail?.age" class="tag">{{ detail.age }}</span>
</div> -->
<div class="chips">
<span v-if="Number(detail?.isHot) === 1" class="chip hot">热门</span>
<!-- <span class="chip status" :class="Number(detail?.status) === 1 ? 'on' : 'off'">{{ Number(detail?.status) === 1 ? '显示' : '隐藏' }}</span> -->
</div>
<div class="kv-list">
<div class="kv-item">
<div class="kv-label">分类名称</div>
<div class="kv-val">{{ detail?.categoryInfo?.name || '—' }}</div>
</div>
<div class="kv-item">
<div class="kv-label">材质</div>
<div class="kv-val">{{ detail?.material || '—' }}</div>
</div>
<div class="kv-item">
<div class="kv-label">年代</div>
<div class="kv-val">{{ detail?.age || '—' }}</div>
</div>
</div>
<div class="desc">
<div class="section">
<div class="section-title">出土信息</div>
<div class="section-text">{{ detail?.excavationInfo || '—' }}</div>
</div>
</div>
<div class="collect-bar" v-if="isLogin">
<el-button class="collect-btn" :type="isCollected ? 'danger' : 'warning'" size="large" @click="toggleCollect">
{{ isCollected ? '取消收藏' : '收藏' }}
</el-button>
</div>
</div>
<div class="story-panel">
<div class="section">
<div class="section-title">文物故事</div>
<div class="section-text" v-html="detail?.story || '—'"></div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, getCurrentInstance, onMounted } from 'vue'
import { getRelic } from '@/api/relic'
import { addItemCollection, delItemCollection } from '@/api/itemCollection'
// ID
import { Back,ArrowLeft } from '@element-plus/icons-vue'
import Cookies from 'js-cookie'
const { proxy } = getCurrentInstance()
const getFilePrefix = proxy.getFilePrefix
const loading = ref(true)
const detail = ref(null)
const isCollected = ref(false)
const collectionId = ref(null)
let relicIdCache = null
const isLogin = ref(!!Cookies.get('role'))
function formatDate(val) {
if (!val) return '—'
const d = new Date(val)
if (isNaN(d.getTime())) return '—'
const yyyy = d.getFullYear()
const MM = String(d.getMonth() + 1).padStart(2, '0')
const DD = String(d.getDate()).padStart(2, '0')
const HH = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return `${yyyy}-${MM}-${DD} ${HH}:${mm}`
}
function openModel() {
if (detail.value?.modelUrl) {
window.open(getFilePrefix + detail.value.modelUrl, '_blank')
}
}
function goBack() {
proxy.$router.back()
}
function refreshCollectStatus() {
if (!isLogin.value) {
isCollected.value = false
collectionId.value = null
return
}
const list = Array.isArray(detail.value?.itemCollections) ? detail.value.itemCollections : []
const mine = list.length ? list[0] : null
isCollected.value = !!mine
collectionId.value = mine ? mine.id : null
}
function fetchDetail() {
if (!relicIdCache) { loading.value = false; return }
getRelic(relicIdCache).then(res => {
detail.value = res.data || res
loading.value = false
refreshCollectStatus()
}).catch(() => { loading.value = false })
}
function toggleCollect() {
if (!relicIdCache) return
if (!isLogin.value) return
if (!isCollected.value) {
addItemCollection({ itemId: relicIdCache }).then(r => {
// proxy.$message.success((r && r.msg) || '')
fetchDetail()
})
} else if (collectionId.value) {
delItemCollection(collectionId.value).then(r => {
// proxy.$message.success((r && r.msg) || '')
fetchDetail()
})
}
}
onMounted(() => {
const relicId = proxy.$route.params.relicId
relicIdCache = relicId
fetchDetail()
})
</script>
<style scoped>
.detail-container {
width: 80%;
margin: 24px auto 40px;
padding-top: 16px;
}
.top-bar {
display: flex;
gap: 12px;
align-items: center;
margin-bottom: 16px;
}
.detail-body {
display: grid;
grid-template-columns: 1.3fr 1fr;
gap: 28px;
grid-auto-rows: min-content;
}
.media {
display: flex;
}
.cover {
position: relative;
width: 100%;
height: 520px;
background: #f9f6f1;
border: 1px solid #e7dfcf;
border-radius: 16px;
overflow: hidden;
}
.cover :deep(.el-image) {
width: 100%;
height: 100%;
}
.cover-empty {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #909399;
font-size: 18px;
}
.badge360 {
position: absolute;
top: 16px;
left: 16px;
background: #ffffff;
color: #8a2b2b;
border: 1px solid #e8d9cf;
border-radius: 999px;
padding: 8px 12px;
font-weight: 800;
cursor: pointer;
}
.badge360:hover {
background: #fef4f4;
}
.info {
display: flex;
flex-direction: column;
background: linear-gradient(180deg, #faf7f2 0%, #f7f3ea 100%);
border: 1px solid #e7dfcf;
border-radius: 16px;
padding: 16px 18px;
box-shadow: 0 6px 14px rgba(138,43,43,0.08);
}
.title {
font-size: 28px;
font-weight: 800;
color: #2c1f1f;
letter-spacing: 1px;
margin-bottom: 12px;
}
.meta {
display: flex;
gap: 10px;
align-items: center;
margin-bottom: 16px;
flex-wrap: wrap;
}
.chips {
display: flex;
gap: 8px;
margin-bottom: 14px;
}
.chip {
display: inline-block;
padding: 4px 10px;
font-size: 12px;
border-radius: 999px;
border: 1px solid #e8d9cf;
background: #f5e9e2;
color: #8a2b2b;
font-weight: 700;
}
.chip.hot {
background: #ffefe6;
border-color: #ffd5bf;
color: #c64820;
}
.chip.status.on {
background: #e8f5e9;
border-color: #c8e6c9;
color: #2e7d32;
}
.chip.status.off {
background: #fdecea;
border-color: #f5c6c6;
color: #c62828;
}
.kv-list {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px 16px;
margin-bottom: 12px;
}
.kv-item {
display: flex;
gap: 8px;
align-items: baseline;
}
.kv-label {
width: 80px;
color: #8a2b2b;
font-weight: 700;
font-size: 13px;
}
.kv-val {
color: #2c1f1f;
font-size: 14px;
}
.cat {
font-size: 14px;
color: #8a2b2b;
background: #f5e9e2;
border: 1px solid #e8d9cf;
border-radius: 999px;
padding: 4px 10px;
}
.tag {
font-size: 14px;
color: #2c1f1f;
background: #efe9df;
border: 1px solid #e0d6c7;
border-radius: 999px;
padding: 4px 10px;
}
.desc {
display: flex;
flex-direction: column;
gap: 14px;
}
.section {
background: #ffffff;
border: 1px solid #eadfce;
border-radius: 14px;
padding: 16px;
}
.section-title {
font-size: 16px;
font-weight: 800;
color: #2c1f1f;
margin-bottom: 8px;
}
.section-text {
color: #606266;
line-height: 1.9;
font-size: 16px;
}
.story-panel {
grid-column: 1 / -1;
}
.collect-bar {
grid-column: 1 / -1;
display: flex;
justify-content: center;
margin-top: 16px;
}
.collect-btn {
padding: 18px 44px;
font-size: 18px;
border-radius: 999px;
box-shadow: 0 8px 18px rgba(138,43,43,0.12);
font-weight: 700;
}
.section-text :deep(img) {
max-width: 100%;
height: auto;
display: block;
margin: 8px auto;
border-radius: 8px;
}
</style>

View File

@ -0,0 +1,72 @@
package com.amms.controller;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.amms.domain.vo.Result;
import com.amms.domain.Carousel;
import com.amms.service.ICarouselService;
import java.util.List;
/**
* 轮播图Controller
*/
@RestController
@RequestMapping("/carousel")
public class CarouselController {
@Autowired
private ICarouselService carouselService;
/**
* 查询轮播图列表
*/
@GetMapping("/list")
public PageInfo list(Carousel carousel, @RequestParam("pageNum") Integer pageNum, @RequestParam("pageSize") Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<Carousel> carousels = carouselService.selectCarouselList(carousel);
return new PageInfo(carousels);
}
/**
* 查询全部轮播图列表
*/
@GetMapping("/listAll")
public List<Carousel> listAll(Carousel carousel) {
return carouselService.selectCarouselList(carousel);
}
/**
* 获取轮播图详细信息
*/
@GetMapping(value = "/info/{id}")
public Result getInfo(@PathVariable("id") Long id) {
return Result.success(carouselService.selectCarouselById(id));
}
/**
* 新增轮播图
*/
@PostMapping("/add")
public Result add(@RequestBody Carousel carousel) {
return carouselService.insertCarousel(carousel) > 0 ? Result.success("新增成功") : Result.error("新增失败");
}
/**
* 修改轮播图
*/
@PutMapping
public Result update(@RequestBody Carousel carousel) {
return carouselService.updateCarousel(carousel) > 0 ? Result.success("修改成功") : Result.error("修改失败");
}
/**
* 删除轮播图
*/
@DeleteMapping("/{id}")
public Result delete(@PathVariable Long id) {
return carouselService.deleteCarouselById(id) > 0 ? Result.success("删除成功") : Result.error("删除失败");
}
}

View File

@ -0,0 +1,161 @@
package com.amms.controller;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.URLEncoder;
import java.util.UUID;
/**
* 文件处理controller
*/
@Controller
@RequestMapping("/files")
public class FilesController {
private static Logger logger = LoggerFactory.getLogger(FilesController.class);
@Value("${files.path}")
private String path;
/**
* 文件上传
* @param file
* @return
* @throws IOException
*/
@PostMapping("/upload")
@ResponseBody
public String upload(@RequestParam("file") MultipartFile file ,@RequestParam(name = "filePath" ,required = false) String filePath) throws IOException {
logger.info("上传文件:{}", file.getName());
File fileUploadPath = null;
// 创建目标路径
if (!filePath.equals("") && filePath != null) {
fileUploadPath = new File(path, filePath);
} else {
fileUploadPath = new File(path);
}
if (!fileUploadPath.exists()) {
fileUploadPath.mkdirs();
}
// 新文件名
String newFileName = UUID.randomUUID().toString().replace("-", "") + "-" + file.getOriginalFilename();
file.transferTo(new File(fileUploadPath, newFileName));
// 判断参数中filePath时候为null 不为空则拼接到新文件名前
if (!filePath.equals("") && filePath != null) {
if (filePath.endsWith("/")) {
newFileName = filePath + newFileName;
} else {
newFileName = filePath + "/" + newFileName;
}
}
return newFileName;
}
byte[] buffer = new byte[1024];
BufferedInputStream bis = null;
OutputStream os = null;
/**
* 获取文件图片
* @param fileName
* @param response
* @throws UnsupportedEncodingException
*/
@GetMapping("/get")
public void getImage(@RequestParam("fileName") String fileName, HttpServletResponse response) throws UnsupportedEncodingException {
logger.debug("加载图片,文件名:" + fileName);
File file = new File(path, fileName);
// 判断文件是否存在
if (!file.exists()) {
logger.error("文件【" + file.getAbsolutePath() + "】不存在!");
response.setStatus(404);
return;
}
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") );
response.setContentType("image/jpeg");
response.setContentLength((int)file.length());
FileInputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream(file);
os = response.getOutputStream();
byte[] buf = new byte[8192];
int rd;
while ((rd = is.read(buf)) > 0)
os.write(buf, 0, rd);
} catch (IOException e) {
logger.error(e.getMessage());
} finally {
try {
if (is!=null) is.close();
if (os!=null) {
os.flush();
os.close();
}
} catch (IOException e) {
logger.error(e.getMessage());
}
}
}
/**
* 删除文件
* @param fileName
* @param response
* @throws UnsupportedEncodingException
*/
@GetMapping("/delete")
public void delete(@RequestParam("fileName") String fileName, HttpServletResponse response) throws UnsupportedEncodingException {
logger.error("删除文件,文件名:" + fileName);
File file = new File(path + fileName);
if (!file.exists()) {
logger.error("文件【" + fileName + "】不存在!");
response.setStatus(404);
return;
}
try {
file.delete();
} catch (Exception e) {
logger.error(e.getMessage());
}
}
/**
* 下载文件
* @param fileName
* @param request
* @param response
* @throws IOException
*/
@GetMapping("/download")
public void download(String fileName, HttpServletRequest request, HttpServletResponse response) throws IOException {
logger.info("下载文件:{}", fileName);
File fileUploadPathFile = new File(path);
if (!fileUploadPathFile.exists()) {
fileUploadPathFile.mkdir();
}
File file = new File(fileUploadPathFile, fileName);
response.setContentType("application/octet-stream;charset=UTF-8");
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
response.setHeader("Content-Disposition","attachment;filename="+fileName);
response.setCharacterEncoding("UTF-8");
os = response.getOutputStream();
bis = new BufferedInputStream(new FileInputStream(file));
while(bis.read(buffer) != -1){
os.write(buffer);
}
}
}

View File

@ -0,0 +1,119 @@
package com.amms.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* 轮播图对象 carousel
*/
public class Carousel {
/** 轮播图id */
private Long id;
/** 标题 */
private String title;
/** 图片地址 */
private String imageUrl;
/** 跳转链接 */
private String link;
/** 排序序号 */
private Integer sort;
/** 状态0隐藏 1显示 */
private Long status;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public String getImageUrl() {
return imageUrl;
}
public void setLink(String link) {
this.link = link;
}
public String getLink() {
return link;
}
public void setSort(Integer sort) {
this.sort = sort;
}
public Integer getSort() {
return sort;
}
public void setStatus(Long status) {
this.status = status;
}
public Long getStatus() {
return status;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
@Override
public String toString() {
return "Carousel{" +
"id=" + id +
", title=" + title +
", imageUrl=" + imageUrl +
", link=" + link +
", sort=" + sort +
", status=" + status +
", createTime=" + createTime +
", updateTime=" + updateTime +
'}';
}
}

View File

@ -0,0 +1,52 @@
package com.amms.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.amms.domain.Carousel;
/**
* 轮播图Mapper接口
*/
@Mapper
public interface CarouselMapper {
/**
* 查询轮播图
*
* @param id 轮播图主键
* @return 轮播图
*/
public Carousel selectCarouselById(Long id);
/**
* 查询轮播图列表
*
* @param carousel 轮播图
* @return 轮播图集合
*/
public List<Carousel> selectCarouselList(Carousel carousel);
/**
* 新增轮播图
*
* @param carousel 轮播图
* @return 结果
*/
public int insertCarousel(Carousel carousel);
/**
* 修改轮播图
*
* @param carousel 轮播图
* @return 结果
*/
public int updateCarousel(Carousel carousel);
/**
* 删除轮播图
*
* @param id 轮播图主键
* @return 结果
*/
public int deleteCarouselById(Long id);
}

View File

@ -0,0 +1,77 @@
package com.amms.service.impl;
import java.util.List;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.amms.mapper.CarouselMapper;
import com.amms.domain.Carousel;
import com.amms.service.ICarouselService;
/**
* 轮播图Service业务层处理
*/
@Service
public class CarouselServiceImpl implements ICarouselService {
@Autowired
private CarouselMapper carouselMapper;
/**
* 查询轮播图列表
*
* @param carousel 轮播图
* @return 轮播图
*/
@Override
public List<Carousel> selectCarouselList(Carousel carousel) {
return carouselMapper.selectCarouselList(carousel);
}
/**
* 查询轮播图
*
* @param id 轮播图主键
* @return 轮播图
*/
@Override
public Carousel selectCarouselById(Long id) {
return carouselMapper.selectCarouselById(id);
}
/**
* 新增轮播图
*
* @param carousel 轮播图
* @return 结果
*/
@Override
public int insertCarousel(Carousel carousel) {
carousel.setCreateTime(new Date());
return carouselMapper.insertCarousel(carousel);
}
/**
* 修改轮播图
*
* @param carousel 轮播图
* @return 结果
*/
@Override
public int updateCarousel(Carousel carousel) {
carousel.setUpdateTime(new Date());
return carouselMapper.updateCarousel(carousel);
}
/**
* 删除轮播图信息
*
* @param id 轮播图主键
* @return 结果
*/
@Override
public int deleteCarouselById(Long id) {
return carouselMapper.deleteCarouselById(id);
}
}

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.amms.mapper.CarouselMapper">
<resultMap type="com.amms.domain.Carousel" id="CarouselResult">
<result property="id" column="id" />
<result property="title" column="title" />
<result property="imageUrl" column="image_url" />
<result property="link" column="link" />
<result property="sort" column="sort" />
<result property="status" column="status" />
<result property="createTime" column="create_time" />
<result property="updateTime" column="update_time" />
</resultMap>
<sql id="selectCarouselVo">
select id, title, image_url, link, sort, status, create_time, update_time from carousel
</sql>
<select id="selectCarouselList" parameterType="com.amms.domain.Carousel" resultMap="CarouselResult">
<include refid="selectCarouselVo"/>
<where>
<if test="title != null and title != ''"> and title = #{title}</if>
<if test="imageUrl != null and imageUrl != ''"> and image_url = #{imageUrl}</if>
<if test="link != null and link != ''"> and link = #{link}</if>
<if test="sort != null "> and sort = #{sort}</if>
<if test="status != null "> and status = #{status}</if>
</where>
</select>
<select id="selectCarouselById" parameterType="Long" resultMap="CarouselResult">
<include refid="selectCarouselVo"/>
where id = #{id}
</select>
<insert id="insertCarousel" parameterType="com.amms.domain.Carousel" useGeneratedKeys="true" keyProperty="id">
insert into carousel
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="title != null and title != ''">title,</if>
<if test="imageUrl != null and imageUrl != ''">image_url,</if>
<if test="link != null">link,</if>
<if test="sort != null">sort,</if>
<if test="status != null">status,</if>
<if test="createTime != null">create_time,</if>
<if test="updateTime != null">update_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="title != null and title != ''">#{title},</if>
<if test="imageUrl != null and imageUrl != ''">#{imageUrl},</if>
<if test="link != null">#{link},</if>
<if test="sort != null">#{sort},</if>
<if test="status != null">#{status},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateTime != null">#{updateTime},</if>
</trim>
</insert>
<update id="updateCarousel" parameterType="com.amms.domain.Carousel">
update carousel
<trim prefix="SET" suffixOverrides=",">
<if test="title != null and title != ''">title = #{title},</if>
<if test="imageUrl != null and imageUrl != ''">image_url = #{imageUrl},</if>
<if test="link != null">link = #{link},</if>
<if test="sort != null">sort = #{sort},</if>
<if test="status != null">status = #{status},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteCarouselById" parameterType="Long">
delete from carousel where id = #{id}
</delete>
</mapper>