完成菜品管理接口开发 #38

Merged
ppnwsfegt merged 1 commits from Brunch_LPQ into main 2 months ago

@ -0,0 +1,32 @@
package com.itmk.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {
@Value("${web.load-path}")
private String loadPath;
/**
*
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("*")
.allowedHeaders("*")
.maxAge(3600)
.allowCredentials(true);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/images/**")
.addResourceLocations(loadPath);
}
}

@ -8,11 +8,16 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itmk.utils.ResultUtils;
import com.itmk.utils.ResultVo;
import com.itmk.web.category.entity.CategoryPageParm;
import com.itmk.web.category.entity.SelectType;
import com.itmk.web.category.entity.SysCategory;
import com.itmk.web.category.service.SysCategoryService;
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/category")
public class SysCategoryController {
@ -66,4 +71,21 @@ public class SysCategoryController {
return ResultUtils.success("查询成功", list);
}
@GetMapping("/getSelectList")
public ResultVo getSelectList() {
List<SysCategory> list = sysCategoryService.list();
// 组装为前端下拉选择器的数据格式
List<SelectType> selectList = new ArrayList<>();
Optional.ofNullable(list).orElse(new ArrayList<>())
.stream()
.forEach(item -> {
SelectType selectType = new SelectType();
selectType.setLabel(item.getCategoryName());
selectType.setValue(item.getCategoryId().toString());
selectList.add(selectType);
});
return ResultUtils.success("查询成功", selectList);
}
}

@ -0,0 +1,10 @@
package com.itmk.web.category.entity;
import lombok.Data;
@Data
public class SelectType {
private String value;
private String label;
}

@ -0,0 +1,73 @@
package com.itmk.web.goods.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.goods.entity.GoodsPageParm;
import com.itmk.web.goods.entity.GoodsParm;
import com.itmk.web.goods.entity.SysGoods;
import com.itmk.web.goods.service.SysGoodsService;
import com.itmk.web.goods_specs.entity.SysGoodsSpecs;
import com.itmk.web.goods_specs.service.SysGoodsSpecsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/goods")
public class SysGoodsController {
@Autowired
private SysGoodsService sysGoodsService;
@Autowired
private SysGoodsSpecsService sysGoodsSpecsService;
@PostMapping
public ResultVo addGoods(@RequestBody GoodsParm parm) {
sysGoodsService.saveGoods(parm);
return ResultUtils.success("新增菜品成功", parm);
}
//编辑
@PutMapping
public ResultVo editGoods(@RequestBody GoodsParm parm){
sysGoodsService.editGoods(parm);
return ResultUtils.success("编辑菜品成功!");
}
@DeleteMapping("/{goodsId}")
public ResultVo deleteGoods(@PathVariable Long goodsId) {
return sysGoodsService.removeById(goodsId)
? ResultUtils.success("删除菜品成功")
: ResultUtils.error("删除菜品失败");
}
@GetMapping("/list")
public ResultVo getList(GoodsPageParm parm) {
// 构造分页对象
IPage<SysGoods> page = new Page<>(parm.getCurrentPage(), parm.getPageSize());
// 构造查询条件
QueryWrapper<SysGoods> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().like(!StringUtils.isEmpty(parm.getGoodsName()), SysGoods::getGoodsName, parm.getGoodsName())
// 根据菜名升序排序
.orderByAsc(SysGoods::getGoodsName);
IPage<SysGoods> list = sysGoodsService.page(page, queryWrapper);
if (list.getRecords().size() > 0) {
for (int i = 0; i < list.getRecords().size(); i++) {
QueryWrapper<SysGoodsSpecs> query = new QueryWrapper<>();
query.lambda().eq(SysGoodsSpecs::getGoodsId, list.getRecords().get(i).getGoodsId())
.orderByAsc(SysGoodsSpecs::getOrderNum);
List<SysGoodsSpecs> specs = sysGoodsSpecsService.list(query);
list.getRecords().get(i).setSpecs(specs);
}
}
return ResultUtils.success("查询菜品成功!", list);
}
}

@ -0,0 +1,11 @@
package com.itmk.web.goods.entity;
import lombok.Data;
@Data
public class GoodsPageParm {
private Integer currentPage;//当前页
private Integer pageSize;//每页显示多少条
private String goodsName;
}

@ -0,0 +1,21 @@
package com.itmk.web.goods.entity;
import com.itmk.web.goods_specs.entity.SysGoodsSpecs;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class GoodsParm {
private Long goodsId;
private Long categoryId;
private String goodsName;
private String goodsImage;
private String goodsDesc;
private String status;
private String goodsUnit;
private String orderNum;
private List<SysGoodsSpecs> specs = new ArrayList<>();
}

@ -0,0 +1,32 @@
package com.itmk.web.goods.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.itmk.web.goods_specs.entity.SysGoodsSpecs;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
@TableName("sys_goods")
public class SysGoods {
@TableId(type = IdType.AUTO)
private Long goodsId;
private Long CategoryId;
private String goodsName;
private String goodsImage;
private String goodsDesc;
private String status;
private String goodsUnit;
private String orderNum;
@TableField(exist = false)//表明该字段不在这张表中,需要排除
List<SysGoodsSpecs> specs = new ArrayList<>();
}

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

@ -0,0 +1,14 @@
package com.itmk.web.goods.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itmk.web.goods.entity.GoodsParm;
import com.itmk.web.goods.entity.SysGoods;
public interface SysGoodsService extends IService<SysGoods> {
//保存
void saveGoods(GoodsParm parm);
//编辑
void editGoods(GoodsParm parm);
}

@ -0,0 +1,65 @@
package com.itmk.web.goods.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.util.BeanUtil;
import com.itmk.web.goods.entity.GoodsParm;
import com.itmk.web.goods.entity.SysGoods;
import com.itmk.web.goods.mapper.SysGoodsMapper;
import com.itmk.web.goods.service.SysGoodsService;
import com.itmk.web.goods_specs.entity.SysGoodsSpecs;
import com.itmk.web.goods_specs.service.SysGoodsSpecsService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class SysGoodsServiceImpl extends ServiceImpl<SysGoodsMapper, SysGoods> implements SysGoodsService {
@Autowired
private SysGoodsSpecsService sysGoodsSpecsService;
@Override
@Transactional
public void saveGoods(GoodsParm parm) {
//1、保存菜品
SysGoods goods = new SysGoods();
BeanUtils.copyProperties(parm, goods);
int insert = this.baseMapper.insert(goods);
//2、保存规格、价格
if (insert > 0) {
List<SysGoodsSpecs> specs = parm.getSpecs();
for (int i = 0; i < specs.size(); i++) {
//设置菜品id
specs.get(i).setGoodsId(goods.getGoodsId());
}
//批量插入
sysGoodsSpecsService.saveBatch(specs);
}
}
@Override
@Transactional
public void editGoods(GoodsParm parm) {
//1、保存菜品
SysGoods goods = new SysGoods();
BeanUtils.copyProperties(parm, goods);
int insert = this.baseMapper.updateById(goods);
//2、删除菜品原来的规格
QueryWrapper<SysGoodsSpecs> query = new QueryWrapper<>();
query.lambda().eq(SysGoodsSpecs::getGoodsId, parm.getGoodsId());
sysGoodsSpecsService.remove(query);
//3、保存规格、价格
if (insert > 0) {
List<SysGoodsSpecs> specs = parm.getSpecs();
for (int i = 0; i < specs.size(); i++) {
//设置菜品id
specs.get(i).setGoodsId(goods.getGoodsId());
}
//批量插入
sysGoodsSpecsService.saveBatch(specs);
}
}
}

@ -0,0 +1,23 @@
package com.itmk.web.goods_specs.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
@Data
@TableName("sys_goods_specs")
public class SysGoodsSpecs {
// 设置主键自增
@TableId(type = IdType.AUTO)
private Long specsId;
private Long goodsId;
private String specsName;
private BigDecimal goodsPrice;
private Integer orderNum;
}

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

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

@ -0,0 +1,12 @@
package com.itmk.web.goods_specs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itmk.web.goods_specs.entity.SysGoodsSpecs;
import com.itmk.web.goods_specs.mapper.SysGoodsSpecsMapper;
import com.itmk.web.goods_specs.service.SysGoodsSpecsService;
import org.springframework.stereotype.Service;
@Service
public class SysGoodsSpecsImpl extends ServiceImpl<SysGoodsSpecsMapper, SysGoodsSpecs> implements SysGoodsSpecsService {
}

@ -0,0 +1,47 @@
package com.itmk.web.image;
import com.itmk.utils.ResultUtils;
import com.itmk.utils.ResultVo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.UUID;
@RestController
@RequestMapping("/api/upload")
public class ImageUploadController {
//图片上传的路径
@Value("${web.uploadpath}")
private String webUploadpath;
@RequestMapping("/uploadImage")
public ResultVo uploadImage(@RequestParam("file") MultipartFile file) {
String Url = null;
String fileName = file.getOriginalFilename();
//获取扩展名 aa.png
String fileExtenionName = fileName.substring(fileName.indexOf("."));
//生成新的文件名
String newName = UUID.randomUUID().toString() + fileExtenionName;
String path = webUploadpath;
File fileDir = new File(path);
if (!fileDir.exists()) {
fileDir.mkdirs();
//设置权限
fileDir.setWritable(true);
}
File targetFile = new File(path, newName);
try {
file.transferTo(targetFile);
Url = "/"+targetFile.getName();
} catch (Exception e) {
return null;
}
return ResultUtils.success("成功", "/images" + Url);
}
}

@ -18,6 +18,10 @@ spring:
test-on-borrow: true
validation-query: SELECT 1
#图片上传
web:
uploadpath: D:/images/ #图片上传的路径
load-path: file:D://images/ #图片访问、加载的路径
# MyBatis Plus配置
mybatis-plus:
configuration:

@ -17,7 +17,10 @@ spring:
max-active: 20
test-on-borrow: true
validation-query: SELECT 1
#图片上传
web:
uploadpath: D:/images/ #图片上传的路径
load-path: file:D://images/ #图片访问、加载的路径
# MyBatis Plus配置
mybatis-plus:
configuration:

@ -0,0 +1,7 @@
<!--xml接口映射文件-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itmk.web.goods.mapper.SysGoodsMapper">
</mapper>

@ -0,0 +1,7 @@
<!--xml接口映射文件-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itmk.web.goods_specs.mapper.SysGoodsSpecsMapper">
</mapper>

@ -9,6 +9,8 @@
"version": "0.0.0",
"dependencies": {
"@element-plus/icons-vue": "^2.3.2",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.11.0",
"element-plus": "^2.10.6",
"pinia": "^3.0.3",
@ -16,12 +18,12 @@
"vue-router": "^4.5.1"
},
"devDependencies": {
"@types/node": "^24.2.1",
"@vitejs/plugin-vue": "^6.0.0",
"@types/node": "^24.3.0",
"@vitejs/plugin-vue": "^6.0.1",
"@vue/tsconfig": "^0.7.0",
"sass": "^1.90.0",
"typescript": "~5.8.3",
"vite": "^7.0.4",
"typescript": "^5.9.2",
"vite": "^7.1.2",
"vue-tsc": "^2.2.12"
}
},
@ -58,6 +60,15 @@
"node": ">=6.0.0"
}
},
"node_modules/@babel/runtime": {
"version": "7.28.3",
"resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.28.3.tgz",
"integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/types": {
"version": "7.28.2",
"resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.2.tgz",
@ -1170,6 +1181,12 @@
"win32"
]
},
"node_modules/@transloadit/prettier-bytes": {
"version": "0.0.7",
"resolved": "https://registry.npmmirror.com/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz",
"integrity": "sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==",
"license": "MIT"
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
@ -1177,6 +1194,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/event-emitter": {
"version": "0.3.5",
"resolved": "https://registry.npmmirror.com/@types/event-emitter/-/event-emitter-0.3.5.tgz",
"integrity": "sha512-zx2/Gg0Eg7gwEiOIIh5w9TrhKKTeQh7CPCOPNc0el4pLSwzebA8SmnHwZs2dWlLONvyulykSwGSQxQHLhjGLvQ==",
"license": "MIT"
},
"node_modules/@types/lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.20.tgz",
@ -1193,9 +1216,9 @@
}
},
"node_modules/@types/node": {
"version": "24.2.1",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-24.2.1.tgz",
"integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
"version": "24.3.0",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-24.3.0.tgz",
"integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1208,6 +1231,61 @@
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==",
"license": "MIT"
},
"node_modules/@uppy/companion-client": {
"version": "2.2.2",
"resolved": "https://registry.npmmirror.com/@uppy/companion-client/-/companion-client-2.2.2.tgz",
"integrity": "sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==",
"license": "MIT",
"dependencies": {
"@uppy/utils": "^4.1.2",
"namespace-emitter": "^2.0.1"
}
},
"node_modules/@uppy/core": {
"version": "2.3.4",
"resolved": "https://registry.npmmirror.com/@uppy/core/-/core-2.3.4.tgz",
"integrity": "sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==",
"license": "MIT",
"dependencies": {
"@transloadit/prettier-bytes": "0.0.7",
"@uppy/store-default": "^2.1.1",
"@uppy/utils": "^4.1.3",
"lodash.throttle": "^4.1.1",
"mime-match": "^1.0.2",
"namespace-emitter": "^2.0.1",
"nanoid": "^3.1.25",
"preact": "^10.5.13"
}
},
"node_modules/@uppy/store-default": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/@uppy/store-default/-/store-default-2.1.1.tgz",
"integrity": "sha512-xnpTxvot2SeAwGwbvmJ899ASk5tYXhmZzD/aCFsXePh/v8rNvR2pKlcQUH7cF/y4baUGq3FHO/daKCok/mpKqQ==",
"license": "MIT"
},
"node_modules/@uppy/utils": {
"version": "4.1.3",
"resolved": "https://registry.npmmirror.com/@uppy/utils/-/utils-4.1.3.tgz",
"integrity": "sha512-nTuMvwWYobnJcytDO3t+D6IkVq/Qs4Xv3vyoEZ+Iaf8gegZP+rEyoaFT2CK5XLRMienPyqRqNbIfRuFaOWSIFw==",
"license": "MIT",
"dependencies": {
"lodash.throttle": "^4.1.1"
}
},
"node_modules/@uppy/xhr-upload": {
"version": "2.1.3",
"resolved": "https://registry.npmmirror.com/@uppy/xhr-upload/-/xhr-upload-2.1.3.tgz",
"integrity": "sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==",
"license": "MIT",
"dependencies": {
"@uppy/companion-client": "^2.2.2",
"@uppy/utils": "^4.1.2",
"nanoid": "^3.1.25"
},
"peerDependencies": {
"@uppy/core": "^2.3.3"
}
},
"node_modules/@vitejs/plugin-vue": {
"version": "6.0.1",
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz",
@ -1527,6 +1605,165 @@
}
}
},
"node_modules/@wangeditor/basic-modules": {
"version": "1.1.7",
"resolved": "https://registry.npmmirror.com/@wangeditor/basic-modules/-/basic-modules-1.1.7.tgz",
"integrity": "sha512-cY9CPkLJaqF05STqfpZKWG4LpxTMeGSIIF1fHvfm/mz+JXatCagjdkbxdikOuKYlxDdeqvOeBmsUBItufDLXZg==",
"license": "MIT",
"dependencies": {
"is-url": "^1.2.4"
},
"peerDependencies": {
"@wangeditor/core": "1.x",
"dom7": "^3.0.0",
"lodash.throttle": "^4.1.1",
"nanoid": "^3.2.0",
"slate": "^0.72.0",
"snabbdom": "^3.1.0"
}
},
"node_modules/@wangeditor/code-highlight": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/@wangeditor/code-highlight/-/code-highlight-1.0.3.tgz",
"integrity": "sha512-iazHwO14XpCuIWJNTQTikqUhGKyqj+dUNWJ9288Oym9M2xMVHvnsOmDU2sgUDWVy+pOLojReMPgXCsvvNlOOhw==",
"license": "MIT",
"dependencies": {
"prismjs": "^1.23.0"
},
"peerDependencies": {
"@wangeditor/core": "1.x",
"dom7": "^3.0.0",
"slate": "^0.72.0",
"snabbdom": "^3.1.0"
}
},
"node_modules/@wangeditor/core": {
"version": "1.1.19",
"resolved": "https://registry.npmmirror.com/@wangeditor/core/-/core-1.1.19.tgz",
"integrity": "sha512-KevkB47+7GhVszyYF2pKGKtCSj/YzmClsD03C3zTt+9SR2XWT5T0e3yQqg8baZpcMvkjs1D8Dv4fk8ok/UaS2Q==",
"license": "MIT",
"dependencies": {
"@types/event-emitter": "^0.3.3",
"event-emitter": "^0.3.5",
"html-void-elements": "^2.0.0",
"i18next": "^20.4.0",
"scroll-into-view-if-needed": "^2.2.28",
"slate-history": "^0.66.0"
},
"peerDependencies": {
"@uppy/core": "^2.1.1",
"@uppy/xhr-upload": "^2.0.3",
"dom7": "^3.0.0",
"is-hotkey": "^0.2.0",
"lodash.camelcase": "^4.3.0",
"lodash.clonedeep": "^4.5.0",
"lodash.debounce": "^4.0.8",
"lodash.foreach": "^4.5.0",
"lodash.isequal": "^4.5.0",
"lodash.throttle": "^4.1.1",
"lodash.toarray": "^4.4.0",
"nanoid": "^3.2.0",
"slate": "^0.72.0",
"snabbdom": "^3.1.0"
}
},
"node_modules/@wangeditor/editor": {
"version": "5.1.23",
"resolved": "https://registry.npmmirror.com/@wangeditor/editor/-/editor-5.1.23.tgz",
"integrity": "sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==",
"license": "MIT",
"dependencies": {
"@uppy/core": "^2.1.1",
"@uppy/xhr-upload": "^2.0.3",
"@wangeditor/basic-modules": "^1.1.7",
"@wangeditor/code-highlight": "^1.0.3",
"@wangeditor/core": "^1.1.19",
"@wangeditor/list-module": "^1.0.5",
"@wangeditor/table-module": "^1.1.4",
"@wangeditor/upload-image-module": "^1.0.2",
"@wangeditor/video-module": "^1.1.4",
"dom7": "^3.0.0",
"is-hotkey": "^0.2.0",
"lodash.camelcase": "^4.3.0",
"lodash.clonedeep": "^4.5.0",
"lodash.debounce": "^4.0.8",
"lodash.foreach": "^4.5.0",
"lodash.isequal": "^4.5.0",
"lodash.throttle": "^4.1.1",
"lodash.toarray": "^4.4.0",
"nanoid": "^3.2.0",
"slate": "^0.72.0",
"snabbdom": "^3.1.0"
}
},
"node_modules/@wangeditor/editor-for-vue": {
"version": "5.1.12",
"resolved": "https://registry.npmmirror.com/@wangeditor/editor-for-vue/-/editor-for-vue-5.1.12.tgz",
"integrity": "sha512-0Ds3D8I+xnpNWezAeO7HmPRgTfUxHLMd9JKcIw+QzvSmhC5xUHbpCcLU+KLmeBKTR/zffnS5GQo6qi3GhTMJWQ==",
"license": "MIT",
"peerDependencies": {
"@wangeditor/editor": ">=5.1.0",
"vue": "^3.0.5"
}
},
"node_modules/@wangeditor/list-module": {
"version": "1.0.5",
"resolved": "https://registry.npmmirror.com/@wangeditor/list-module/-/list-module-1.0.5.tgz",
"integrity": "sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ==",
"license": "MIT",
"peerDependencies": {
"@wangeditor/core": "1.x",
"dom7": "^3.0.0",
"slate": "^0.72.0",
"snabbdom": "^3.1.0"
}
},
"node_modules/@wangeditor/table-module": {
"version": "1.1.4",
"resolved": "https://registry.npmmirror.com/@wangeditor/table-module/-/table-module-1.1.4.tgz",
"integrity": "sha512-5saanU9xuEocxaemGdNi9t8MCDSucnykEC6jtuiT72kt+/Hhh4nERYx1J20OPsTCCdVr7hIyQenFD1iSRkIQ6w==",
"license": "MIT",
"peerDependencies": {
"@wangeditor/core": "1.x",
"dom7": "^3.0.0",
"lodash.isequal": "^4.5.0",
"lodash.throttle": "^4.1.1",
"nanoid": "^3.2.0",
"slate": "^0.72.0",
"snabbdom": "^3.1.0"
}
},
"node_modules/@wangeditor/upload-image-module": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/@wangeditor/upload-image-module/-/upload-image-module-1.0.2.tgz",
"integrity": "sha512-z81lk/v71OwPDYeQDxj6cVr81aDP90aFuywb8nPD6eQeECtOymrqRODjpO6VGvCVxVck8nUxBHtbxKtjgcwyiA==",
"license": "MIT",
"peerDependencies": {
"@uppy/core": "^2.0.3",
"@uppy/xhr-upload": "^2.0.3",
"@wangeditor/basic-modules": "1.x",
"@wangeditor/core": "1.x",
"dom7": "^3.0.0",
"lodash.foreach": "^4.5.0",
"slate": "^0.72.0",
"snabbdom": "^3.1.0"
}
},
"node_modules/@wangeditor/video-module": {
"version": "1.1.4",
"resolved": "https://registry.npmmirror.com/@wangeditor/video-module/-/video-module-1.1.4.tgz",
"integrity": "sha512-ZdodDPqKQrgx3IwWu4ZiQmXI8EXZ3hm2/fM6E3t5dB8tCaIGWQZhmqd6P5knfkRAd3z2+YRSRbxOGfoRSp/rLg==",
"license": "MIT",
"peerDependencies": {
"@uppy/core": "^2.1.4",
"@uppy/xhr-upload": "^2.0.7",
"@wangeditor/core": "1.x",
"dom7": "^3.0.0",
"nanoid": "^3.2.0",
"slate": "^0.72.0",
"snabbdom": "^3.1.0"
}
},
"node_modules/alien-signals": {
"version": "1.0.13",
"resolved": "https://registry.npmmirror.com/alien-signals/-/alien-signals-1.0.13.tgz",
@ -1638,6 +1875,12 @@
"node": ">= 0.8"
}
},
"node_modules/compute-scroll-into-view": {
"version": "1.0.20",
"resolved": "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz",
"integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==",
"license": "MIT"
},
"node_modules/copy-anything": {
"version": "3.0.5",
"resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-3.0.5.tgz",
@ -1659,6 +1902,19 @@
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT"
},
"node_modules/d": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/d/-/d-1.0.2.tgz",
"integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==",
"license": "ISC",
"dependencies": {
"es5-ext": "^0.10.64",
"type": "^2.7.2"
},
"engines": {
"node": ">=0.12"
}
},
"node_modules/dayjs": {
"version": "1.11.13",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz",
@ -1695,6 +1951,15 @@
"node": ">=0.10"
}
},
"node_modules/dom7": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/dom7/-/dom7-3.0.0.tgz",
"integrity": "sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==",
"license": "MIT",
"dependencies": {
"ssr-window": "^3.0.0-alpha.1"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
@ -1792,6 +2057,46 @@
"node": ">= 0.4"
}
},
"node_modules/es5-ext": {
"version": "0.10.64",
"resolved": "https://registry.npmmirror.com/es5-ext/-/es5-ext-0.10.64.tgz",
"integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==",
"hasInstallScript": true,
"license": "ISC",
"dependencies": {
"es6-iterator": "^2.0.3",
"es6-symbol": "^3.1.3",
"esniff": "^2.0.1",
"next-tick": "^1.1.0"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/es6-iterator": {
"version": "2.0.3",
"resolved": "https://registry.npmmirror.com/es6-iterator/-/es6-iterator-2.0.3.tgz",
"integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
"license": "MIT",
"dependencies": {
"d": "1",
"es5-ext": "^0.10.35",
"es6-symbol": "^3.1.1"
}
},
"node_modules/es6-symbol": {
"version": "3.1.4",
"resolved": "https://registry.npmmirror.com/es6-symbol/-/es6-symbol-3.1.4.tgz",
"integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==",
"license": "ISC",
"dependencies": {
"d": "^1.0.2",
"ext": "^1.7.0"
},
"engines": {
"node": ">=0.12"
}
},
"node_modules/esbuild": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.8.tgz",
@ -1840,12 +2145,46 @@
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"license": "MIT"
},
"node_modules/esniff": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/esniff/-/esniff-2.0.1.tgz",
"integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
"license": "ISC",
"dependencies": {
"d": "^1.0.1",
"es5-ext": "^0.10.62",
"event-emitter": "^0.3.5",
"type": "^2.7.2"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"license": "MIT"
},
"node_modules/event-emitter": {
"version": "0.3.5",
"resolved": "https://registry.npmmirror.com/event-emitter/-/event-emitter-0.3.5.tgz",
"integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
"license": "MIT",
"dependencies": {
"d": "1",
"es5-ext": "~0.10.14"
}
},
"node_modules/ext": {
"version": "1.7.0",
"resolved": "https://registry.npmmirror.com/ext/-/ext-1.7.0.tgz",
"integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
"license": "ISC",
"dependencies": {
"type": "^2.7.2"
}
},
"node_modules/fdir": {
"version": "6.4.6",
"resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.4.6.tgz",
@ -2039,6 +2378,35 @@
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
"license": "MIT"
},
"node_modules/html-void-elements": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-2.0.1.tgz",
"integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/i18next": {
"version": "20.6.1",
"resolved": "https://registry.npmmirror.com/i18next/-/i18next-20.6.1.tgz",
"integrity": "sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.12.0"
}
},
"node_modules/immer": {
"version": "9.0.21",
"resolved": "https://registry.npmmirror.com/immer/-/immer-9.0.21.tgz",
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/immutable": {
"version": "5.1.3",
"resolved": "https://registry.npmmirror.com/immutable/-/immutable-5.1.3.tgz",
@ -2071,6 +2439,12 @@
"node": ">=0.10.0"
}
},
"node_modules/is-hotkey": {
"version": "0.2.0",
"resolved": "https://registry.npmmirror.com/is-hotkey/-/is-hotkey-0.2.0.tgz",
"integrity": "sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==",
"license": "MIT"
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
@ -2082,6 +2456,21 @@
"node": ">=0.12.0"
}
},
"node_modules/is-plain-object": {
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-url": {
"version": "1.2.4",
"resolved": "https://registry.npmmirror.com/is-url/-/is-url-1.2.4.tgz",
"integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
"license": "MIT"
},
"node_modules/is-what": {
"version": "4.1.16",
"resolved": "https://registry.npmmirror.com/is-what/-/is-what-4.1.16.tgz",
@ -2117,6 +2506,49 @@
"lodash-es": "*"
}
},
"node_modules/lodash.camelcase": {
"version": "4.3.0",
"resolved": "https://registry.npmmirror.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
"license": "MIT"
},
"node_modules/lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmmirror.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
"license": "MIT"
},
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
"license": "MIT"
},
"node_modules/lodash.foreach": {
"version": "4.5.0",
"resolved": "https://registry.npmmirror.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
"integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==",
"license": "MIT"
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmmirror.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
"license": "MIT"
},
"node_modules/lodash.throttle": {
"version": "4.1.1",
"resolved": "https://registry.npmmirror.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
"integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==",
"license": "MIT"
},
"node_modules/lodash.toarray": {
"version": "4.4.0",
"resolved": "https://registry.npmmirror.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
"integrity": "sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==",
"license": "MIT"
},
"node_modules/magic-string": {
"version": "0.30.17",
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz",
@ -2179,6 +2611,15 @@
"node": ">= 0.6"
}
},
"node_modules/mime-match": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/mime-match/-/mime-match-1.0.2.tgz",
"integrity": "sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==",
"license": "ISC",
"dependencies": {
"wildcard": "^1.1.0"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
@ -2220,6 +2661,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/namespace-emitter": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/namespace-emitter/-/namespace-emitter-2.0.1.tgz",
"integrity": "sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==",
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
@ -2238,6 +2685,12 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/next-tick": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/next-tick/-/next-tick-1.1.0.tgz",
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==",
"license": "ISC"
},
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz",
@ -2342,6 +2795,25 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/preact": {
"version": "10.27.0",
"resolved": "https://registry.npmmirror.com/preact/-/preact-10.27.0.tgz",
"integrity": "sha512-/DTYoB6mwwgPytiqQTh/7SFRL98ZdiD8Sk8zIUVOxtwq4oWcwrcd1uno9fE/zZmUaUrFNYzbH14CPebOz9tZQw==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/prismjs": {
"version": "1.30.0",
"resolved": "https://registry.npmmirror.com/prismjs/-/prismjs-1.30.0.tgz",
"integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@ -2429,6 +2901,47 @@
"@parcel/watcher": "^2.4.1"
}
},
"node_modules/scroll-into-view-if-needed": {
"version": "2.2.31",
"resolved": "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz",
"integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==",
"license": "MIT",
"dependencies": {
"compute-scroll-into-view": "^1.0.20"
}
},
"node_modules/slate": {
"version": "0.72.8",
"resolved": "https://registry.npmmirror.com/slate/-/slate-0.72.8.tgz",
"integrity": "sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==",
"license": "MIT",
"dependencies": {
"immer": "^9.0.6",
"is-plain-object": "^5.0.0",
"tiny-warning": "^1.0.3"
}
},
"node_modules/slate-history": {
"version": "0.66.0",
"resolved": "https://registry.npmmirror.com/slate-history/-/slate-history-0.66.0.tgz",
"integrity": "sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==",
"license": "MIT",
"dependencies": {
"is-plain-object": "^5.0.0"
},
"peerDependencies": {
"slate": ">=0.65.3"
}
},
"node_modules/snabbdom": {
"version": "3.6.2",
"resolved": "https://registry.npmmirror.com/snabbdom/-/snabbdom-3.6.2.tgz",
"integrity": "sha512-ig5qOnCDbugFntKi6c7Xlib8bA6xiJVk8O+WdFrV3wxbMqeHO0hXFQC4nAhPVWfZfi8255lcZkNhtIBINCc4+Q==",
"license": "MIT",
"engines": {
"node": ">=12.17.0"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
@ -2447,6 +2960,12 @@
"node": ">=0.10.0"
}
},
"node_modules/ssr-window": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/ssr-window/-/ssr-window-3.0.0.tgz",
"integrity": "sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==",
"license": "MIT"
},
"node_modules/superjson": {
"version": "2.2.2",
"resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.2.tgz",
@ -2459,6 +2978,12 @@
"node": ">=16"
}
},
"node_modules/tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
"license": "MIT"
},
"node_modules/tinyglobby": {
"version": "0.2.14",
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.14.tgz",
@ -2490,10 +3015,16 @@
"node": ">=8.0"
}
},
"node_modules/type": {
"version": "2.7.3",
"resolved": "https://registry.npmmirror.com/type/-/type-2.7.3.tgz",
"integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==",
"license": "ISC"
},
"node_modules/typescript": {
"version": "5.8.3",
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"version": "5.9.2",
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.2.tgz",
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
"devOptional": true,
"license": "Apache-2.0",
"bin": {
@ -2512,9 +3043,9 @@
"license": "MIT"
},
"node_modules/vite": {
"version": "7.0.6",
"resolved": "https://registry.npmmirror.com/vite/-/vite-7.0.6.tgz",
"integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==",
"version": "7.1.2",
"resolved": "https://registry.npmmirror.com/vite/-/vite-7.1.2.tgz",
"integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2522,7 +3053,7 @@
"fdir": "^6.4.6",
"picomatch": "^4.0.3",
"postcss": "^8.5.6",
"rollup": "^4.40.0",
"rollup": "^4.43.0",
"tinyglobby": "^0.2.14"
},
"bin": {
@ -2645,6 +3176,12 @@
"peerDependencies": {
"typescript": ">=5.0.0"
}
},
"node_modules/wildcard": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/wildcard/-/wildcard-1.1.2.tgz",
"integrity": "sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==",
"license": "MIT"
}
}
}

@ -10,6 +10,8 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.2",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.11.0",
"element-plus": "^2.10.6",
"pinia": "^3.0.3",
@ -17,12 +19,12 @@
"vue-router": "^4.5.1"
},
"devDependencies": {
"@types/node": "^24.2.1",
"@vitejs/plugin-vue": "^6.0.0",
"@types/node": "^24.3.0",
"@vitejs/plugin-vue": "^6.0.1",
"@vue/tsconfig": "^0.7.0",
"sass": "^1.90.0",
"typescript": "~5.8.3",
"vite": "^7.0.4",
"typescript": "^5.9.2",
"vite": "^7.1.2",
"vue-tsc": "^2.2.12"
}
}

@ -4,7 +4,7 @@
export type listCategoryParm = {
currentPage: number;//当前页
pageSize: number;//每页显示多少条
categoryName: string;//根据菜品名查询
categoryName: string;//根据菜品分类名查询
total: number;//总条数
}

@ -26,4 +26,12 @@ export const editCategoryApi = (parm: categoryModel) => {
export const getCategoryApi = (parm: listCategoryParm) => {
// 查需要找到列表,把列表里面的数据返回出来
return http.get('/api/category/list', parm)
}
// 下拉列表
export const getSelectApi = () => {
// 查需要找到列表,把列表里面的数据返回出来
console.log("下拉api已调用")
return http.get('/api/category/getSelectList')
}

@ -0,0 +1,31 @@
// 列表参数类型
export type ListGoodsParm = {
currentPage: number;//当前页
pageSize: number;//每页显示多少条
goodsName: string;//根据菜品名查询
total: number;//总条数
}
// 定义菜品数据类型
export type goodsModel = {
goodsId: string;
goodsName: string;
orderNum: string;
type: string;
categoryId: string;
categoryName?: string;
goodsImage: string;
goodsDesc: string;
status: string;
goodsUnit: string;
specs: Array<SpecsType>;
}
// 定义菜品规格类型
export type SpecsType = {
specsId?: string;
goodsId?: string
specsName: string
goodsPrice: string
orderNum: string
}

@ -0,0 +1,29 @@
// 配置接口
import http from "../../http";
// 导入数据类型的定义
import type { ListGoodsParm, goodsModel } from "./goodsModel";
// 增(传入goodsModel参数用来接收需要传入该表单的数据类型)
export const addGoodsApi = (parm: goodsModel) => {
// 返回http中的post请求传入路径和数据参数通过请求接入后端接口
return http.post('/api/goods', parm)
}
// 删
export const deleteGoodsApi = (goodsId: string) => {
return http.delete(`/api/goods/${goodsId}`)
}
// 改
export const editGoodsApi = (parm: goodsModel) => {
return http.put('/api/goods', parm)
}
// 查(传入的参数是列表参数,包括分页,查询等属性)
export const getGoodsApi = (parm: ListGoodsParm) => {
// 查需要找到列表,把列表里面的数据返回出来
return http.get('/api/goods/list', parm)
}

@ -0,0 +1,5 @@
import http from "../../http";
//图片上传
export const uploadImageApi = (parm:object)=>{
return http.upload("/api/upload/uploadImage",parm)
}

@ -0,0 +1,85 @@
<template>
<el-upload ref="uploadRef" action="#" :on-change="uploadFile" list-type="picture-card" :auto-upload="false"
:file-list="fileList" :show-file-list="true" :limit="3" :on-remove="handleRemove" :on-exceed="moreLimit">
<el-icon>
<Plus />
</el-icon>
</el-upload>
</template>
<script setup lang="ts">
declare const __APP_BASE_URL__: string;
import { Plus } from "@element-plus/icons-vue";
import type {
UploadFile,
UploadUserFile,
UploadFiles
} from "element-plus";
import { ref } from "vue";
import { ElMessage } from "element-plus";
import { uploadImageApi } from "../api/img/index";
import type { NewType } from "../type/baseType"
//ref
const uploadRef = ref();
//
const emits = defineEmits(["getImg"]);
const newImgRes = ref<NewType>({
newImgUrl: [],
deleteImgUrl: [],
});
//
type PropType = {
fileList: UploadUserFile[];
oldUrl: Array<{ url: string }>;
};
const props = withDefaults(defineProps<PropType>(), {
fileList: () => [],
oldUrl: () => [],
});
//
const handleRemove = (file: UploadFile) => {
if (props.oldUrl.some(item => item.url === file.name)) {
newImgRes.value.deleteImgUrl.push({ url: file.name })
emits("getImg", newImgRes.value);
} else {
let images = newImgRes.value.newImgUrl.filter((item) => item.url != file.name);
newImgRes.value.newImgUrl = images;
emits("getImg", newImgRes.value);
}
};
//
const moreLimit = (files: File[], uploadFiles: UploadUserFile[]) => {
ElMessage.warning("最多只能上传" + uploadFiles.length + "张图片!");
};
//
const uploadFile = async (file: any) => {
//
const typeArr = ["image/png", "image/gif", "image/jpeg", "image/jpg"];
const isImg = typeArr.indexOf(file.raw.type) !== -1;
const isMore3M = file.size / 1024 / 1024 < 3;
if (!isImg) {
ElMessage.warning("只能上传图片类型!");
uploadRef.value?.clearFiles();
return;
}
if (!isMore3M) {
ElMessage.warning("图片大小不能超过3M!");
uploadRef.value?.clearFiles();
return;
}
//
const formData = new FormData();
formData.append("file", file.raw);
let res = await uploadImageApi(formData);
if (res && res.code == 200 && res.data) {
ElMessage.success("图片上传成功!");
const baseUrl = __APP_BASE_URL__;
file.name = baseUrl + res.data;
newImgRes.value.newImgUrl.push({ url: baseUrl + res.data });
emits("getImg", newImgRes.value);
}
};
</script>
<style scoped></style>

@ -0,0 +1,47 @@
import { onBeforeUnmount, ref, shallowRef, onMounted } from 'vue'
import type { IEditorConfig } from '@wangeditor/editor'
declare const __APP_BASE_URL__: string;
export default function useEditor() {
type InsertFnType = (url: string, alt?: string, href?: string) => void
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()
// 内容 HTML
const valueHtml = ref('')
const toolbarConfig = {}
// const editorConfig = { placeholder: '请输入内容...' }
const editorConfig: Partial<IEditorConfig> = { MENU_CONF: {}, placeholder: '请输入内容...' }
const mode = ref('default') // 或 'simple'
// 上传图片的配置
const baseUrl = __APP_BASE_URL__;
editorConfig.MENU_CONF!['uploadImage'] = {
// form-data fieldName ,默认值 'wangeditor-uploaded-image'
fieldName: 'file',
//上传图片后端地址
server: baseUrl + '/api/upload/uploadImage',
// 自定义插入图片
customInsert(res: any, insertFn: InsertFnType) {
// res 即服务端的返回结果
console.log(res)
// 从 res 中找到 url alt href ,然后插图图片
insertFn(baseUrl + res.data)
},
}
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
const handleCreated = (editor: any) => {
editorRef.value = editor // 记录 editor 实例,重要!
}
return {
editorRef,
valueHtml,
toolbarConfig,
editorConfig,
handleCreated,
mode
}
}

@ -0,0 +1,50 @@
// 抽取增删改业务
import { ref } from "vue";
import type { FuncList } from "../../type/baseType";
import type { goodsModel } from "../../api/goods/goodsModel";
import { EditType } from "../../type/baseType";
import { deleteGoodsApi } from "../../api/goods";
import { ElMessage } from "element-plus";
import useInstance from "@/hooks/useInstance";
export default function useGoods(getList: FuncList) {
const { global } = useInstance()
// 获取AddGoods里面的show方法
const showBtn = ref<{ show: (title: string, type: string, width?: number, height?: number, row?: goodsModel) => {} }>();
// 增
const addBtn = () => {
showBtn.value?.show("新增", EditType.ADD, 900, 500)
}
// 删
const deleteBtn = async (row: goodsModel) => {
let confirm = await global.$myconfirm('确定删除该数据吗?')
// 判断confirm返回true用户确定删除就进行删除调用删除api
if (confirm) {
let res = await deleteGoodsApi(row.goodsId)
if (res && res.code == 200) {
// 删除成功信息提示
ElMessage.success(res.msg)
// 删除后刷新列表
getList()
}
}
}
// 改
// 传递row参数将旧数据传递出去方便编辑
const editBtn = (row: goodsModel) => {
console.log(row)
showBtn.value?.show("编辑", EditType.EDIT, 900, 500, row)
}
return {
showBtn,
addBtn,
deleteBtn,
editBtn
}
}

@ -0,0 +1,77 @@
import { getGoodsApi } from "../../api/goods";
import type { ListGoodsParm } from "../../api/goods/goodsModel";
import { nextTick, onMounted, reactive, ref } from "vue";
export default function useGoodsTable() {
// 设置表格高度(默认值设置为0)
const tableHeight = ref(0)
// 列表查询参数
const listParm = reactive<ListGoodsParm>({
currentPage: 1,
pageSize: 10,
goodsName: '',
total: 0
})
// 定义tableList用来接收列表数据
const tableList = ref([])
// 列表
const getList = async () => {
// 在getList方法中调用getGoodsApi
let res = await getGoodsApi(listParm)
console.log(res)
if (res && res.code == 200) {
tableList.value = res.data.records;
listParm.total = res.data.total;
}
}
// 搜索按钮
const searchBtn = () => {
getList()
}
// 重置按钮
const resetBtn = () => {
listParm.currentPage = 1;
listParm.goodsName = ''
getList()
}
// 在组件挂载时加载数据
onMounted(() => {
getList()
// 计算表格高度覆盖默认值
nextTick(() => {
tableHeight.value = window.innerHeight - 220
})
})
// 定义页容量改变方法
const sizeChange = (size: number) => {
listParm.pageSize = size
getList()
}
// 定义当前页数改变方法
const currentChange = (page: number) => {
listParm.currentPage = page
getList()
}
// 将表格查询的方法返回出去
return {
listParm,
getList,
searchBtn,
resetBtn,
tableList,
sizeChange,
currentChange,
tableHeight
}
}

@ -0,0 +1,56 @@
import { ref } from "vue";
import { getSelectApi } from "../../api/category";
// 定义下拉选项的类型
export interface SelectOption {
value: string | number;
label: string;
}
export default function useSelectCategory() {
// 下拉数据
const selectData = ref<SelectOption[]>([])
// 获取下拉数据
const getSelect = async () => {
try {
let res = await getSelectApi();
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
}
}

@ -121,24 +121,33 @@ class Http {
}
// get请求
get<T = Result> (url: string, params?: object): Promise<T> {
get<T = Result>(url: string, params?: object): Promise<T> {
return this.instance.get(url, { params })
}
// post请求
post<T = Result> (url: string, data?: object): Promise<T> {
post<T = Result>(url: string, data?: object): Promise<T> {
return this.instance.post(url, data)
}
// put请求
put<T = Result> (url: string, data?: object): Promise<T> {
put<T = Result>(url: string, data?: object): Promise<T> {
return this.instance.put(url, data)
}
// delete请求
delete<T = Result> (url: string): Promise<T> {
delete<T = Result>(url: string): Promise<T> {
return this.instance.delete(url)
}
//图片上传
upload<T = Result>(url: string, params?: object): Promise<T> {
return this.instance.post(url, params, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
}
// 导出

@ -5,4 +5,10 @@ export type FuncList = () => any
export enum EditType{
ADD = '0', //新增
EDIT = '1' //编辑
}
// 图片上传数据类型
export type NewType = {
newImgUrl:Array<{url:string}>;
deleteImgUrl:Array<{url:string}>;
}

@ -0,0 +1,424 @@
<template>
<SysDialog :title="dialog.title" :visible="dialog.visible" :width="dialog.width" :height="dialog.height"
@onClose="onClose" @onConfirm="commit">
<!-- 通过插槽名称输入内容 -->
<template #content>
<el-form :model="addModel" ref="addFormRef" :rules="rules" label-width="80px" :inline="false" size="default">
<el-row :gutter="20">
<el-col :span="12" :offset="0">
<el-form-item prop="categoryId" label="菜品分类">
<el-select v-model="addModel.categoryId" placeholder="请选择菜品分类" style="width: 100%">
<el-option v-for="item in selectData" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" :offset="0">
<el-form-item prop="goodsName" label="菜品名称">
<el-input v-model="addModel.goodsName"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12" :offset="0">
<el-form-item prop="orderNum" label="菜品序号">
<el-input v-model="addModel.orderNum"></el-input>
</el-form-item>
</el-col>
<el-col :span="12" :offset="0">
<el-form-item prop="status" label="是否热推">
<el-radio-group v-model="addModel.status">
<el-radio :value="'1'"></el-radio>
<el-radio :value="'0'"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-form-item prop="specs" label="菜品规格">
<el-row>
<!-- <el-col :span="12" :offset="0">
<el-divider content-position="left">菜品规格</el-divider>
</el-col> -->
<el-col style="display: flex; align-items: center" :span="12" :offset="0">
<el-button type="primary" plain style="margin-left: 20px" :icon="Plus" size="small"
@click="addSpecs">添加规格</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item v-for="num in addModel.specs.length" size="small" :key="num">
<el-row>
<el-col :span="7" :offset="0">
<el-form-item :label="'名称' + num" size="small">
<el-input v-model="addModel.specs[num - 1].specsName"></el-input>
</el-form-item>
</el-col>
<el-col :span="7" :offset="0">
<el-form-item :label="'价格' + num" size="small">
<el-input v-model="addModel.specs[num - 1].goodsPrice"></el-input>
</el-form-item>
</el-col>
<el-col :span="7" :offset="0">
<el-form-item :label="'序号' + num" size="small">
<el-input v-model="addModel.specs[num - 1].orderNum"></el-input>
</el-form-item>
</el-col>
<el-col :span="3" :offset="0">
<el-button @click="deleteSpecs(num)" style="margin-left: 15px" :icon="Close" type="danger" plain circle
size="small"></el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item prop="goodsUnit" label="菜品单位">
<el-input v-model="addModel.goodsUnit"></el-input>
</el-form-item>
<el-form-item prop="goodsImage" label="上传菜品图片">
<UploadImage @getImg="getImg" :fileList="fileList" :oldUrl="oldUrl"></UploadImage>
</el-form-item>
<el-form-item prop="goodsDesc" label="菜品详情">
<div style="border: 1px solid #ccc">
<Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig"
:mode="mode" />
<Editor v-if="dialog.visible" style="height: 250px; overflow-y: hidden" v-model="valueHtml" :defaultConfig="editorConfig"
:mode="mode" @onCreated="handleCreated" />
</div>
</el-form-item>
</el-form>
</template>
</SysDialog>
</template>
<script setup lang="ts">
import { Plus, Close } from '@element-plus/icons-vue';
//
import '@wangeditor/editor/dist/css/style.css' // css
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import useEditor from '@/compositions/goods/useEditor';
const { editorRef,
valueHtml,
toolbarConfig,
editorConfig,
handleCreated,
mode } = useEditor()
//
import SysDialog from '@/components/SysDialog.vue';
//
import useDialog from '@/hooks/useDialog';
import { ref, reactive, nextTick, watch, toRefs, computed } from 'vue';
//
import UploadImage from '@/components/UploadImage.vue';
// GoodsModel
import type { goodsModel, SpecsType } from '../../api/goods/goodsModel';
import { ElMessage, type UploadUserFile, type FormInstance } from 'element-plus';
// post
import { addGoodsApi, editGoodsApi } from '../../api/goods/index';
import { EditType, type NewType } from '../../type/baseType';
//
const { dialog, onClose, onConfirm, onShow } = useDialog()
//
import useSelectCategory from '@/compositions/goods/useSelectCategory';
import type SelectOption from '@/compositions/goods/useSelectCategory';
const selectCategory = useSelectCategory();
const { selectData } = toRefs(selectCategory); // selectData
const getSelect = selectCategory.getSelect; //
//
const addModel = reactive<goodsModel>({
type: "",
goodsId: "",
categoryId: "",
categoryName: "",
goodsName: "",
goodsImage: "",
goodsDesc: "",
status: "",
goodsUnit: "",
orderNum: "",
specs: [],
});
//
const selectLoading = ref(true);
const selectError = ref(false);
//
const resetEditor = () => {
valueHtml.value = '';
if (editorRef.value) {
editorRef.value.clear();
editorRef.value.destroy(); //
}
};
//
const show = async (title: string, type: string, width: number, height: number, row?: goodsModel) => {
try {
//
selectLoading.value = true;
selectError.value = false;
//
resetEditor();
//
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 = [];
//
if (editorRef.value) {
editorRef.value.clear();
}
//
if (type == EditType.EDIT && row) {
//
const categoryId = row.categoryId.toString();
//
const selectedCategory = selectData.value.find(
item => item.value === categoryId
);
//
const editData = {
...row,
categoryId: categoryId,
categoryName: selectedCategory ? selectedCategory.label : ""
};
nextTick(() => {
Object.assign(addModel, editData);
//
if (addModel.goodsImage) {
let imgs = addModel.goodsImage.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] });
}
}
//
valueHtml.value = addModel.goodsDesc;
});
} else {
nextTick(() => {
//
addModel.specs = [];
//
addSpecs();
valueHtml.value = '';
});
}
//
onShow(title, 960, 560);
addModel.type = type;
} catch (error) {
console.error("加载下拉数据失败:", error);
ElMessage.error("加载分类数据失败");
}
};
//ref
const addFormRef = ref<FormInstance>();
//
const fileList = ref<Array<UploadUserFile>>([]);
//
const oldUrl = ref<Array<{ url: string }>>([]);
//
const imgUrl = ref<Array<{ url: string }>>([]);
//使
defineExpose({
show,
});
const checkEdit = (rule: any, value: any, callback: any) => {
if (editorRef.value.getText().length == 0) {
callback(new Error("请填写菜品详情"));
} else {
callback();
}
};
//
const checkSpecs = (rule: any, value: any, callback: any) => {
if (addModel.specs.length == 0) {
callback(new Error("请填写规格"));
} else {
let tag = false;
addModel.specs.forEach((item: SpecsType) => {
if (!item.specsName) {
tag = false;
callback(new Error("请填写规格名称"));
} else if (!item.goodsPrice) {
tag = false;
callback(new Error("请填写规格价格"));
} else if (!item.orderNum) {
tag = false;
callback(new Error("请填写规格序号"));
} else {
tag = true;
}
});
if (tag) {
callback();
}
}
};
//
const rules = reactive({
categoryId: [
{
required: true,
message: "请选择分类",
trigger: "blur",
},
],
goodsName: [
{
required: true,
message: "请填写菜品名称",
trigger: "blur",
},
],
goodsImage: [
{
required: true,
message: "请上传菜品图片",
trigger: "blur",
},
],
goodsUnit: [
{
required: true,
message: "请填写单位",
trigger: "blur",
},
],
orderNum: [
{
required: true,
message: "请填写序号",
trigger: "blur",
},
],
status: [
{
required: true,
message: "请选择是否是热推",
trigger: "blur",
},
],
goodsDesc: [
{
trigger: "blur",
validator: checkEdit,
required: true,
},
],
specs: [
{
required: true,
validator: checkSpecs,
trigger: "blur",
},
],
});
//
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.goodsImage = url.substring(0, url.lastIndexOf(","))
console.log(addModel.goodsImage)
};
//
watch(
() => valueHtml.value,
(value) => {
console.log(value)
addModel.goodsDesc = value
}
)
//
const addSpecs = () => {
addModel.specs.push({
goodsPrice: "",
specsName: "",
orderNum: "",
});
};
//
const deleteSpecs = (num: number) => {
addModel.specs.splice(num - 1, 1);
};
//
const emits = defineEmits(['onFresh'])
//
const commit = () => {
console.log(addModel);
addFormRef.value?.validate(async (valid) => {
if (valid) {
let res = null;
if (addModel.type == EditType.ADD) {
res = await addGoodsApi(addModel);
} else {
res = await editGoodsApi(addModel);
}
if (res && res.code == 200) {
ElMessage.success(res.msg);
emits('onFresh')
onConfirm()
}
}
});
};
</script>
<style></style>

@ -1,13 +1,82 @@
<template>
<div>
菜品管理
</div>
<el-main>
<!-- 搜索栏 -->
<el-form :model="listParm" label-width="80px" :inline="true" size="default">
<el-form-item>
<el-input v-model="listParm.goodsName" placeholder="请输入菜品名称:"></el-input>
</el-form-item>
<el-form-item>
<el-button :icon="Search" @click="searchBtn"></el-button>
<el-button plain :icon="Close" type="danger" @click="resetBtn"></el-button>
<el-button plain type="primary" :icon="Plus" @click="addBtn"></el-button>
</el-form-item>
</el-form>
<!-- 表格 -->
<el-table row-key="goodsId" :height="tableHeight" :data="tableList" border stripe>
<el-table-column type="expand">
<template #default="scope">
<el-table :data="scope.row.specs" border stripe>
<el-table-column label="名称" prop="specsName"></el-table-column>
<el-table-column label="价格" prop="goodsPrice"></el-table-column>
<el-table-column label="序号" prop="orderNum"></el-table-column>
</el-table>
</template>
</el-table-column>
<el-table-column label="菜品图片" prop="goodsImage">
<template #default="scope">
<el-image :src="scope.row.goodsImage.split(',')[0]"
style="height:60px;width:60px;border-radius:50%;"></el-image>
</template>
</el-table-column>
<el-table-column label="菜品名称" prop="goodsName"></el-table-column>
<el-table-column label="菜品单位" prop="goodsUnit"></el-table-column>
<el-table-column label="菜品序号" prop="orderNum"></el-table-column>
<el-table-column label="是否热推" prop="status">
<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="操作" width="200" align="center">
<template #default="scoped">
<el-button type="primary" :icon="Edit" size="default" @click="editBtn(scoped.row)"></el-button>
<el-button type="danger" :icon="Delete" 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>
<!-- 新增编辑按钮弹框 -->
<AddGoods ref="showBtn" @onFresh="getList"></AddGoods>
</template>
<script setup lang="ts">
import useGoodsTable from '@/compositions/goods/useGoodsTable';
const { listParm,
getList,
searchBtn,
resetBtn,
tableList,
sizeChange,
currentChange,
tableHeight } = useGoodsTable()
</script>
import { Search, Close, Plus } from '@element-plus/icons-vue'
// useGoods,
import useGoods from '@/compositions/goods/useGoods';
<style scoped>
const { showBtn, addBtn, deleteBtn, editBtn } = useGoods(getList)
// AddGoods,
import AddGoods from './AddGoods.vue';
import { Edit, Delete } from '@element-plus/icons-vue';
</script>
</style>
<style scoped></style>

@ -18,6 +18,13 @@ interface PromiseConstructor {
allSettled<T>(values: Iterable<T | PromiseLike<T>>): Promise<PromiseSettledResult<T>[]>;
}
// 添加 wangEditor 声明
declare module '@wangeditor/editor-for-vue' {
import { DefineComponent } from 'vue';
export const Editor: DefineComponent;
export const Toolbar: DefineComponent;
}
// 添加全局 Promise 声明
declare var Promise: PromiseConstructor;
@ -36,7 +43,7 @@ interface ObjectConstructor {
// 声明 Vue 文件模块
declare module '*.vue' {
import type { DefineComponent } from 'vue'
import { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
@ -91,4 +98,4 @@ declare module '@/api/user/userModel' {
sex: string;
name: string;
}
}
}

@ -1,7 +1,6 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
//
"baseUrl": ".",
@ -9,28 +8,36 @@
"@/*": [
"src/*"
],
"@/api/*": ["src/api/*"]
"@/api/*": [
"src/api/*"
]
},
//
"lib": ["ES2020", "ES2015.Promise", "DOM", "DOM.Iterable", "ESNext"],
"lib": [
"ESNext",
"DOM",
"DOM.Iterable"
],
"types": [
"vite/client"
],
//
"module": "ESNext",
"moduleResolution": "Node",
"moduleResolution": "bundler",
//
"target": "ESNext",
"esModuleInterop": true,
"resolveJsonModule": true,
"strict": true,
"jsx": "preserve",
"downlevelIteration": true,
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"vite-env.d.ts", //
"vite-env.d.ts",
"vite.config.ts",
"src/api/**/*.ts"
]
}

