完成广告管理模块接口

pull/39/head
riverflow 2 months ago
parent 99b4209280
commit a3c02ec4e0

@ -0,0 +1,59 @@
package com.itmk.web.banner.controller;
import com.alibaba.druid.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itmk.utils.ResultUtils;
import com.itmk.utils.ResultVo;
import com.itmk.web.banner.entity.BannerParm;
import com.itmk.web.banner.entity.SysBanner;
import com.itmk.web.banner.service.SysBannerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/banner")
public class SysBannerController {
@Autowired
private SysBannerService sysBannerService;
// 新增
@PostMapping
public ResultVo addCategory(@RequestBody SysBanner sysBanner) {
sysBannerService.save(sysBanner);
return ResultUtils.success("新增广告成功!", sysBanner);
}
// 编辑广告
@PutMapping
public ResultVo editCategory(@RequestBody SysBanner sysBanner){
if(sysBannerService.updateById(sysBanner)){
return ResultUtils.success("编辑广告成功!", sysBanner);
}
return ResultUtils.error("编辑广告失败!");
}
// 删除广告
@DeleteMapping("/{categoryId}")
public ResultVo deleteCategory(@PathVariable Long categoryId){
if(sysBannerService.removeById(categoryId)){
return ResultUtils.success("删除广告成功!");
}
return ResultUtils.error("删除广告失败!");
}
// 列表查询
// 列表查询需要分页
@GetMapping("/list")
public ResultVo getList(BannerParm parm) {
IPage<SysBanner> page = new Page<>(parm.getCurrentPage(), parm.getPageSize());
//构造查询条件
QueryWrapper<SysBanner> query = new QueryWrapper<>();
query.lambda().like(!StringUtils.isEmpty(parm.getTitle()),SysBanner::getTitle,parm.getTitle());
IPage<SysBanner> list = sysBannerService.page(page, query);
return ResultUtils.success("查询成功",list);
}
}

@ -0,0 +1,11 @@
package com.itmk.web.banner.entity;
import lombok.Data;
@Data
public class BannerParm {
private Integer currentPage; //当前页
private Integer pageSize;//每页查询的条数
private String title;
}

@ -0,0 +1,18 @@
package com.itmk.web.banner.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("sys_banner")
public class SysBanner {
@TableId(type = IdType.AUTO)
private Long bannerId;
private Long goodsId;
private String title;
private String images;
private String status;
private Integer orderNum;
}

@ -0,0 +1,7 @@
package com.itmk.web.banner.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itmk.web.banner.entity.SysBanner;
public interface SysBannerMapper extends BaseMapper<SysBanner> {
}

@ -0,0 +1,7 @@
package com.itmk.web.banner.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itmk.web.banner.entity.SysBanner;
public interface SysBannerService extends IService<SysBanner> {
}

@ -0,0 +1,11 @@
package com.itmk.web.banner.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itmk.web.banner.entity.SysBanner;
import com.itmk.web.banner.mapper.SysBannerMapper;
import com.itmk.web.banner.service.SysBannerService;
import org.springframework.stereotype.Service;
@Service
public class SysBannerServiceImpl extends ServiceImpl<SysBannerMapper, SysBanner> implements SysBannerService {
}

@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itmk.utils.ResultUtils;
import com.itmk.utils.ResultVo;
import com.itmk.web.category.entity.SelectType;
import com.itmk.web.goods.entity.GoodsPageParm;
import com.itmk.web.goods.entity.GoodsParm;
import com.itmk.web.goods.entity.SysGoods;
@ -16,7 +17,9 @@ import com.itmk.web.goods_specs.service.SysGoodsSpecsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/api/goods")
@ -70,4 +73,21 @@ public class SysGoodsController {
return ResultUtils.success("查询菜品成功!", list);
}
//列表
@GetMapping("/getSelectList")
public ResultVo getSelectList(){
List<SysGoods> list = sysGoodsService.list();
//组装为前端下拉选择器需要的数据格式
List<SelectType> selectList = new ArrayList<>();
Optional.ofNullable(list).orElse(new ArrayList<>())
.stream()
.forEach(item ->{
SelectType type = new SelectType();
type.setLabel(item.getGoodsName());
type.setValue(String.valueOf(item.getGoodsId()));
selectList.add(type);
});
return ResultUtils.success("查询成功!",selectList);
}
}

@ -0,0 +1,7 @@
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itmk.web.sys_banner.mapper.SysBannerMapper">
</mapper>

@ -0,0 +1,18 @@
//列表参数类型
export type bannerListParm = {
currentPage:number;
pageSize:number;
title:string;
total:number; //分页的总条数
}
//广告管理数据类型
export type bannerType = {
categoryId: string;
type:string;
bannerId:string;
goodsId:string;
title:string;
images:string;
status:string;
orderNum:string;
}

@ -0,0 +1,20 @@
import http from "../../http";
import type { bannerType, bannerListParm } from "./bannerModel";
//新增
export const addApi = (parm:bannerType)=>{
return http.post("/api/banner",parm)
}
//列表
export const getListApi = (parm:bannerListParm)=>{
return http.get("/api/banner/list",parm)
}
//编辑
export const editApi = (parm:bannerType)=>{
return http.put("/api/banner",parm)
}
//删除
export const deleteApi = (bannerId:string)=>{
return http.delete(`/api/banner/${bannerId}`)
}

@ -26,4 +26,9 @@ export const editGoodsApi = (parm: goodsModel) => {
export const getGoodsApi = (parm: ListGoodsParm) => {
// 查需要找到列表,把列表里面的数据返回出来
return http.get('/api/goods/list', parm)
}
//菜品下拉列表
export const getSelectListApi = ()=>{
return http.get("/api/goods/getSelectList")
}

@ -0,0 +1,35 @@
import type { bannerType } from "../../api/banner/bannerModel";
import { EditType, type FuncList } from "../../type/baseType";
import { ref } from "vue";
import { deleteApi } from '../../api/banner/index'
import useInstance from "@/hooks/useInstance";
import { ElMessage } from "element-plus";
export default function useBanner(getList: FuncList) {
const { global } = useInstance()
const showBtn = ref<{ show: (title: string, type: string, width?: number, height?: number, row?: bannerType) => {} }>();
//新增
const addBtn = () => {
showBtn.value?.show("新增", EditType.ADD, 900, 500)
}
//编辑
const editBtn = (row: bannerType) => {
showBtn.value?.show("编辑", EditType.EDIT, 900, 500, row)
}
//删除
const deleteBtn = async (row: bannerType) => {
const confirm = await global.$myconfirm('确定删除吗?')
if (confirm) {
let res = await deleteApi(row.bannerId)
if (res && res.code == 200) {
ElMessage.success(res.msg)
getList()
}
}
}
return {
showBtn,
addBtn,
editBtn,
deleteBtn
}
}

@ -0,0 +1,60 @@
import type { bannerListParm } from "../../api/banner/bannerModel";
import { reactive ,ref,onMounted,nextTick} from "vue";
import {getListApi} from '../../api/banner/index'
export default function useBannerTable(){
//表格高度
const tableHeight = ref(0)
//接收表格数据
const tableList = ref([])
//列表查询的参数
const listParm = reactive<bannerListParm>({
currentPage:1,
pageSize:10,
title:'',
total:0
})
//列表
const getList = async()=>{
let res = await getListApi(listParm)
if(res && res.code == 200){
tableList.value = res.data.records;
listParm.total = res.data.total;
}
}
//搜索
const searchBtn = ()=>{
getList()
}
//重置
const resetBtn = ()=>{
listParm.currentPage = 1;
listParm.title = ''
getList()
}
//页容量改变时触发
const sizeChange = (size:number)=>{
listParm.pageSize = size;
getList()
}
//页数改变触发
const currentChange = (page:number)=>{
listParm.currentPage = page;
getList()
}
onMounted(()=>{
getList()
nextTick(()=>{
tableHeight.value = window.innerHeight - 220
})
})
return{
listParm,
getList,
searchBtn,
resetBtn,
tableList,
sizeChange,
currentChange,
tableHeight
}
}

@ -0,0 +1,56 @@
import { ref } from "vue";
import { getSelectListApi } from "../../api/goods";
// 定义下拉选项的类型
export interface SelectOption {
value: string | number;
label: string;
}
export default function useSelectCategory() {
// 下拉数据
const selectData = ref<SelectOption[]>([])
// 获取下拉数据
const getSelect = async () => {
try {
let res = await getSelectListApi();
console.log("API原始响应:", res); // 打印完整响应
if (res && res.code == 200) {
// 打印实际数据结构
console.log("API返回的数据类型:", typeof res.data);
console.log("API返回的数据:", res.data);
// 检查数据是否为数组
if (!Array.isArray(res.data)) {
console.error("API返回的数据不是数组:", res.data);
selectData.value = [];
return;
}
// 检查数组元素结构
if (res.data.length > 0) {
console.log("第一个元素的结构:", Object.keys(res.data[0]));
}
// 创建新数组赋值,确保响应式更新
const newData = res.data.map(item => ({
value: item.value.toString(),
label: item.label
}));
selectData.value = newData;
console.log("映射后的分类数据:", JSON.parse(JSON.stringify(newData)));
}
} catch (error) {
console.error("获取分类数据失败:", error);
selectData.value = [];
}
};
return {
selectData, getSelect
}
}

@ -0,0 +1,221 @@
<template>
<SysDialog :title="dialog.title" :width="dialog.width" :height="dialog.height" :visible="dialog.visible"
@onClose="onClose" @onConfirm="commit">
<template v-slot:content>
<el-form :model="addModel" ref="addFormRef" :rules="rules" label-width="80px" :inline="false" size="default">
<el-form-item prop="title" label="标题:">
<el-input v-model="addModel.title"></el-input>
</el-form-item>
<el-form-item prop="goodsId" label="菜品:">
<el-select style="width: 100%" v-model="addModel.goodsId" placeholder="请选择菜品" size="default">
<el-option v-for="item in selectData" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item prop="orderNum" label="序号:">
<el-input v-model="addModel.orderNum"></el-input>
</el-form-item>
<el-form-item prop="status" label="上下架:">
<el-radio-group v-model="addModel.status">
<el-radio :label="'0'">下架</el-radio>
<el-radio :label="'1'">上架</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item prop="images" label="广告图片">
<UploadImage @getImg="getImg" :oldUrl="oldUrl" :fileList="fileList"></UploadImage>
</el-form-item>
</el-form>
</template>
</SysDialog>
</template>
<script setup lang="ts">
import { EditType, type NewType } from "../../type/baseType";
import UploadImage from "@/components/UploadImage.vue";
import type { bannerType } from "../../api/banner/bannerModel";
import SysDialog from "@/components/SysDialog.vue";
import useDialog from "@/hooks/useDialog";
import { ElMessage, type FormInstance, type UploadUserFile } from "element-plus";
import { reactive, ref, nextTick, toRefs } from "vue";
import useSelectCategory from "../../compositions/banner/useSelectGoods";
import { addApi, editApi } from "../../api/banner/index";
//
const fileList = ref<Array<UploadUserFile>>([]);
//
const oldUrl = ref<Array<{ url: string }>>([]);
//
const imgUrl = ref<Array<{ url: string }>>([]);
//
import type SelectOption from '@/compositions/goods/useSelectCategory';
const selectCategory = useSelectCategory();
const { selectData } = toRefs(selectCategory); // selectData
const getSelect = selectCategory.getSelect; //
//ref
const addFormRef = ref<FormInstance>();
//
const { dialog, onClose, onConfirm, onShow } = useDialog();
//
const selectLoading = ref(true);
const selectError = ref(false);
//
const show = async (title: string, type: string, width: number, height: number, row?: bannerType) => {
//
selectLoading.value = true;
selectError.value = false;
await getSelect();
console.log("下拉数据已加载:", selectData.value);
//
if (selectData.value.length === 0) {
selectError.value = true;
ElMessage.warning("没有可用的分类数据");
}
//
addFormRef.value?.resetFields();
//
imgUrl.value = [];
oldUrl.value = [];
fileList.value = [];
dialog.height = 400;
dialog.width = 800;
onShow(title, width, height)
if (type == EditType.EDIT && row) {
//
const goodsId = row.goodsId.toString();
//
const selectedGoods = selectData.value.find(
item => item.value === goodsId
);
//
const editData = {
...row,
goodsId: goodsId,
goodsName: selectedGoods ? selectedGoods.label : ""
};
nextTick(() => {
Object.assign(addModel, editData);
//
if (addModel.images) {
//
let imgs = addModel.images.split(",");
for (let i = 0; i < imgs.length; i++) {
let img = {
name: "",
url: "",
};
img.name = imgs[i];
img.url = imgs[i];
fileList.value.push(img);
oldUrl.value.push({ url: imgs[i] });
}
}
});
}
addModel.type = type;
};
//
defineExpose({
show,
});
//
const addModel = reactive<bannerType>({
categoryId: "",
type: "",
bannerId: "",
goodsId: "",
title: "",
images: "",
status: "",
orderNum: "",
});
//
const rules = reactive({
goodsId: [
{
required: true,
trigger: "blur",
message: "请选择菜品",
},
],
title: [
{
required: true,
trigger: "blur",
message: "请填写标题",
},
],
orderNum: [
{
required: true,
trigger: "blur",
message: "请填写序号",
},
],
status: [
{
required: true,
trigger: "blur",
message: "请选择是否上架",
},
],
images: [
{
required: true,
trigger: "blur",
message: "请上传图片",
},
],
});
//
const getImg = (img: NewType) => {
console.log("999");
console.log(img.newImgUrl);
imgUrl.value = oldUrl.value.concat(img.newImgUrl);
if (img.deleteImgUrl.length > 0) {
let newArr = imgUrl.value.filter(
(x) => !img.deleteImgUrl.some((item) => x.url === item.url)
);
imgUrl.value = newArr;
}
// console.log(imgUrl.value)
//
let url = "";
for (let k = 0; k < imgUrl.value.length; k++) {
url = url + imgUrl.value[k].url + ",";
}
addModel.images = url.substring(0, url.lastIndexOf(","));
console.log(addModel.images);
};
//
const emits = defineEmits(['onFresh'])
//
const commit = () => {
addFormRef.value?.validate(async (valid) => {
if (valid) {
let res = null;
if (addModel.type == EditType.ADD) {
res = await addApi(addModel);
} else {
res = await editApi(addModel)
}
if (res && res.code == 200) {
ElMessage.success(res.msg);
emits('onFresh')
onClose();
}
}
});
};
</script>
<style scoped></style>

@ -1,13 +1,70 @@
<template>
<div>
广告管理
</div>
<el-main>
<!-- 搜索栏 -->
<el-form :model="listParm" :inline="true" size="default">
<el-form-item>
<el-input placeholder="请输入标题" v-model="listParm.title"></el-input>
</el-form-item>
<el-form-item>
<el-button :icon="Search" @click="searchBtn"></el-button>
<el-button :icon="Close" type="danger" plain @click="resetBtn"
>重置</el-button
>
<el-button :icon="Plus" type="primary" plain @click="addBtn"></el-button>
</el-form-item>
</el-form>
<!-- 表格 -->
<el-table :height="tableHeight" :data="tableList" border stripe>
<el-table-column label="广告图片" prop="goodsImage">
<template #default="scope">
<el-image
:src="scope.row.images.split(',')[0]"
style="height: 60px; width: 60px; border-radius: 50%"
></el-image>
</template>
</el-table-column>
<el-table-column label="标题" prop="title"></el-table-column>
<el-table-column label="是否上架" prop="title">
<template #default="scope">
<el-tag v-if="scope.row.status == '0'" type="danger" size="default"
></el-tag
>
<el-tag v-else size="default" effect="light"></el-tag>
</template>
</el-table-column>
<el-table-column label="序号" prop="orderNum"></el-table-column>
<el-table-column label="操作" width="220" align="center">
<template #default="scoped">
<el-button :icon="Edit" type="primary" size="default" @click="editBtn(scoped.row)"></el-button>
<el-button :icon="Delete" type="danger" size="default" @click="deleteBtn(scoped.row)"></el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
@size-change="sizeChange"
@current-change="currentChange"
:current-page.sync="listParm.currentPage"
:page-sizes="[10,20, 40, 80, 100]"
:page-size="listParm.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="listParm.total" background>
</el-pagination>
</el-main>
<!-- 新增编辑 -->
<add-banner ref="showBtn" @onFresh="getList"></add-banner>
</template>
<script setup lang="ts">
import AddBanner from "@/views/banner/AddBanner.vue";
import { Edit, Plus, Delete, Search, Close } from "@element-plus/icons-vue";
import useBannerTable from "@/compositions/banner/useBannerTable";
import useBanner from "@/compositions/banner/useBanner";
//
const { getList, listParm, resetBtn, searchBtn, tableList,sizeChange ,currentChange,tableHeight} = useBannerTable();
//
const { addBtn, editBtn, deleteBtn, showBtn } = useBanner(getList);
</script>
<style scoped>
</style>
<style scoped></style>
Loading…
Cancel
Save