@ -3,5 +3,8 @@
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
],
"compilerOptions": {
"types": ["vite/client"] //
}
}

@ -5,10 +5,10 @@
"lib": [
"ES2023"
],
"module": "ESNext",
"module": "Node16",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"moduleResolution": "NodeNext",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
@ -19,7 +19,8 @@
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
"noUncheckedSideEffectImports": true,
"types": ["node", "vite/client"]
},
"include": [
"vite.config.ts"

@ -1,40 +1,55 @@
import { defineConfig } from 'vite'
/// <reference types="vite/client" />
/// <reference types="@vitejs/plugin-vue" />
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path' //导入nodejs核心模块path解构提取resolce方法
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
server: {
host: '0.0.0.0',//解决控制台Network:use--host to expose
port: 8080,//配置端口号
hmr: true,//开启热更新
open: true,//启动在浏览器打开
// 跨域配置,所有api的请求都会被转接到后端接口
proxy: {
'/api': { // 这里也匹配 /api 路径
target: 'http://localhost:8089',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
export default defineConfig(({ mode }) => {
console.log('加载的环境变量:', loadEnv(mode, process.cwd()))
return {
define: {
// 替换为全局变量
__APP_BASE_URL__: JSON.stringify("http://localhost:8089")
},
plugins: [vue()],
server: {
host: '0.0.0.0',//解决控制台Network:use--host to expose
port: 8080,//配置端口号
hmr: true,//开启热更新
open: true,//启动在浏览器打开
// 跨域配置,所有api的请求都会被转接到后端接口
proxy: {
'/api': { // 这里也匹配 /api 路径
target: 'http://localhost:8089',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
},
// resolve: {
// //为项目设置别名,简化项目路径
// alias: [
// {
// find: '@',
// replacement: resolve(__dirname, 'src')
// }
// ]
// }
resolve: {
// 修正为对象格式
alias: {
'@': path.resolve(__dirname, 'src'),
// 可以添加更多别名
'components': path.resolve(__dirname, 'src/components')
},
// 添加扩展名支持
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
// resolve: {
// //为项目设置别名,简化项目路径
// alias: [
// {
// find: '@',
// replacement: resolve(__dirname, 'src')
// }
// ]
// }
resolve: {
// 修正为对象格式
alias: {
'@': path.resolve(__dirname, 'src'),
// 可以添加更多别名
'components': path.resolve(__dirname, 'src/components')
},
// 添加扩展名支持
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
},
build: {
chunkSizeWarningLimit: 1500
}
}
})

Loading…
Cancel
Save