批量上传
@ -0,0 +1,100 @@
|
||||
package io.modules.sys.controller;
|
||||
|
||||
|
||||
import io.common.annotation.LogOperation;
|
||||
import io.common.constant.Constant;
|
||||
import io.common.page.PageData;
|
||||
import io.common.utils.ExcelUtils;
|
||||
import io.common.utils.Result;
|
||||
import io.common.validator.AssertUtils;
|
||||
import io.common.validator.ValidatorUtils;
|
||||
import io.common.validator.group.AddGroup;
|
||||
import io.common.validator.group.DefaultGroup;
|
||||
import io.common.validator.group.UpdateGroup;
|
||||
import io.modules.item.dto.CategoriesDTO;
|
||||
import io.modules.item.service.CategoriesService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* 商品分类表
|
||||
*
|
||||
* @author Mark #
|
||||
* @since 1.0.0 2025-02-13
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("sys/categories")
|
||||
@Tag(name="商品分类表")
|
||||
public class CategoriesController {
|
||||
@Autowired
|
||||
private CategoriesService categoriesService;
|
||||
|
||||
@GetMapping("page")
|
||||
@Operation(summary = "分页")
|
||||
@Parameters({
|
||||
@Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref="int") ,
|
||||
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY,required = true, ref="int") ,
|
||||
@Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref="String") ,
|
||||
@Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref="String")
|
||||
})
|
||||
public Result<PageData<CategoriesDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params){
|
||||
PageData<CategoriesDTO> page = categoriesService.page(params);
|
||||
return new Result<PageData<CategoriesDTO>>().ok(page);
|
||||
}
|
||||
|
||||
@GetMapping("{id}")
|
||||
|
||||
public Result<CategoriesDTO> get(@PathVariable("id") Long id){
|
||||
CategoriesDTO data = categoriesService.get(id);
|
||||
return new Result<CategoriesDTO>().ok(data);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "保存")
|
||||
|
||||
public Result save(@RequestBody CategoriesDTO dto){
|
||||
//效验数据
|
||||
ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class);
|
||||
|
||||
categoriesService.save(dto);
|
||||
|
||||
return new Result();
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@Operation(summary = "修改")
|
||||
|
||||
public Result update(@RequestBody CategoriesDTO dto){
|
||||
//效验数据
|
||||
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
|
||||
|
||||
categoriesService.update(dto);
|
||||
|
||||
return new Result();
|
||||
}
|
||||
|
||||
@DeleteMapping("{id}")
|
||||
@Operation(summary = "删除")
|
||||
@LogOperation("删除")
|
||||
public Result delete(@PathVariable Long id){
|
||||
|
||||
Long[] ids = new Long[] { id };
|
||||
//效验数据
|
||||
AssertUtils.isArrayEmpty(ids, "id");
|
||||
categoriesService.delete(ids);
|
||||
return new Result();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -73,8 +73,8 @@ public class CommentController {
|
||||
Long itemId = comment.getItemId();
|
||||
ItemDTO itemDTO = itemService.get(itemId);
|
||||
if (itemDTO != null){
|
||||
itemDTO.setLargePic(uploadUrl + "item/" + itemDTO.getLargePic());
|
||||
itemDTO.setNormalPic(uploadUrl + "item/n_" + itemDTO.getNormalPic());
|
||||
// itemDTO.setLargePic(uploadUrl + "item/" + itemDTO.getLargePic());
|
||||
// itemDTO.setNormalPic(uploadUrl + "item/n_" + itemDTO.getNormalPic());
|
||||
}
|
||||
comment.setItemDTO(itemDTO);
|
||||
return comment;
|
||||
|
@ -10,7 +10,9 @@ import io.common.validator.ValidatorUtils;
|
||||
import io.common.validator.group.AddGroup;
|
||||
import io.common.validator.group.DefaultGroup;
|
||||
import io.common.validator.group.UpdateGroup;
|
||||
import io.modules.item.dto.CategoriesDTO;
|
||||
import io.modules.item.dto.ItemDTO;
|
||||
import io.modules.item.service.CategoriesService;
|
||||
import io.modules.item.service.ItemService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
@ -20,6 +22,7 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
@ -30,41 +33,49 @@ import java.util.stream.Collectors;
|
||||
@CrossOrigin
|
||||
@RestController
|
||||
@RequestMapping("sys/item")
|
||||
@Tag(name="电影表")
|
||||
@Tag(name = "电影表")
|
||||
public class ItemController {
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
|
||||
@Autowired
|
||||
private CategoriesService categoriesService;
|
||||
|
||||
@Value("${upload.url}")
|
||||
private String uploadUrl;
|
||||
|
||||
@GetMapping("query")
|
||||
@Operation(summary = "首页顶部展示")
|
||||
public Result< List<Object>> page(){
|
||||
public Result<List<Object>> page() {
|
||||
List<Object> res = itemService.query();
|
||||
return new Result< List<Object>>().ok(res);
|
||||
return new Result<List<Object>>().ok(res);
|
||||
}
|
||||
|
||||
@GetMapping("view")
|
||||
@Operation(summary = "首页分析")
|
||||
public Result< List<Map<String, Object>>> view(){
|
||||
List<Map<String, Object>>res = itemService.view();
|
||||
return new Result< List<Map<String, Object>>>().ok(res);
|
||||
public Result<List<Map<String, Object>>> view() {
|
||||
List<Map<String, Object>> res = itemService.view();
|
||||
return new Result<List<Map<String, Object>>>().ok(res);
|
||||
}
|
||||
|
||||
@GetMapping("page")
|
||||
@Operation(summary = "分页")
|
||||
@Parameters({
|
||||
@Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref="int") ,
|
||||
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY,required = true, ref="int") ,
|
||||
@Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref="String") ,
|
||||
@Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref="String")
|
||||
@Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"),
|
||||
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"),
|
||||
@Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"),
|
||||
@Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"),
|
||||
@Parameter(name = "status", description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String")
|
||||
})
|
||||
public Result<PageData<ItemDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params){
|
||||
public Result<PageData<ItemDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
|
||||
|
||||
System.out.println(params);
|
||||
PageData<ItemDTO> page = itemService.page(params);
|
||||
List<ItemDTO> list = page.getList().stream().map(e -> {
|
||||
e.setLargePic(uploadUrl + "item/l_" + e.getLargePic());
|
||||
e.setNormalPic(uploadUrl + "item/n_" + e.getNormalPic());
|
||||
e.setImage(uploadUrl + e.getImage());
|
||||
CategoriesDTO categoriesDTO = categoriesService.get(e.getCategoryId());
|
||||
e.setCategoryName(categoriesDTO.getName());
|
||||
|
||||
return e;
|
||||
}).collect(Collectors.toList());
|
||||
page.setList(list);
|
||||
@ -73,28 +84,27 @@ public class ItemController {
|
||||
|
||||
@GetMapping("{id}")
|
||||
@Operation(summary = "信息")
|
||||
public Result<ItemDTO> get(@PathVariable("id") Long id){
|
||||
public Result<ItemDTO> get(@PathVariable("id") Long id) {
|
||||
ItemDTO data = itemService.get(id);
|
||||
data.setLargePic(uploadUrl + "item/l_" + data.getLargePic());
|
||||
data.setImage(uploadUrl + data.getImage());
|
||||
return new Result<ItemDTO>().ok(data);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "保存")
|
||||
public Result save(@RequestBody ItemDTO dto){
|
||||
public Result save(@RequestBody ItemDTO dto) {
|
||||
//效验数据
|
||||
ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class);
|
||||
dto.setLargePic(uploadUrl + "item/l_" + dto.getLargePic());
|
||||
dto.setNormalPic(uploadUrl + "item/n_" + dto.getNormalPic());
|
||||
dto.setImage(uploadUrl + dto.getImage());
|
||||
itemService.save(dto);
|
||||
return new Result();
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@Operation(summary = "修改")
|
||||
public Result update(@RequestBody ItemDTO dto){ //效验数据
|
||||
public Result update(@RequestBody ItemDTO dto) { //效验数据
|
||||
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
|
||||
dto.setLargePic(dto.getLargePic().replace(uploadUrl+"item/l_","") );
|
||||
dto.setImage(uploadUrl + dto.getImage());
|
||||
itemService.update(dto);
|
||||
return new Result();
|
||||
}
|
||||
@ -102,9 +112,9 @@ public class ItemController {
|
||||
@DeleteMapping("{id}")
|
||||
@Operation(summary = "删除")
|
||||
@LogOperation("删除")
|
||||
public Result delete(@PathVariable Long id){
|
||||
public Result delete(@PathVariable Long id) {
|
||||
|
||||
Long[] ids = new Long[] { id };
|
||||
Long[] ids = new Long[]{id};
|
||||
//效验数据
|
||||
AssertUtils.isArrayEmpty(ids, "id");
|
||||
itemService.delete(ids);
|
||||
|
@ -16,11 +16,14 @@ import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
@ -33,6 +36,10 @@ public class SlidesController {
|
||||
@Autowired
|
||||
private SlidesService slidesService;
|
||||
|
||||
|
||||
@Value("${upload.url}")
|
||||
private String uploadUrl;
|
||||
|
||||
@GetMapping("page")
|
||||
@Operation(summary = "分页")
|
||||
@Parameters({
|
||||
@ -43,6 +50,13 @@ public class SlidesController {
|
||||
})
|
||||
public Result<PageData<SlidesDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params){
|
||||
PageData<SlidesDTO> page = slidesService.page(params);
|
||||
|
||||
List<SlidesDTO> list = page.getList().stream().map(e -> {
|
||||
e.setPath(uploadUrl + e.getPath());
|
||||
return e;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
page.setList(list);
|
||||
return new Result<PageData<SlidesDTO>>().ok(page);
|
||||
}
|
||||
|
||||
|
@ -3,9 +3,9 @@ spring:
|
||||
druid:
|
||||
#MySQL
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://ordinary.jimostudio.link:26449/bs_movies?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
|
||||
url: jdbc:mysql://47.94.76.54:23306/bookstore?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
|
||||
username: root
|
||||
password: minxianrui
|
||||
password: x6dbfGN4s6YjcX8P
|
||||
initial-size: 10
|
||||
max-active: 100
|
||||
min-idle: 10
|
||||
@ -38,4 +38,4 @@ mybatis-plus:
|
||||
boolValue: TRUE
|
||||
upload:
|
||||
path: D:\\code\\security\\upload\\
|
||||
url: https://bs.oss.xunyingcloud.cn/upload/movies/
|
||||
url: http://localhost:18081/
|
||||
|
@ -0,0 +1,17 @@
|
||||
package io.modules.item.dao;
|
||||
|
||||
|
||||
import io.common.dao.BaseDao;
|
||||
import io.modules.item.entity.CategoriesEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 商品分类表
|
||||
*
|
||||
* @author Mark #
|
||||
* @since 1.0.0 2025-02-13
|
||||
*/
|
||||
@Mapper
|
||||
public interface CategoriesDao extends BaseDao<CategoriesEntity> {
|
||||
|
||||
}
|
@ -1,17 +1,11 @@
|
||||
package io.modules.item.dao;
|
||||
|
||||
|
||||
import io.common.dao.BaseDao;
|
||||
import io.modules.item.entity.ItemEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 电影表
|
||||
*
|
||||
* @author Mark #
|
||||
* @since 1.0.0 2025-02-10
|
||||
* 主表
|
||||
*/
|
||||
@Mapper
|
||||
public interface ItemDao extends BaseDao<ItemEntity> {
|
||||
|
||||
}
|
||||
public interface ItemDao extends BaseDao<ItemEntity> {}
|
||||
|
@ -0,0 +1,38 @@
|
||||
package io.modules.item.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.media.SchemaProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
* 商品分类表
|
||||
*
|
||||
* @author Mark #
|
||||
* @since 1.0.0 2025-02-13
|
||||
*/
|
||||
@Data
|
||||
@Schema(name = "商品分类表")
|
||||
public class CategoriesDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SchemaProperty(name = "编号")
|
||||
private Long id;
|
||||
|
||||
@SchemaProperty(name = "名称")
|
||||
private String name;
|
||||
|
||||
@SchemaProperty(name = "图片")
|
||||
private String image;
|
||||
|
||||
@SchemaProperty(name = "父编号")
|
||||
private Integer parentId;
|
||||
|
||||
@SchemaProperty(name = "创建时间")
|
||||
private Date createTime;
|
||||
|
||||
|
||||
}
|
@ -7,56 +7,61 @@ import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 电影表
|
||||
* 商品表
|
||||
*/
|
||||
@Data
|
||||
@Schema(name = "电影表")
|
||||
@Schema(name = "商品表")
|
||||
public class ItemDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SchemaProperty(name = "编号")
|
||||
private Long id;
|
||||
|
||||
@SchemaProperty(name = "电影名称")
|
||||
@SchemaProperty(name = "名称")
|
||||
private String title;
|
||||
private String tag;
|
||||
|
||||
@SchemaProperty(name = "电影副标题")
|
||||
private String cardSubtitle;
|
||||
@SchemaProperty(name = "类别编号")
|
||||
private Long categoryId;
|
||||
private String categoryName;
|
||||
|
||||
@SchemaProperty(name = "评分")
|
||||
private BigDecimal ratingValue;
|
||||
@SchemaProperty(name = "图片")
|
||||
private String image;
|
||||
|
||||
@SchemaProperty(name = "评分人数")
|
||||
private Integer ratingCount;
|
||||
@SchemaProperty(name = "图片列表")
|
||||
private String imgList;
|
||||
|
||||
@SchemaProperty(name = "星级评分")
|
||||
private BigDecimal ratingStarCount;
|
||||
@SchemaProperty(name = "价格")
|
||||
private Double price;
|
||||
|
||||
@SchemaProperty(name = "上映时间")
|
||||
private String year;
|
||||
@SchemaProperty(name = "描述")
|
||||
private String description;
|
||||
|
||||
@SchemaProperty(name = "大图文件名")
|
||||
private String largePic;
|
||||
@SchemaProperty(name = "商品库存数量")
|
||||
private Integer stockQuantity;
|
||||
|
||||
@SchemaProperty(name = "小图文件名")
|
||||
private String normalPic;
|
||||
@SchemaProperty(name = "状态")
|
||||
private Integer status;
|
||||
|
||||
@SchemaProperty(name = "国家")
|
||||
private String country;
|
||||
|
||||
@SchemaProperty(name = "电影类型")
|
||||
private String movieType;
|
||||
|
||||
@SchemaProperty(name = "导演")
|
||||
private String director;
|
||||
|
||||
@SchemaProperty(name = "主演")
|
||||
private String actor;
|
||||
private Boolean isFavorite;
|
||||
@SchemaProperty(name = "排序")
|
||||
private Integer sort;
|
||||
|
||||
@SchemaProperty(name = "创建时间")
|
||||
private Date createTime;
|
||||
|
||||
@SchemaProperty(name = "产地")
|
||||
private String origin;
|
||||
|
||||
@SchemaProperty(name = "品牌")
|
||||
private String brand;
|
||||
|
||||
@SchemaProperty(name = "点击次数")
|
||||
private Integer view;
|
||||
|
||||
@SchemaProperty(name = "属性")
|
||||
private String attribute;
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,35 @@
|
||||
package io.modules.item.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 商品分类表
|
||||
*/
|
||||
@Data
|
||||
@TableName("tb_categories")
|
||||
public class CategoriesEntity {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 图片
|
||||
*/
|
||||
private String image;
|
||||
/**
|
||||
* 父编号
|
||||
*/
|
||||
private Integer parentId;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
}
|
@ -3,14 +3,10 @@ package io.modules.item.entity;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 电影表
|
||||
*
|
||||
* @author Mark #
|
||||
* @since 1.0.0 2025-02-10
|
||||
* 商品表
|
||||
*/
|
||||
@Data
|
||||
@TableName("tb_item")
|
||||
@ -21,51 +17,60 @@ public class ItemEntity {
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 电影名称
|
||||
* 名称
|
||||
*/
|
||||
private String title;
|
||||
private String tag;
|
||||
/**
|
||||
* 电影副标题
|
||||
* 类别编号
|
||||
*/
|
||||
private String cardSubtitle;
|
||||
private Long categoryId;
|
||||
/**
|
||||
* 评分
|
||||
* 图片
|
||||
*/
|
||||
private BigDecimal ratingValue;
|
||||
private String image;
|
||||
/**
|
||||
* 评分人数
|
||||
* 图片列表
|
||||
*/
|
||||
private Integer ratingCount;
|
||||
private String imgList;
|
||||
/**
|
||||
* 星级评分
|
||||
* 价格
|
||||
*/
|
||||
private BigDecimal ratingStarCount;
|
||||
private Double price;
|
||||
/**
|
||||
* 上映时间
|
||||
* 描述
|
||||
*/
|
||||
private String year;
|
||||
private String description;
|
||||
/**
|
||||
* 大图文件名
|
||||
* 商品库存数量
|
||||
*/
|
||||
private String largePic;
|
||||
private Integer stockQuantity;
|
||||
/**
|
||||
* 小图文件名
|
||||
* 状态
|
||||
*/
|
||||
private String normalPic;
|
||||
private Integer status;
|
||||
/**
|
||||
* 国家
|
||||
* 排序
|
||||
*/
|
||||
private String country;
|
||||
private Integer sort;
|
||||
/**
|
||||
* 电影类型
|
||||
* 创建时间
|
||||
*/
|
||||
private String movieType;
|
||||
private Date createTime;
|
||||
/**
|
||||
* 导演
|
||||
* 产地
|
||||
*/
|
||||
private String director;
|
||||
private String origin;
|
||||
/**
|
||||
* 主演
|
||||
* 品牌
|
||||
*/
|
||||
private String actor;
|
||||
}
|
||||
private String brand;
|
||||
/**
|
||||
* 点击次数
|
||||
*/
|
||||
private Integer view;
|
||||
/**
|
||||
* 属性
|
||||
*/
|
||||
private String attribute;
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
package io.modules.item.service;
|
||||
|
||||
|
||||
import io.common.service.CrudService;
|
||||
import io.modules.item.dto.CategoriesDTO;
|
||||
import io.modules.item.entity.CategoriesEntity;
|
||||
|
||||
/**
|
||||
* 商品分类表
|
||||
*
|
||||
* @author Mark #
|
||||
* @since 1.0.0 2025-02-13
|
||||
*/
|
||||
public interface CategoriesService extends CrudService<CategoriesEntity, CategoriesDTO> {
|
||||
|
||||
}
|
@ -8,10 +8,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 电影表
|
||||
*
|
||||
* @author Mark #
|
||||
* @since 1.0.0 2025-02-10
|
||||
* 主表
|
||||
*/
|
||||
public interface ItemService extends CrudService<ItemEntity, ItemDTO> {
|
||||
|
||||
|
@ -0,0 +1,35 @@
|
||||
package io.modules.item.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import io.common.service.impl.CrudServiceImpl;
|
||||
import io.modules.item.dao.CategoriesDao;
|
||||
import io.modules.item.dto.CategoriesDTO;
|
||||
import io.modules.item.entity.CategoriesEntity;
|
||||
import io.modules.item.service.CategoriesService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 商品分类表
|
||||
*
|
||||
* @author Mark #
|
||||
* @since 1.0.0 2025-02-13
|
||||
*/
|
||||
@Service
|
||||
public class CategoriesServiceImpl extends CrudServiceImpl<CategoriesDao, CategoriesEntity, CategoriesDTO> implements CategoriesService {
|
||||
|
||||
@Override
|
||||
public QueryWrapper<CategoriesEntity> getWrapper(Map<String, Object> params){
|
||||
String id = (String)params.get("id");
|
||||
|
||||
QueryWrapper<CategoriesEntity> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq(StrUtil.isNotBlank(id), "id", id);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -24,10 +24,7 @@ import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 电影表
|
||||
*
|
||||
* @author Mark #
|
||||
* @since 1.0.0 2025-02-10
|
||||
* 主表
|
||||
*/
|
||||
@Service
|
||||
public class ItemServiceImpl extends CrudServiceImpl<ItemDao, ItemEntity, ItemDTO> implements ItemService {
|
||||
@ -42,7 +39,27 @@ public class ItemServiceImpl extends CrudServiceImpl<ItemDao, ItemEntity, ItemDT
|
||||
@Override
|
||||
public QueryWrapper<ItemEntity> getWrapper(Map<String, Object> params){
|
||||
String title = (String)params.get("title");
|
||||
String status = (String)params.get("status");
|
||||
|
||||
QueryWrapper<ItemEntity> wrapper = new QueryWrapper<>();
|
||||
|
||||
if (StrUtil.isNotBlank(status)){
|
||||
|
||||
switch (status){
|
||||
case "0":
|
||||
wrapper.eq("status", 0);
|
||||
break;
|
||||
case "1":
|
||||
wrapper.eq("status", 1);
|
||||
break;
|
||||
case "2":
|
||||
wrapper.lt("stock_quantity", 100);
|
||||
break;
|
||||
case "3":
|
||||
wrapper.eq("stock_quantity", 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
wrapper.like(StrUtil.isNotBlank(title), "title", title);
|
||||
return wrapper;
|
||||
}
|
||||
@ -51,7 +68,7 @@ public class ItemServiceImpl extends CrudServiceImpl<ItemDao, ItemEntity, ItemDT
|
||||
@Override
|
||||
public List<ItemEntity> score() {
|
||||
LambdaQueryWrapper<ItemEntity> lwq = new LambdaQueryWrapper<>();
|
||||
lwq.orderByDesc(ItemEntity::getRatingCount);
|
||||
lwq.orderByDesc(ItemEntity::getView);
|
||||
|
||||
|
||||
// 创建 Page 对象,设置当前页和每页大小
|
||||
@ -114,7 +131,7 @@ public class ItemServiceImpl extends CrudServiceImpl<ItemDao, ItemEntity, ItemDT
|
||||
@Override
|
||||
public List<ItemEntity> commit() {
|
||||
LambdaQueryWrapper<ItemEntity> lwq = new LambdaQueryWrapper<>();
|
||||
lwq.orderByDesc(ItemEntity::getRatingCount);
|
||||
lwq.orderByDesc(ItemEntity::getView);
|
||||
|
||||
// 创建 Page 对象,设置当前页和每页大小
|
||||
Page<ItemEntity> page = new Page<>(1, 10); // 第1页,每页10条
|
||||
|
@ -1,7 +0,0 @@
|
||||
-- 菜单初始SQL
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date)VALUES (1888815450461077506, 1067246875800000035, '电影表', 'item/item', NULL, 0, 'icon-desktop', 0, 1067246875800000001, now(), 1067246875800000001, now());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077507, 1888815450461077506, '查看', NULL, 'item:item:page,item:item:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077508, 1888815450461077506, '新增', NULL, 'item:item:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077509, 1888815450461077506, '修改', NULL, 'item:item:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077510, 1888815450461077506, '删除', NULL, 'item:item:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077511, 1888815450461077506, '导出', NULL, 'item:item:export', 1, NULL, 4, 1067246875800000001, now(), 1067246875800000001, now());
|
@ -1,7 +0,0 @@
|
||||
-- 菜单初始SQL
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date)VALUES (1888815450461077506, 1067246875800000035, '电影表', 'item/item', NULL, 0, 'icon-desktop', 0, 1067246875800000001, now(), 1067246875800000001, now());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077507, 1888815450461077506, '查看', NULL, 'item:item:page,item:item:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077508, 1888815450461077506, '新增', NULL, 'item:item:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077509, 1888815450461077506, '修改', NULL, 'item:item:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077510, 1888815450461077506, '删除', NULL, 'item:item:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077511, 1888815450461077506, '导出', NULL, 'item:item:export', 1, NULL, 4, 1067246875800000001, now(), 1067246875800000001, now());
|
@ -1,7 +0,0 @@
|
||||
-- 菜单初始SQL
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date)VALUES (1888815450461077506, 1067246875800000035, '电影表', 'item/item', NULL, 0, 'icon-desktop', 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE);
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077507, 1888815450461077506, '查看', NULL, 'item:item:page,item:item:info', 1, NULL, 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE);
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077508, 1888815450461077506, '新增', NULL, 'item:item:save', 1, NULL, 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE);
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077509, 1888815450461077506, '修改', NULL, 'item:item:update', 1, NULL, 2, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE);
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077510, 1888815450461077506, '删除', NULL, 'item:item:delete', 1, NULL, 3, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE);
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077511, 1888815450461077506, '导出', NULL, 'item:item:export', 1, NULL, 4, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE);
|
@ -1,7 +0,0 @@
|
||||
-- 菜单初始SQL
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date)VALUES (1888815450461077506, 1067246875800000035, '电影表', 'item/item', NULL, 0, 'icon-desktop', 0, 1067246875800000001, now(), 1067246875800000001, now());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077507, 1888815450461077506, '查看', NULL, 'item:item:page,item:item:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077508, 1888815450461077506, '新增', NULL, 'item:item:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077509, 1888815450461077506, '修改', NULL, 'item:item:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077510, 1888815450461077506, '删除', NULL, 'item:item:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077511, 1888815450461077506, '导出', NULL, 'item:item:export', 1, NULL, 4, 1067246875800000001, now(), 1067246875800000001, now());
|
@ -1,7 +0,0 @@
|
||||
-- 菜单初始SQL
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date)VALUES (1888815450461077506, 1067246875800000035, '电影表', 'item/item', NULL, 0, 'icon-desktop', 0, 1067246875800000001, getdate(), 1067246875800000001, getdate());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077507, 1888815450461077506, '查看', NULL, 'item:item:page,item:item:info', 1, NULL, 0, 1067246875800000001, getdate(), 1067246875800000001, getdate());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077508, 1888815450461077506, '新增', NULL, 'item:item:save', 1, NULL, 1, 1067246875800000001, getdate(), 1067246875800000001, getdate());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077509, 1888815450461077506, '修改', NULL, 'item:item:update', 1, NULL, 2, 1067246875800000001, getdate(), 1067246875800000001, getdate());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077510, 1888815450461077506, '删除', NULL, 'item:item:delete', 1, NULL, 3, 1067246875800000001, getdate(), 1067246875800000001, getdate());
|
||||
INSERT INTO sys_menu(id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1888815450461077511, 1888815450461077506, '导出', NULL, 'item:item:export', 1, NULL, 4, 1067246875800000001, getdate(), 1067246875800000001, getdate());
|
@ -1,112 +0,0 @@
|
||||
package io.common.modules.item.controller;
|
||||
|
||||
import io.common.common.annotation.LogOperation;
|
||||
import io.common.common.constant.Constant;
|
||||
import io.common.common.page.PageData;
|
||||
import io.common.common.utils.ExcelUtils;
|
||||
import io.common.common.utils.Result;
|
||||
import io.common.common.validator.AssertUtils;
|
||||
import io.common.common.validator.ValidatorUtils;
|
||||
import io.common.common.validator.group.AddGroup;
|
||||
import io.common.common.validator.group.DefaultGroup;
|
||||
import io.common.common.validator.group.UpdateGroup;
|
||||
import io.common.modules.item.dto.ItemDTO;
|
||||
import io.common.modules.item.excel.ItemExcel;
|
||||
import io.common.modules.item.service.ItemService;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* 电影表
|
||||
*
|
||||
* @author Mark #
|
||||
* @since 1.0.0 2025-02-10
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("item/item")
|
||||
@Tag(name="电影表")
|
||||
public class ItemController {
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
|
||||
@GetMapping("page")
|
||||
@Operation(summary = "分页")
|
||||
@Parameters({
|
||||
@Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref="int") ,
|
||||
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY,required = true, ref="int") ,
|
||||
@Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref="String") ,
|
||||
@Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref="String")
|
||||
})
|
||||
@RequiresPermissions("item:item:page")
|
||||
public Result<PageData<ItemDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params){
|
||||
PageData<ItemDTO> page = itemService.page(params);
|
||||
|
||||
return new Result<PageData<ItemDTO>>().ok(page);
|
||||
}
|
||||
|
||||
@GetMapping("{id}")
|
||||
@Operation(summary = "信息")
|
||||
@RequiresPermissions("item:item:info")
|
||||
public Result<ItemDTO> get(@PathVariable("id") Long id){
|
||||
ItemDTO data = itemService.get(id);
|
||||
|
||||
return new Result<ItemDTO>().ok(data);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "保存")
|
||||
@LogOperation("保存")
|
||||
@RequiresPermissions("item:item:save")
|
||||
public Result save(@RequestBody ItemDTO dto){
|
||||
//效验数据
|
||||
ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class);
|
||||
|
||||
itemService.save(dto);
|
||||
|
||||
return new Result();
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@Operation(summary = "修改")
|
||||
@LogOperation("修改")
|
||||
@RequiresPermissions("item:item:update")
|
||||
public Result update(@RequestBody ItemDTO dto){
|
||||
//效验数据
|
||||
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
|
||||
|
||||
itemService.update(dto);
|
||||
|
||||
return new Result();
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@Operation(summary = "删除")
|
||||
@LogOperation("删除")
|
||||
@RequiresPermissions("item:item:delete")
|
||||
public Result delete(@RequestBody Long[] ids){
|
||||
//效验数据
|
||||
AssertUtils.isArrayEmpty(ids, "id");
|
||||
|
||||
itemService.delete(ids);
|
||||
|
||||
return new Result();
|
||||
}
|
||||
|
||||
@GetMapping("export")
|
||||
@Operation(summary = "导出")
|
||||
@LogOperation("导出")
|
||||
@RequiresPermissions("item:item:export")
|
||||
public void export(@Parameter(hidden = true) @RequestParam Map<String, Object> params, HttpServletResponse response) throws Exception {
|
||||
List<ItemDTO> list = itemService.list(params);
|
||||
|
||||
ExcelUtils.exportExcelToTarget(response, null, "电影表", list, ItemExcel.class);
|
||||
}
|
||||
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package io.common.modules.item.dao;
|
||||
|
||||
import io.common.common.dao.BaseDao;
|
||||
import io.common.modules.item.entity.ItemEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 电影表
|
||||
*
|
||||
* @author Mark #
|
||||
* @since 1.0.0 2025-02-10
|
||||
*/
|
||||
@Mapper
|
||||
public interface ItemDao extends BaseDao<ItemEntity> {
|
||||
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
package io.common.modules.item.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 电影表
|
||||
*/
|
||||
@Data
|
||||
@Schema(name = "电影表")
|
||||
public class ItemDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SchemaProperty(name = "编号")
|
||||
private String id;
|
||||
|
||||
@SchemaProperty(name = "电影名称")
|
||||
private String title;
|
||||
|
||||
@SchemaProperty(name = "电影副标题")
|
||||
private String cardSubtitle;
|
||||
|
||||
@SchemaProperty(name = "评分")
|
||||
private BigDecimal ratingValue;
|
||||
|
||||
@SchemaProperty(name = "评分人数")
|
||||
private Integer ratingCount;
|
||||
|
||||
@SchemaProperty(name = "星级评分")
|
||||
private BigDecimal ratingStarCount;
|
||||
|
||||
@SchemaProperty(name = "上映时间")
|
||||
private unknowType year;
|
||||
|
||||
@SchemaProperty(name = "大图文件名")
|
||||
private String largePic;
|
||||
|
||||
@SchemaProperty(name = "小图文件名")
|
||||
private String normalPic;
|
||||
|
||||
@SchemaProperty(name = "国家")
|
||||
private String country;
|
||||
|
||||
@SchemaProperty(name = "电影类型")
|
||||
private String movieType;
|
||||
|
||||
@SchemaProperty(name = "导演")
|
||||
private String director;
|
||||
|
||||
@SchemaProperty(name = "主演")
|
||||
private String star;
|
||||
|
||||
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
package io.common.modules.item.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 电影表
|
||||
*
|
||||
* @author Mark #
|
||||
* @since 1.0.0 2025-02-10
|
||||
*/
|
||||
@Data
|
||||
@TableName("tb_item")
|
||||
public class ItemEntity {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 电影名称
|
||||
*/
|
||||
private String title;
|
||||
/**
|
||||
* 电影副标题
|
||||
*/
|
||||
private String cardSubtitle;
|
||||
/**
|
||||
* 评分
|
||||
*/
|
||||
private BigDecimal ratingValue;
|
||||
/**
|
||||
* 评分人数
|
||||
*/
|
||||
private Integer ratingCount;
|
||||
/**
|
||||
* 星级评分
|
||||
*/
|
||||
private BigDecimal ratingStarCount;
|
||||
/**
|
||||
* 上映时间
|
||||
*/
|
||||
private unknowType year;
|
||||
/**
|
||||
* 大图文件名
|
||||
*/
|
||||
private String largePic;
|
||||
/**
|
||||
* 小图文件名
|
||||
*/
|
||||
private String normalPic;
|
||||
/**
|
||||
* 国家
|
||||
*/
|
||||
private String country;
|
||||
/**
|
||||
* 电影类型
|
||||
*/
|
||||
private String movieType;
|
||||
/**
|
||||
* 导演
|
||||
*/
|
||||
private String director;
|
||||
/**
|
||||
* 主演
|
||||
*/
|
||||
private String star;
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package io.common.modules.item.excel;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.alibaba.excel.annotation.write.style.ColumnWidth;
|
||||
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
|
||||
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 电影表
|
||||
*
|
||||
* @author Mark #
|
||||
* @since 1.0.0 2025-02-10
|
||||
*/
|
||||
@Data
|
||||
public class ItemExcel {
|
||||
@ExcelProperty(value = "编号")
|
||||
private String id;
|
||||
@ExcelProperty(value = "电影名称")
|
||||
private String title;
|
||||
@ExcelProperty(value = "电影副标题")
|
||||
private String cardSubtitle;
|
||||
@ExcelProperty(value = "评分")
|
||||
private BigDecimal ratingValue;
|
||||
@ExcelProperty(value = "评分人数")
|
||||
private Integer ratingCount;
|
||||
@ExcelProperty(value = "星级评分")
|
||||
private BigDecimal ratingStarCount;
|
||||
@ExcelProperty(value = "上映时间")
|
||||
private unknowType year;
|
||||
@ExcelProperty(value = "大图文件名")
|
||||
private String largePic;
|
||||
@ExcelProperty(value = "小图文件名")
|
||||
private String normalPic;
|
||||
@ExcelProperty(value = "国家")
|
||||
private String country;
|
||||
@ExcelProperty(value = "电影类型")
|
||||
private String movieType;
|
||||
@ExcelProperty(value = "导演")
|
||||
private String director;
|
||||
@ExcelProperty(value = "主演")
|
||||
private String star;
|
||||
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package io.common.modules.item.service;
|
||||
|
||||
import io.common.common.service.CrudService;
|
||||
import io.common.modules.item.dto.ItemDTO;
|
||||
import io.common.modules.item.entity.ItemEntity;
|
||||
|
||||
/**
|
||||
* 电影表
|
||||
*
|
||||
* @author Mark #
|
||||
* @since 1.0.0 2025-02-10
|
||||
*/
|
||||
public interface ItemService extends CrudService<ItemEntity, ItemDTO> {
|
||||
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package io.common.modules.item.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import io.common.common.service.impl.CrudServiceImpl;
|
||||
import io.common.modules.item.dao.ItemDao;
|
||||
import io.common.modules.item.dto.ItemDTO;
|
||||
import io.common.modules.item.entity.ItemEntity;
|
||||
import io.common.modules.item.service.ItemService;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 电影表
|
||||
*
|
||||
* @author Mark #
|
||||
* @since 1.0.0 2025-02-10
|
||||
*/
|
||||
@Service
|
||||
public class ItemServiceImpl extends CrudServiceImpl<ItemDao, ItemEntity, ItemDTO> implements ItemService {
|
||||
|
||||
@Override
|
||||
public QueryWrapper<ItemEntity> getWrapper(Map<String, Object> params){
|
||||
String id = (String)params.get("id");
|
||||
|
||||
QueryWrapper<ItemEntity> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq(StrUtil.isNotBlank(id), "id", id);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
<?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="io.common.modules.item.dao.ItemDao">
|
||||
|
||||
<resultMap type="io.common.modules.item.entity.ItemEntity" id="itemMap">
|
||||
<result property="id" column="id"/>
|
||||
<result property="title" column="title"/>
|
||||
<result property="cardSubtitle" column="card_subtitle"/>
|
||||
<result property="ratingValue" column="rating_value"/>
|
||||
<result property="ratingCount" column="rating_count"/>
|
||||
<result property="ratingStarCount" column="rating_star_count"/>
|
||||
<result property="year" column="year"/>
|
||||
<result property="largePic" column="large_pic"/>
|
||||
<result property="normalPic" column="normal_pic"/>
|
||||
<result property="country" column="country"/>
|
||||
<result property="movieType" column="movie_type"/>
|
||||
<result property="director" column="director"/>
|
||||
<result property="star" column="star"/>
|
||||
</resultMap>
|
||||
|
||||
|
||||
</mapper>
|
@ -1,142 +0,0 @@
|
||||
<template>
|
||||
<el-dialog v-model="visible" :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :close-on-press-escape="false">
|
||||
<el-form :model="dataForm" :rules="rules" ref="dataFormRef" @keyup.enter="dataFormSubmitHandle()" label-width="120px">
|
||||
<el-form-item label="电影名称" prop="title">
|
||||
<el-input v-model="dataForm.title" placeholder="电影名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="电影副标题" prop="cardSubtitle">
|
||||
<el-input v-model="dataForm.cardSubtitle" placeholder="电影副标题"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="评分" prop="ratingValue">
|
||||
<el-input v-model="dataForm.ratingValue" placeholder="评分"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="评分人数" prop="ratingCount">
|
||||
<el-input v-model="dataForm.ratingCount" placeholder="评分人数"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="星级评分" prop="ratingStarCount">
|
||||
<el-input v-model="dataForm.ratingStarCount" placeholder="星级评分"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="上映时间" prop="year">
|
||||
<el-input v-model="dataForm.year" placeholder="上映时间"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="大图文件名" prop="largePic">
|
||||
<el-input v-model="dataForm.largePic" placeholder="大图文件名"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="小图文件名" prop="normalPic">
|
||||
<el-input v-model="dataForm.normalPic" placeholder="小图文件名"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="国家" prop="country">
|
||||
<el-input v-model="dataForm.country" placeholder="国家"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="电影类型" prop="movieType">
|
||||
<el-input v-model="dataForm.movieType" placeholder="电影类型"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="导演" prop="director">
|
||||
<el-input v-model="dataForm.director" placeholder="导演"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="主演" prop="star">
|
||||
<el-input v-model="dataForm.star" placeholder="主演"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="dataFormSubmitHandle()">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from "vue";
|
||||
import baseService from "@/service/baseService";
|
||||
import { ElMessage } from "element-plus";
|
||||
const emit = defineEmits(["refreshDataList"]);
|
||||
|
||||
const visible = ref(false);
|
||||
const dataFormRef = ref();
|
||||
|
||||
const dataForm = reactive({
|
||||
id: '', title: '', cardSubtitle: '', ratingValue: '', ratingCount: '', ratingStarCount: '', year: '', largePic: '', normalPic: '', country: '', movieType: '', director: '', star: ''});
|
||||
|
||||
const rules = ref({
|
||||
title: [
|
||||
{ required: true, message: '必填项不能为空', trigger: 'blur' }
|
||||
],
|
||||
cardSubtitle: [
|
||||
{ required: true, message: '必填项不能为空', trigger: 'blur' }
|
||||
],
|
||||
ratingValue: [
|
||||
{ required: true, message: '必填项不能为空', trigger: 'blur' }
|
||||
],
|
||||
ratingCount: [
|
||||
{ required: true, message: '必填项不能为空', trigger: 'blur' }
|
||||
],
|
||||
ratingStarCount: [
|
||||
{ required: true, message: '必填项不能为空', trigger: 'blur' }
|
||||
],
|
||||
year: [
|
||||
{ required: true, message: '必填项不能为空', trigger: 'blur' }
|
||||
],
|
||||
largePic: [
|
||||
{ required: true, message: '必填项不能为空', trigger: 'blur' }
|
||||
],
|
||||
normalPic: [
|
||||
{ required: true, message: '必填项不能为空', trigger: 'blur' }
|
||||
],
|
||||
country: [
|
||||
{ required: true, message: '必填项不能为空', trigger: 'blur' }
|
||||
],
|
||||
movieType: [
|
||||
{ required: true, message: '必填项不能为空', trigger: 'blur' }
|
||||
],
|
||||
director: [
|
||||
{ required: true, message: '必填项不能为空', trigger: 'blur' }
|
||||
],
|
||||
star: [
|
||||
{ required: true, message: '必填项不能为空', trigger: 'blur' }
|
||||
]
|
||||
});
|
||||
|
||||
const init = (id?: number) => {
|
||||
visible.value = true;
|
||||
dataForm.id = "";
|
||||
|
||||
// 重置表单数据
|
||||
if (dataFormRef.value) {
|
||||
dataFormRef.value.resetFields();
|
||||
}
|
||||
|
||||
if (id) {
|
||||
getInfo(id);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取信息
|
||||
const getInfo = (id: number) => {
|
||||
baseService.get("/item/item/" + id).then((res) => {
|
||||
Object.assign(dataForm, res.data);
|
||||
});
|
||||
};
|
||||
|
||||
// 表单提交
|
||||
const dataFormSubmitHandle = () => {
|
||||
dataFormRef.value.validate((valid: boolean) => {
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
(!dataForm.id ? baseService.post : baseService.put)("/item/item", dataForm).then((res) => {
|
||||
ElMessage.success({
|
||||
message: '成功',
|
||||
duration: 500,
|
||||
onClose: () => {
|
||||
visible.value = false;
|
||||
emit("refreshDataList");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
init
|
||||
});
|
||||
</script>
|
@ -1,58 +0,0 @@
|
||||
<template>
|
||||
<div class="mod-item__item">
|
||||
<el-form :inline="true" :model="state.dataForm" @keyup.enter="state.getDataList()">
|
||||
<el-form-item>
|
||||
<el-button v-if="state.hasPermission('item:item:save')" type="primary" @click="addOrUpdateHandle()">新增</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button v-if="state.hasPermission('item:item:delete')" type="danger" @click="state.deleteHandle()">删除</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="state.dataListLoading" :data="state.dataList" border @selection-change="state.dataListSelectionChangeHandle" style="width: 100%">
|
||||
<el-table-column type="selection" header-align="center" align="center" width="50"></el-table-column>
|
||||
<el-table-column prop="id" label="编号" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="title" label="电影名称" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="cardSubtitle" label="电影副标题" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="ratingValue" label="评分" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="ratingCount" label="评分人数" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="ratingStarCount" label="星级评分" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="year" label="上映时间" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="largePic" label="大图文件名" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="normalPic" label="小图文件名" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="country" label="国家" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="movieType" label="电影类型" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="director" label="导演" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="star" label="主演" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column label="操作" fixed="right" header-align="center" align="center" width="150">
|
||||
<template v-slot="scope">
|
||||
<el-button v-if="state.hasPermission('item:item:update')" type="primary" link @click="addOrUpdateHandle(scope.row.id)">修改</el-button>
|
||||
<el-button v-if="state.hasPermission('item:item:delete')" type="primary" link @click="state.deleteHandle(scope.row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination :current-page="state.page" :page-sizes="[10, 20, 50, 100]" :page-size="state.limit" :total="state.total" layout="total, sizes, prev, pager, next, jumper" @size-change="state.pageSizeChangeHandle" @current-change="state.pageCurrentChangeHandle"> </el-pagination>
|
||||
<!-- 弹窗, 新增 / 修改 -->
|
||||
<add-or-update ref="addOrUpdateRef" @refreshDataList="state.getDataList">确定</add-or-update>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import useView from "@/hooks/useView";
|
||||
import { reactive, ref, toRefs } from "vue";
|
||||
import AddOrUpdate from "./item-add-or-update.vue";
|
||||
|
||||
const view = reactive({
|
||||
deleteIsBatch: true,
|
||||
getDataListURL: "/item/item/page",
|
||||
getDataListIsPage: true,
|
||||
exportURL: "/item/item/export",
|
||||
deleteURL: "/item/item"
|
||||
});
|
||||
|
||||
const state = reactive({ ...useView(view), ...toRefs(view) });
|
||||
|
||||
const addOrUpdateRef = ref();
|
||||
const addOrUpdateHandle = (id?: number) => {
|
||||
addOrUpdateRef.value.init(id);
|
||||
};
|
||||
</script>
|
@ -34,7 +34,7 @@ import java.util.stream.Collectors;
|
||||
@CrossOrigin
|
||||
@RestController
|
||||
@RequestMapping("/api/item")
|
||||
@Tag(name="电影表")
|
||||
@Tag(name="主表")
|
||||
public class ItemController {
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
@ -48,13 +48,13 @@ public class ItemController {
|
||||
@Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref="int") ,
|
||||
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY,required = true, ref="int") ,
|
||||
@Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref="String") ,
|
||||
@Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref="String")
|
||||
@Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref="String"),
|
||||
@Parameter(name = "status", description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref="String")
|
||||
})
|
||||
public Result<PageData<ItemDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params){
|
||||
PageData<ItemDTO> page = itemService.page(params);
|
||||
List<ItemDTO> list = page.getList().stream().map(e -> {
|
||||
e.setLargePic(uploadUrl + "item/" + e.getLargePic());
|
||||
|
||||
e.setImage(uploadUrl + e.getImage());
|
||||
return e;
|
||||
}).collect(Collectors.toList());
|
||||
page.setList(list);
|
||||
@ -67,12 +67,6 @@ public class ItemController {
|
||||
@Operation(summary = "查询收藏")
|
||||
public Result<List<ItemEntity>> list(@Parameter(hidden = true) @RequestAttribute("userId") Long userId){
|
||||
List<ItemEntity> page = itemService.listUser(userId);
|
||||
List<ItemEntity> list = page.stream().map(e -> {
|
||||
e.setLargePic(uploadUrl + "item/" + e.getLargePic());
|
||||
e.setNormalPic(uploadUrl + "item/n_" + e.getNormalPic());
|
||||
return e;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
|
||||
// List<ItemDTO> list = page.getList().stream().map(e -> {
|
||||
// e.setLargePic(uploadUrl + "item/" + e.getLargePic());
|
||||
@ -80,7 +74,7 @@ public class ItemController {
|
||||
// return e;
|
||||
// }).collect(Collectors.toList());
|
||||
// page.setList(list);
|
||||
return new Result<List<ItemEntity>>().ok(list);
|
||||
return new Result<List<ItemEntity>>().ok(page);
|
||||
}
|
||||
|
||||
|
||||
@ -92,8 +86,8 @@ public class ItemController {
|
||||
public Result<List<ItemEntity>> score(){
|
||||
List<ItemEntity> list = itemService.score();
|
||||
List<ItemEntity> res = list.stream().map(e -> {
|
||||
e.setLargePic(uploadUrl + "item/" + e.getLargePic());
|
||||
e.setNormalPic(uploadUrl + "item/n_" + e.getNormalPic());
|
||||
e.setImage(uploadUrl + e.getImage());
|
||||
|
||||
return e;
|
||||
}).collect(Collectors.toList());
|
||||
return new Result<List<ItemEntity>>().ok(res);
|
||||
@ -106,8 +100,7 @@ public class ItemController {
|
||||
public Result<List<ItemEntity>> commit(){
|
||||
List<ItemEntity> list = itemService.commit();
|
||||
List<ItemEntity> res = list.stream().map(e -> {
|
||||
e.setLargePic(uploadUrl + "item/" + e.getLargePic());
|
||||
e.setNormalPic(uploadUrl + "item/n_" + e.getNormalPic());
|
||||
e.setImage(uploadUrl + e.getImage());
|
||||
return e;
|
||||
}).collect(Collectors.toList());
|
||||
return new Result<List<ItemEntity>>().ok(res);
|
||||
@ -117,10 +110,9 @@ public class ItemController {
|
||||
@Operation(summary = "信息")
|
||||
public Result<ItemDTO> get(@PathVariable("id") Long id){
|
||||
ItemDTO data = itemService.get(id);
|
||||
data.setLargePic(uploadUrl + "item/" + data.getLargePic());
|
||||
data.setNormalPic(uploadUrl + "item/n_" + data.getNormalPic());
|
||||
data.setImage(uploadUrl + data.getImage());
|
||||
|
||||
data.setIsFavorite(userBehaviorService.getIsFavorite(data.getId(),1));
|
||||
// data.setIsFavorite(userBehaviorService.getIsFavorite(data.getId(),1));
|
||||
return new Result<ItemDTO>().ok(data);
|
||||
}
|
||||
|
||||
@ -137,8 +129,7 @@ public class ItemController {
|
||||
@Operation(summary = "修改")
|
||||
public Result update(@RequestBody ItemDTO dto){ //效验数据
|
||||
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
|
||||
dto.setLargePic(uploadUrl + "item/" + dto.getLargePic());
|
||||
dto.setNormalPic(uploadUrl + "item/n_" + dto.getNormalPic());
|
||||
dto.setImage(uploadUrl + dto.getImage());
|
||||
itemService.update(dto);
|
||||
return new Result();
|
||||
}
|
||||
|
@ -16,8 +16,12 @@ import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
@ -31,6 +35,10 @@ public class SlidesFrontController {
|
||||
@Autowired
|
||||
private SlidesService slidesService;
|
||||
|
||||
@Value("${upload.url}")
|
||||
private String uploadUrl;
|
||||
|
||||
|
||||
@GetMapping("page")
|
||||
@Operation(summary = "分页")
|
||||
@Parameters({
|
||||
@ -42,6 +50,13 @@ public class SlidesFrontController {
|
||||
})
|
||||
public Result<PageData<SlidesDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params){
|
||||
PageData<SlidesDTO> page = slidesService.page(params);
|
||||
|
||||
List<SlidesDTO> list = page.getList().stream().map(e -> {
|
||||
e.setPath(uploadUrl + e.getPath());
|
||||
return e;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
page.setList(list);
|
||||
return new Result<PageData<SlidesDTO>>().ok(page);
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,6 @@ public class UploadController {
|
||||
file.transferTo(new File(path +"/"+ originFileName));
|
||||
String url = uploadUrl + originFileName;
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
|
||||
map.put("path",url);
|
||||
map.put("absolute","upload/" + originFileName);
|
||||
return new Result<>().ok(map);
|
||||
|
@ -6,9 +6,9 @@ spring:
|
||||
datasource:
|
||||
druid:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://ordinary.jimostudio.link:26449/bs_movies?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
|
||||
url: jdbc:mysql://47.94.76.54:23306/bookstore?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
|
||||
username: root
|
||||
password: minxianrui
|
||||
password: x6dbfGN4s6YjcX8P
|
||||
initial-size: 10
|
||||
max-active: 100
|
||||
min-idle: 10
|
||||
@ -33,7 +33,7 @@ spring:
|
||||
multi-statement-allow: true
|
||||
web:
|
||||
resources:
|
||||
static-locations: "file:F:/upload/"
|
||||
static-locations: "file:F:/2025/bookstore-springboot-vue/admin-ui/upload/"
|
||||
upload:
|
||||
path: F:\upload
|
||||
url: https://localhost:10081/upload/
|
||||
path: F:\2025\bookstore-springboot-vue\admin-ui\upload
|
||||
url: http://localhost:18081/
|
||||
|
@ -7,31 +7,12 @@ server:
|
||||
spring:
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
#MySQL配置
|
||||
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://192.168.15.2:3306/bs_movies?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
|
||||
url: jdbc:mysql://47.94.76.54:23306/bookstore?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
|
||||
username: root
|
||||
password: minxianrui
|
||||
#达梦8
|
||||
# driver-class-name: dm.jdbc.driver.DmDriver
|
||||
# url: jdbc:dm://127.0.0.1:5236/renren_security?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
|
||||
# username: renren_security
|
||||
# password: ren123456
|
||||
#oracle配置
|
||||
# driverClassName: oracle.jdbc.OracleDriver
|
||||
# url: jdbc:oracle:thin:@192.168.10.10:1521:xe
|
||||
# username: renren_security
|
||||
# password: 123456
|
||||
#SQLServer配置
|
||||
# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
|
||||
# url: jdbc:sqlserver://192.168.10.10:1433;DatabaseName=renren_security
|
||||
# username: sa
|
||||
# password: 123456
|
||||
#PostgreSQL配置
|
||||
# driverClassName: org.postgresql.Driver
|
||||
# url: jdbc:postgresql://192.168.10.10:5432/renren_security
|
||||
# username: postgres
|
||||
# password: 123456
|
||||
password: x6dbfGN4s6YjcX8P
|
||||
|
||||
jackson:
|
||||
time-zone: GMT+8
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
@ -50,6 +31,5 @@ pagehelper:
|
||||
params: count=countSql
|
||||
|
||||
|
||||
#指定数据库,可选值有【mysql、oracle、sqlserver、postgresql、dm】
|
||||
renren:
|
||||
database: mysql
|
||||
|
BIN
admin-ui/upload/298f82ab-f7f5-4833-8ea0-35148b1c6edf.jpg
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
admin-ui/upload/394a10bc-4aab-467a-aa21-76e4379b3c13.jpg
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
admin-ui/upload/4199679-fm.jpg
Normal file
After Width: | Height: | Size: 174 KiB |
BIN
admin-ui/upload/5900512-fm.jpg
Normal file
After Width: | Height: | Size: 100 KiB |
BIN
admin-ui/upload/5931796-fm.jpg
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
admin-ui/upload/6148055-fm.jpg
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
admin-ui/upload/6214373-fm.jpg
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
admin-ui/upload/6215380-fm.jpg
Normal file
After Width: | Height: | Size: 97 KiB |
BIN
admin-ui/upload/83a8b4999bbefb1d.png
Normal file
After Width: | Height: | Size: 198 KiB |
BIN
admin-ui/upload/bfe918ab1d9cb2cc.jpg
Normal file
After Width: | Height: | Size: 84 KiB |
2
ui/.env
@ -7,7 +7,7 @@ VITE_ADMIN_API_BASE_URL = http://localhost:18080
|
||||
VITE_LOGIN_BG = "/login/e36341619bf8f04dcbdc6b01105a85a.png"
|
||||
|
||||
# 标题
|
||||
VITE_APP_TITLE = 后台
|
||||
VITE_APP_TITLE = 电影系统
|
||||
|
||||
# markdown 渲染支持
|
||||
VITE_APP_MARKDOWN = true
|
||||
|
2
ui/.gitignore
vendored
@ -9,4 +9,4 @@ type-router.d.ts
|
||||
auto-imports.d.ts
|
||||
.eslintrc-auto-import.json
|
||||
vite.config.ts.timestamp*
|
||||
.idea
|
||||
.idea/
|
||||
|
@ -107,6 +107,13 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"element-plus": "^2.9.2"
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||
"element-plus": "^2.9.2",
|
||||
"save": "^2.9.0",
|
||||
"scss": "^0.2.4",
|
||||
"use-element-plus-theme": "^0.0.5",
|
||||
"v-charts": "^1.19.0",
|
||||
"vite-plugin-theme": "^0.8.6"
|
||||
}
|
||||
}
|
||||
|
10840
ui/pnpm-lock.yaml
generated
@ -193,8 +193,6 @@ export default function () {
|
||||
if (env.VITE_APP_DEV_TOOLS) {
|
||||
plugins.push(VueDevTools())
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* api 自动按需引入
|
||||
* https://github.com/antfu/unplugin-auto-import
|
||||
|
BIN
ui/public/a1.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
ui/public/p2915350868.webp
Normal file
After Width: | Height: | Size: 17 KiB |
@ -2,6 +2,10 @@
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
|
||||
</script>
|
||||
<style>
|
||||
p {
|
||||
padding: 0 10px;
|
||||
|
12
ui/src/api/itemApi.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* 分页展示
|
||||
* @param data
|
||||
*/
|
||||
export function itemPage(data: any) {
|
||||
|
||||
return frontRequest.get("/api/item/page",{
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,4 @@
|
||||
/**
|
||||
* 登录
|
||||
* @param data
|
||||
*/
|
||||
export function loginFront(data:any) {
|
||||
frontRequest.post("/api/user/login", data).then(response =>{
|
||||
const user = userStore()
|
||||
user.frontToken = response.data.token
|
||||
frontRequest.get("/api/user/userInfo").then(response =>{
|
||||
user.frontUserInfo = response.data
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册
|
||||
* @param data
|
||||
|
62
ui/src/components/BarChart.vue
Normal file
@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="chart-container" ref="chartRef"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
const chartRef = ref(null)
|
||||
let chartInstance = null
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
// 使用 axios 请求数据
|
||||
const response = await adminRequest.get('/sys/item/view') // 修改为你自己的 API URL
|
||||
const data = response.data // 假设返回的数据是一个数组,格式类似于:[{ name: 'A', value: 100 }, ...]
|
||||
|
||||
// 更新图表配置
|
||||
const options = {
|
||||
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: data.map((item) => item.year),
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: data.map((item) => item.count),
|
||||
type: 'line',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
// 设置图表配置项
|
||||
chartInstance.setOption(options)
|
||||
} catch (error) {
|
||||
console.error('数据请求失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
chartInstance = echarts.init(chartRef.value)
|
||||
// 调用 fetchData 获取数据并更新图表
|
||||
fetchData()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose()
|
||||
chartInstance = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
}
|
||||
</style>
|
@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<div class="head">
|
||||
<div class="head_l">
|
||||
<!-- <img src="/icoimg.png" alt="收缩" />-->
|
||||
</div>
|
||||
<el-dropdown>
|
||||
<div class="head_r">
|
||||
@ -56,7 +55,6 @@
|
||||
<el-input v-model="state.dynamicValidateForm.newPassword" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
|
||||
<el-button type="primary" @click="submitForm(formRef)">确定修改</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@ -73,7 +71,6 @@ const drawer = ref(false)
|
||||
const state = reactive({
|
||||
dynamicValidateForm:{}
|
||||
})
|
||||
|
||||
/**
|
||||
* 退出登录
|
||||
*/
|
||||
@ -83,7 +80,6 @@ const logout = () => {
|
||||
router.push('/login');
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
* @param formEl
|
||||
@ -93,7 +89,7 @@ const submitForm = (formEl: FormInstance | undefined) => {
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
updatePasswordAdmin(state.dynamicValidateForm).then(result => {
|
||||
|
||||
ElMessage.success("修改成功")
|
||||
})
|
||||
}
|
||||
})
|
||||
|
90
ui/src/components/ImageUpload.vue
Normal file
@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
:action="state.path"
|
||||
:show-file-list="false"
|
||||
:on-success="handleSuccess"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<img v-if="state.imageUrl" :src="state.imageUrl" class="avatar" />
|
||||
<el-icon v-else class="avatar-uploader-icon">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
imageUrl: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
|
||||
// 创建 emit 事件
|
||||
import { UploadProps } from 'element-plus'
|
||||
const emit = defineEmits();
|
||||
const state = reactive({
|
||||
imageUrl: '',
|
||||
path:import.meta.env.VITE_API_FRONT_BASE_URL+"/api/upload"
|
||||
})
|
||||
// 监控 props.imageUrl 的变化
|
||||
watch(
|
||||
() => props.imageUrl, // 监听 imageUrl 的变化
|
||||
(newVal, oldVal) => {
|
||||
// console.log('imageUrl changed from', oldVal, 'to', newVal)
|
||||
// 这里可以执行相应的操作,比如清空本地 state.imageUrl 或更新界面
|
||||
state.imageUrl = newVal // 更新本地的 imageUrl
|
||||
}
|
||||
)
|
||||
onMounted(()=>{
|
||||
console.log(props.imageUrl)
|
||||
if (props.imageUrl){
|
||||
state.imageUrl = props.imageUrl
|
||||
}
|
||||
})
|
||||
|
||||
// 上传前的钩子,返回 false 可以阻止上传
|
||||
function beforeUpload(file) {
|
||||
const isImage = file.type.startsWith('image/')
|
||||
if (!isImage) {
|
||||
ElMessage.error('只能上传图片文件!')
|
||||
}
|
||||
return isImage
|
||||
}
|
||||
|
||||
// 上传成功后的回调
|
||||
const handleSuccess: UploadProps['onSuccess'] = (
|
||||
response,
|
||||
uploadFile
|
||||
) => {
|
||||
state.imageUrl = response.data.path
|
||||
ElMessage.success('上传成功')
|
||||
console.log("fanhui1",response)
|
||||
// 通过 emit 将路径传递给父组件
|
||||
emit('update:imageUrl', response.data.absolute);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.el-upload ){
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
.avatar{
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
}
|
||||
</style>
|
113
ui/src/components/e-editor.vue
Normal file
@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<div >
|
||||
<Toolbar
|
||||
style="border-bottom: 1px solid #ccc"
|
||||
:editor="editorRef"
|
||||
:defaultConfig="toolbarConfig"
|
||||
:mode="mode"
|
||||
/>
|
||||
<!-- 内容-->
|
||||
<Editor
|
||||
style="height: 500px; overflow-y: hidden"
|
||||
v-model="valueHtml"
|
||||
:defaultConfig="editorConfig"
|
||||
:mode="mode"
|
||||
@onCreated="handleCreated"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import '@wangeditor/editor/dist/css/style.css'
|
||||
import { onBeforeUnmount, ref, shallowRef, reactive } from 'vue'
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
// 编辑器实例,必须用 shallowRef
|
||||
const editorRef = shallowRef<InstanceType<typeof Editor> | null>(null)
|
||||
const emit = defineEmits();
|
||||
|
||||
const state = reactive({
|
||||
path: import.meta.env.VITE_API_FRONT_BASE_URL + '/api/upload',
|
||||
})
|
||||
const props = defineProps({
|
||||
content: { type: String, default: '' },
|
||||
});
|
||||
// 内容 HTML
|
||||
const valueHtml = ref<string>('')
|
||||
// watch监听 更新编辑器内容
|
||||
watch(
|
||||
() => props.content,
|
||||
(newContent) => {
|
||||
console.log('新值变化', newContent);
|
||||
valueHtml.value = newContent;
|
||||
emit( "update:content",valueHtml.value)
|
||||
},
|
||||
{ immediate: true } // immediate 确保第一次渲染时就执行一次回调
|
||||
);
|
||||
watch(() => valueHtml.value,
|
||||
(newContent) => {
|
||||
emit( "update:content",valueHtml.value)
|
||||
},
|
||||
{ immediate: true } // immediate 确保第一次渲染时就执行一次回调
|
||||
);
|
||||
|
||||
// 工具栏配置
|
||||
const toolbarConfig = {
|
||||
toolbarKeys: [
|
||||
// 一些常用的菜单 key
|
||||
"bold", // 加粗
|
||||
"italic", // 斜体
|
||||
"through", // 删除线
|
||||
"underline", // 下划线
|
||||
"bulletedList", // 无序列表
|
||||
"numberedList", // 有序列表
|
||||
"color", // 文字颜色
|
||||
"fontSize", // 字体大小
|
||||
"lineHeight", // 行高
|
||||
"uploadImage", // 上传图片
|
||||
"delIndent", // 缩进
|
||||
"indent", // 增进
|
||||
"deleteImage", //删除图片
|
||||
"divider", // 分割线
|
||||
"justifyCenter", // 居中对齐
|
||||
"justifyJustify", // 两端对齐
|
||||
"justifyLeft", // 左对齐
|
||||
"justifyRight", // 右对齐
|
||||
"undo", // 撤销
|
||||
"redo", // 重做
|
||||
"clearStyle", // 清除格式
|
||||
],
|
||||
};
|
||||
// 编辑器配置
|
||||
const editorConfig = {
|
||||
placeholder: '请输入内容...',
|
||||
MENU_CONF: {
|
||||
uploadImage: {
|
||||
server: state.path,
|
||||
fieldName: 'file',
|
||||
maxFileSize: 20 * 1024 * 1024, // 20MB
|
||||
allowedFileTypes: ['image/jpeg', 'image/png', 'image/gif'], // 允许的图片类型
|
||||
customInsert(res, insertFn) {
|
||||
if (res.code == 0) {
|
||||
insertFn(res.data, null, res.data);
|
||||
ElMessage.success("上传成功")
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
// 组件销毁时,及时销毁编辑器
|
||||
onBeforeUnmount(() => {
|
||||
const editor = editorRef.value
|
||||
if (editor == null) return
|
||||
editor.destroy()
|
||||
})
|
||||
|
||||
// 编辑器创建时的回调
|
||||
const handleCreated = (editor: InstanceType<typeof Editor>) => {
|
||||
editorRef.value = editor
|
||||
}
|
||||
|
||||
// 模式:default 或 simple
|
||||
const mode = 'default'
|
||||
</script>
|
@ -3,7 +3,7 @@
|
||||
<el-col :span="24">
|
||||
<el-carousel :height="height" motion-blur>
|
||||
<el-carousel-item v-for="item in state.item" :key="item">
|
||||
<el-image :src="item.img" fit="fill" />
|
||||
<el-image :src="item.path" fit="fill" />
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</el-col>
|
||||
@ -19,13 +19,18 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
const state = reactive({
|
||||
item:[
|
||||
{img:"https://img2.epetbar.com/2024-12/31/10/973909536cb5d8845253a118bd2ace20.jpg?x-oss-process=style/water"},
|
||||
{img:"https://img2.epetbar.com/2024-12/31/10/973909536cb5d8845253a118bd2ace20.jpg?x-oss-process=style/water"},
|
||||
{img:"https://img2.epetbar.com/2024-12/31/10/973909536cb5d8845253a118bd2ace20.jpg?x-oss-process=style/water"},
|
||||
{img:"https://img2.epetbar.com/2024-12/31/10/973909536cb5d8845253a118bd2ace20.jpg?x-oss-process=style/water"},
|
||||
]
|
||||
item:[]
|
||||
})
|
||||
|
||||
function init() {
|
||||
|
||||
}
|
||||
onMounted(()=>{
|
||||
frontRequest.get("/api/slides/page").then(res =>{
|
||||
state.item = res.data.list
|
||||
})
|
||||
})
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
:deep(.el-image){
|
||||
|
@ -1,11 +1,20 @@
|
||||
<!--详情底部-->
|
||||
<template>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="14">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-input v-model="state.content" class="commit " placeholder="发表神秘评论" />
|
||||
<div style="margin-bottom: 10px">
|
||||
<el-button color="#626aef" :dark="isDark" plain @click="comment">发表</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!--评论区-->
|
||||
<div class="comment" v-for="item in state.commentList">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="3">{{ item.userName }}</el-col>
|
||||
<el-col :span="3" ><span style="color: #598bd3;">{{ item.nickName }}</span></el-col>
|
||||
<el-col :span="6">
|
||||
<span class="time">
|
||||
{{item.createTime}}
|
||||
@ -17,6 +26,7 @@
|
||||
</div>
|
||||
<el-divider />
|
||||
</div>
|
||||
|
||||
</el-col>
|
||||
<!--热门-->
|
||||
<el-col :span="10">
|
||||
@ -26,14 +36,38 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Item2 from '~/components/front/item2.vue'
|
||||
import { useRoute } from 'vue-router';
|
||||
const route = useRoute();
|
||||
const state =reactive(<any>{
|
||||
commentList:[
|
||||
{userName:"小人",content:"的电影影视作品,影片的导演是凯莉",createTime:"2024-09-30 23:15:50"},
|
||||
{userName:"小人",content:"的电影影视作品,影片的导演是凯莉",createTime:"2024-09-30 23:15:50"},
|
||||
{userName:"小人",content:"的电影影视作品,影片的导演是凯莉",createTime:"2024-09-30 23:15:50"},
|
||||
{userName:"小人",content:"的电影影视作品,影片的导演是凯莉",createTime:"2024-09-30 23:15:50"},
|
||||
{userName:"小人",content:"的电影影视作品,影片的导演是凯莉",createTime:"2024-09-30 23:15:50"},
|
||||
]
|
||||
commentList:[],
|
||||
content:""
|
||||
})
|
||||
|
||||
const itemId = route.params.id;
|
||||
function init() {
|
||||
frontRequest.get("/api/comment/list",{
|
||||
params:{itemId:itemId}
|
||||
}).then(response =>{
|
||||
state.commentList = response.data
|
||||
console.log( response.data)
|
||||
})
|
||||
}
|
||||
|
||||
function comment() {
|
||||
if (state.content ==""){
|
||||
ElMessage.error("内容不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
frontRequest.post("/api/comment",{itemId:itemId,content:state.content}).then(response =>{
|
||||
ElMessage.success("发表成功")
|
||||
state.comment=""
|
||||
init()
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
@ -43,15 +77,27 @@ const state =reactive(<any>{
|
||||
word-wrap: break-word;
|
||||
.content{
|
||||
padding-top: 15px;
|
||||
font-size: 14px;
|
||||
color: #111;
|
||||
text-indent: 2em; /* 中文缩进 */
|
||||
}
|
||||
.time{
|
||||
color: #aaa;
|
||||
color: #598bd3;
|
||||
}
|
||||
}
|
||||
:deep(.el-image){
|
||||
border-radius: 10px;
|
||||
height: 420px;
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.commit{
|
||||
width: 500px;
|
||||
}
|
||||
:deep(.el-input__wrapper){
|
||||
|
||||
height: 100px;
|
||||
border-radius: 10px;
|
||||
|
||||
}
|
||||
</style>
|
||||
|
@ -3,22 +3,20 @@
|
||||
<el-row :gutter="20" class="info">
|
||||
<el-col :span="10">
|
||||
<!-- 详情图片-->
|
||||
<el-image :src="state.info.url" fit="contain" />
|
||||
<el-image :src="state.info.largePic" fit="contain" />
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<!-- 标题-->
|
||||
<h1>{{state.info.title}}</h1>
|
||||
<h1>{{state.info.title}} ( {{state.info.year}} ) </h1>
|
||||
<!-- 简介-->
|
||||
<div class="introduction" v-if="state.info.introduction">
|
||||
{{state.info.introduction}}
|
||||
</div>
|
||||
<!-- 价格-->
|
||||
<div class="tag" v-if="state.info.price">
|
||||
<span style="color: red;font-size: 26px"> ¥ <span style="font-weight: 700">{{state.info.price}}</span></span>
|
||||
</div>
|
||||
<div class="introduction" v-if="state.info.country"> <span style="font-weight: 700">编剧:</span> {{state.info.actor}} </div>
|
||||
<div class="introduction" v-if="state.info.country"> <span style="font-weight: 700">导演: </span> {{state.info.director}} </div>
|
||||
<div class="introduction" v-if="state.info.country"> <span style="font-weight: 700">制片国家/地区: </span> {{state.info.country}} </div>
|
||||
<div class="introduction" v-if="state.info.country"> <span style="font-weight: 700">电影评分: </span> {{state.info.ratingValue}} </div>
|
||||
|
||||
<!--标签-->
|
||||
<div class="tag">
|
||||
<el-tag v-for="tag in state.info.tags" :key="tag">{{tag}}</el-tag>
|
||||
<span style="font-weight: 700">导演: </span> <el-tag v-for="tag in (state.info.movieType || '').split(' ')" :key="tag" style="margin-right: 10px">{{ tag }}</el-tag>
|
||||
</div>
|
||||
<!--操作-->
|
||||
<div class="operate">
|
||||
@ -27,8 +25,8 @@
|
||||
<!-- 立即购买-->
|
||||
<el-button type="primary" @click="handleBuy" v-if="state.info.price">立即购买</el-button>
|
||||
<!-- 收藏-->
|
||||
<el-button type="primary" @click="handleFavorite">
|
||||
{{state.isFavorite?'已收藏':'收藏'}}
|
||||
<el-button style="width: 220px" type="primary" @click="handleFavorite">
|
||||
{{state.info.isFavorite? '已收藏':'未收藏'}}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
@ -70,10 +68,12 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
|
||||
const buyDrawer = ref(false)
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
import { useRoute } from 'vue-router';
|
||||
const route = useRoute();
|
||||
const itemId = route.params.id;
|
||||
const state =reactive(<any>{
|
||||
info:{
|
||||
id:1122334545,
|
||||
@ -92,6 +92,39 @@ const state =reactive(<any>{
|
||||
}
|
||||
})
|
||||
|
||||
// 深度监听 route.params.id
|
||||
watch( () => route.params.id,
|
||||
(newId) => {
|
||||
window.location.href = `/info/${newId}`
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
function init() {
|
||||
frontRequest.get(`/api/item/${itemId}`).then(response =>{
|
||||
state.info = response.data
|
||||
})
|
||||
}
|
||||
onMounted(()=>{
|
||||
init()
|
||||
})
|
||||
|
||||
/**
|
||||
* 用户收藏
|
||||
*/
|
||||
function handleFavorite() {
|
||||
if (state.info.isFavorite){
|
||||
frontRequest.post("/api/behavior/delete",{itemId:itemId,type:1}).then(res =>{
|
||||
ElMessage.success("取消成功")
|
||||
init()
|
||||
})
|
||||
}else {
|
||||
frontRequest.post("/api/behavior",{itemId:itemId,type:1}).then(res =>{
|
||||
ElMessage.success("收藏成功")
|
||||
init()
|
||||
})
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 购买按钮提交
|
||||
* @param formEl
|
||||
@ -107,7 +140,6 @@ const buySubmitForm = (formEl: FormInstance | undefined) => {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 购买按钮
|
||||
*/
|
||||
@ -130,13 +162,13 @@ const handleChange = () => {
|
||||
padding: 20px;
|
||||
h1{
|
||||
color: #11192d;
|
||||
font-size: 20px;
|
||||
font-size: 38px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.introduction{
|
||||
font-size: 14px;
|
||||
text-indent: 2em;
|
||||
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.tag{
|
||||
@ -147,7 +179,6 @@ const handleChange = () => {
|
||||
border-radius: 10px;
|
||||
height: 420px;
|
||||
margin-left: 30px;
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -1,27 +1,53 @@
|
||||
<!--商品-->
|
||||
<template>
|
||||
|
||||
<el-row v-if="isPage">
|
||||
<el-col :span="24">
|
||||
<div style="padding: 10px 10px 10px 10px;width: 350px;margin: 0 auto" >
|
||||
<el-input
|
||||
v-model="state.page.title"
|
||||
style="max-width: 600px"
|
||||
placeholder="寻找好图片"
|
||||
class="input-with-select"
|
||||
clearable
|
||||
@input="init"
|
||||
>
|
||||
<template #append>
|
||||
<el-button :icon="Search" @click="init"/>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-space wrap>
|
||||
<el-card
|
||||
v-for="i in 5" :key="i"
|
||||
v-for="item in getList" :key="item.id"
|
||||
class="box-card"
|
||||
@click="to()"
|
||||
@click="to(item.id)"
|
||||
shadow="hover" >
|
||||
<!-- 图片-->
|
||||
<el-image src="https://img2.epetbar.com/common/upload/commonfile/2020/03/20/0104650_205628.jpg" fit="fill" />
|
||||
<el-image :src="item.image" fit="fill" />
|
||||
<!-- 标题-->
|
||||
<div class="item-title ">澳大利亚原装进口自然馈赠Natures Gift 牛肉配方成犬粮 18kg</div>
|
||||
<div class="item-title ">{{ item.title }}</div>
|
||||
<!-- 价格&& 评分-->
|
||||
<div class="price-box">
|
||||
<span class="price-now ft16 " style="line-height: 1">¥998.00</span>
|
||||
<span class="ft14 c999 price-old" style="line-height: 1">¥1098.00</span>
|
||||
<span class="price-now ft16 " style="line-height: 1;">
|
||||
¥ {{item.price}}
|
||||
<!-- <el-rate-->
|
||||
<!-- v-model="item.ratingStarCount"-->
|
||||
<!-- disabled-->
|
||||
<!-- show-score-->
|
||||
<!-- text-color="#ff9900"-->
|
||||
<!-- score-template="{value}"-->
|
||||
<!-- />-->
|
||||
</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-space>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- <el-row class="pagination-container">-->
|
||||
<el-row v-if="isPage" class="pagination-container">
|
||||
<el-col :span="24">
|
||||
@ -29,30 +55,69 @@
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
@current-change="handleCurrentChange"
|
||||
:total="1000" />
|
||||
:total="state.total" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
import { itemPage } from '~/api/itemApi'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
const router = useRouter()
|
||||
const props = defineProps({
|
||||
isPage: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
getList: {
|
||||
type: Array,
|
||||
default: () => [], // 默认值为空数组
|
||||
},
|
||||
})
|
||||
// 计算属性来判断 list 是否为空,如果为空则使用 props.getList
|
||||
const getList = computed(() => {
|
||||
return state.getList.length > 0 ? state.getList : props.getList;
|
||||
});
|
||||
const state = reactive(<any>{
|
||||
total:0,
|
||||
page:{
|
||||
page:1,
|
||||
title:""
|
||||
},
|
||||
getList:[]
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
function init() {
|
||||
if (props.isPage){
|
||||
itemPage(state.page).then(res =>{
|
||||
state.getList = res.data.list
|
||||
state.total = res.data.total
|
||||
})
|
||||
}
|
||||
}
|
||||
/**
|
||||
*分页
|
||||
* @param val
|
||||
*/
|
||||
const handleCurrentChange = (val: number) => {
|
||||
console.log(`current page: ${val}`)
|
||||
state.page.page = val
|
||||
init()
|
||||
}
|
||||
|
||||
const to = () => {
|
||||
router.push("/front/info")
|
||||
/**
|
||||
* 跳转
|
||||
*/
|
||||
const to = (id:number) => {
|
||||
router.push(`/info/${id}`)
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
init()
|
||||
})
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.box-card{
|
||||
@ -76,6 +141,7 @@ const to = () => {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
.price-box{
|
||||
text-align: center;
|
||||
|
@ -4,18 +4,18 @@
|
||||
<el-col :span="24">
|
||||
<el-space wrap>
|
||||
<el-card
|
||||
v-for="i in 5" :key="i"
|
||||
v-for="item in state.commit" :key="state.id"
|
||||
class="box-card"
|
||||
@click="to()"
|
||||
@click="to(item.id)"
|
||||
shadow="hover" >
|
||||
<!--图片-->
|
||||
<img class="img" src="https://img2.epetbar.com/common/upload/commonfile/2020/03/20/0104650_205628.jpg" fit="fill" />
|
||||
<img class="img" :src="item.largePic" fit="fill" />
|
||||
<!-- 标题-->
|
||||
<div class="item-title ">澳大利亚原装进口自然馈赠Natures Gift 牛肉配方成犬粮 18kg</div>
|
||||
<div class="item-title ">{{item.title}}</div>
|
||||
<!-- 价格&& 评分-->
|
||||
<div class="price-box">
|
||||
<span class="price-now ft16 " >¥998.00</span>
|
||||
<span class="ft14 c999 price-old" >¥1098.00</span>
|
||||
<span class="price-now ft16 " >{{item.movieType}}</span>
|
||||
<span class="ft14 c999 price-old" >{{item.ratingValue}} 分</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-space>
|
||||
@ -25,9 +25,24 @@
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
const to = () => {
|
||||
router.push("/front/info")
|
||||
const state = reactive({
|
||||
commit:[]
|
||||
})
|
||||
|
||||
function init() {
|
||||
frontRequest.get("/api/item/commit").then(res =>{
|
||||
state.commit = res.data
|
||||
})
|
||||
|
||||
}
|
||||
onMounted(() =>{
|
||||
init()
|
||||
})
|
||||
|
||||
const to = (id:number) => {
|
||||
router.push(`/info/${id}`)
|
||||
}
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.box-card{
|
||||
@ -53,6 +68,7 @@ const to = () => {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
.price-box{
|
||||
text-align: center;
|
||||
@ -68,4 +84,7 @@ const to = () => {
|
||||
.ft14 {
|
||||
font-size: 14px;
|
||||
}
|
||||
.price-old{
|
||||
color: #fce04b;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,13 +1,27 @@
|
||||
<!--商品-->
|
||||
<template>
|
||||
|
||||
<div style="height: 20px"></div>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<span style="font-weight: 700;font-size: 26px">狗狗主粮</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<div class="containa">
|
||||
<el-space wrap>
|
||||
<div v-for="(i, index) in 10" :key="i">
|
||||
|
||||
<el-image style="width: 232px;height: 300px" v-if="index === 0 || index === 5" src="https://img2.epetbar.com/common/upload/commonfile/2020/03/20/0104650_205628.jpg" fit="fill" />
|
||||
|
||||
<div
|
||||
@click="to()"
|
||||
class="box-card"
|
||||
v-if="index === 0 || index === 5">
|
||||
<el-image
|
||||
style="width: 230px;height: 300px;"
|
||||
src="https://img2.epetbar.com/common/upload/commonfile/2020/03/20/0104650_205628.jpg"
|
||||
fit="fill" />
|
||||
</div>
|
||||
<el-card
|
||||
v-else
|
||||
class="box-card"
|
||||
@ -56,12 +70,17 @@ const to = () => {
|
||||
width: 232px;
|
||||
height: 300px
|
||||
}
|
||||
.box-card1{
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 300px
|
||||
}
|
||||
:deep(.el-image){
|
||||
display: block;
|
||||
width: 170px;
|
||||
height: 180px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 20px;
|
||||
|
||||
}
|
||||
.item-title{
|
||||
color: #333;
|
||||
|
@ -20,25 +20,27 @@
|
||||
</el-menu>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<el-button v-if="userStore().frontUserInfo" style="margin-top: 12px" type="primary" round @click="router.push('/login')">登录</el-button>
|
||||
<el-button v-if="!userStore().frontIsLogin"
|
||||
style="margin-top: 12px" type="primary"
|
||||
round @click="router.push('/login')">登录</el-button>
|
||||
<el-dropdown v-else>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<!-- <el-avatar :src="userStore().frontUserInfo.avatar" />-->
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<h6>{{ userStore().frontUserInfo.username }}</h6>
|
||||
<el-col :span="24">
|
||||
<h6>{{ userStore().frontUserInfo.nickName }}</h6>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item>个人中心</el-dropdown-item>
|
||||
<el-dropdown-item @click="to">个人中心</el-dropdown-item>
|
||||
<el-dropdown-item @click="logout">退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { getFrontList } from '~/utils/utils'
|
||||
@ -58,6 +60,10 @@ const logout = () => {
|
||||
router.push('/login')
|
||||
})
|
||||
}
|
||||
|
||||
function to() {
|
||||
router.push('/user')
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
:deep(.el-menu--horizontal) {
|
@ -7,6 +7,7 @@ export const frontRequest = axios.create({
|
||||
frontRequest.interceptors.request.use(
|
||||
function (config) {
|
||||
const token = userStore().frontToken
|
||||
|
||||
if (token !== null || token !== undefined) {
|
||||
//添加header
|
||||
config.headers.Authorization = token
|
||||
|
@ -1,164 +1,60 @@
|
||||
|
||||
<template>
|
||||
<div class="main">
|
||||
<div class="nav_left">
|
||||
<div class="logo">
|
||||
<img src="/logo.png" alt="后台管理系统">
|
||||
</div>
|
||||
<div class="nav_list">
|
||||
<div class="nav_title"><img src="/logo.png" alt="图标">导航管理</div>
|
||||
<div class="rArrl"></div>
|
||||
</div>
|
||||
<div class="nav_li">
|
||||
<ul>
|
||||
<li v-for="r of routes" :key="r.path">
|
||||
<img src="/logo.png" alt="图标"><RouterLink style="width: 100%; height: 45px; line-height: 45px" :to="r.path">{{ te(r.name) ? t(r.name) : r.name }}</RouterLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="nav_list">
|
||||
<div class="nav_title"><img src="/logo.png" alt="图标">权限管理</div>
|
||||
<div class="rArrl"></div>
|
||||
</div>
|
||||
<div class="nav_list">
|
||||
<div class="nav_title"><img src="/logo.png" alt="图标">模型设置</div>
|
||||
<div class="rArrl"></div>
|
||||
</div>
|
||||
<div class="nav_list">
|
||||
<div class="nav_title"><img src="/logo.png" alt="图标">内容管理</div>
|
||||
<div class="rArrl"></div>
|
||||
</div>
|
||||
<div class="nav_list">
|
||||
<div class="nav_title"><img src="/logo.png" alt="图标">会员管理</div>
|
||||
<div class="rArrl"></div>
|
||||
</div>
|
||||
<div class="nav_list">
|
||||
<div class="nav_title"><img src="/logo.png" alt="图标">模版管理</div>
|
||||
<div class="rArrl"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav_right">
|
||||
<Heads />
|
||||
<div class="content">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="fade" mode="out-in">
|
||||
<component :is="Component" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <Navigation />-->
|
||||
<!-- <div class="w-screen flex flex-col items-center justify-center">-->
|
||||
<!-- <router-view v-slot="{ Component }">-->
|
||||
<!-- <transition name="fade" mode="out-in">-->
|
||||
<!-- <component :is="Component" />-->
|
||||
<!-- </transition>-->
|
||||
<!-- </router-view>-->
|
||||
<!-- </div>-->
|
||||
<el-container>
|
||||
<el-header>
|
||||
<heads></heads>
|
||||
</el-header>
|
||||
<el-container>
|
||||
<el-aside width="200px" >
|
||||
<el-menu
|
||||
:default-active="state.activeIndex"
|
||||
router
|
||||
@select="handleSelect"
|
||||
>
|
||||
<el-menu-item
|
||||
v-for="r in getAdminList()"
|
||||
:key="r.name"
|
||||
:index="r.path"
|
||||
>
|
||||
<component class="icons" :is="r.icon" />
|
||||
<template #title>{{ r.name }}</template>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<el-main class="main">
|
||||
<router-view ></router-view>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { getAdminList} from '~/utils/utils'
|
||||
import { useRoute } from 'vue-router'
|
||||
// 获取当前路由信息
|
||||
const route = useRoute()
|
||||
const state = reactive({
|
||||
activeIndex:'/admin'
|
||||
})
|
||||
const handleSelect = (key: string, keyPath: string[]) => {
|
||||
state.activeIndex = key
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
// 初始化activeIndex为当前路由的path路径,方便在菜单栏高<EFBFBD><EFBFBD>显示选中项
|
||||
state.activeIndex = route.path
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.main{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
.nav_left{
|
||||
width: 15%;
|
||||
height: 100vh;
|
||||
background: #111c43;
|
||||
overflow-y: auto;
|
||||
.logo{
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
border-bottom: 0.5px solid #293356;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.logo img{
|
||||
width: 85%;
|
||||
height: 40px;
|
||||
background: #293356;
|
||||
color: #FFFFFF;
|
||||
font-size: 12px;
|
||||
}
|
||||
.nav_list{
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 45px;
|
||||
.nav_title{
|
||||
font-size: 14px;
|
||||
color: #a3aed1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.nav_title img{
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: #333333;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.rArrl{
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-top: 1px solid #a3aed1;
|
||||
border-right: 1px solid #a3aed1;
|
||||
margin-top: 9px;
|
||||
-webkit-transform: translate3d(0, -50%, 0) rotate(45deg);
|
||||
transform: translate3d(0, -50%, 0) rotate(45deg);
|
||||
}
|
||||
}
|
||||
.nav_li{
|
||||
width: 100%;
|
||||
padding-left: 16%;
|
||||
background: #1a223f;
|
||||
}
|
||||
.nav_li ul li{
|
||||
font-size: 12px;
|
||||
color: #a3aed1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 45px;
|
||||
width: 100%;
|
||||
}
|
||||
.nav_li ul li img{
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: #333333;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.nav_right{
|
||||
width: 85%;
|
||||
height: 100vh;
|
||||
background: #f0f1f7;
|
||||
.content{
|
||||
width: 100%;
|
||||
height: auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
height: calc(100vh - 80px);
|
||||
background-color: #f2f4f8;
|
||||
}
|
||||
.icons{
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
</style>
|
||||
<script setup lang="ts">
|
||||
import Heads from '~/components/Heads.vue'
|
||||
import { getRoutes } from '@/plugins/router'
|
||||
const { te, t } = useI18n()
|
||||
const routes = getRoutes()
|
||||
.filter((r) => !r.path.includes('notFound'))
|
||||
.map((r) => {
|
||||
let { path, name } = r
|
||||
if (path === safeResolve('/')) {
|
||||
return { path, name: 'home' }
|
||||
}
|
||||
if (!name) {
|
||||
name = path
|
||||
}
|
||||
return { path, name: name.toString().slice(1).replaceAll('/', ' · ') }
|
||||
})
|
||||
const $route = useRoute()
|
||||
</script>
|
||||
|
@ -1,59 +1,5 @@
|
||||
|
||||
|
||||
<template>
|
||||
<el-container>
|
||||
<el-header>
|
||||
<heads></heads>
|
||||
</el-header>
|
||||
<el-container>
|
||||
<el-aside width="200px" >
|
||||
<el-menu
|
||||
:default-active="state.activeIndex"
|
||||
router
|
||||
@select="handleSelect"
|
||||
>
|
||||
<el-menu-item
|
||||
v-for="r in getAdminList()"
|
||||
:key="r.name"
|
||||
:index="r.path"
|
||||
>
|
||||
<component class="icons" :is="r.icon" />
|
||||
<template #title>{{ r.name }}</template>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<el-main class="main">
|
||||
<router-view ></router-view>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
<router-view />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { getAdminList} from '~/utils/utils'
|
||||
import { useRoute } from 'vue-router'
|
||||
// 获取当前路由信息
|
||||
const route = useRoute()
|
||||
const state = reactive({
|
||||
activeIndex:'/admin'
|
||||
})
|
||||
|
||||
const handleSelect = (key: string, keyPath: string[]) => {
|
||||
state.activeIndex = key
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
// 初始化activeIndex为当前路由的path路径,方便在菜单栏高<EFBFBD><EFBFBD>显示选中项
|
||||
state.activeIndex = route.path
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.main{
|
||||
width: 100%;
|
||||
height: calc(100vh - 80px);
|
||||
background-color: #f2f4f8;
|
||||
}
|
||||
.icons{
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
</style>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<div class="common-layout">
|
||||
<el-container>
|
||||
<el-header>
|
||||
<nav-navigation1></nav-navigation1>
|
||||
<nav-navigation></nav-navigation>
|
||||
</el-header>
|
||||
<el-main class="main">
|
||||
<div class="container">
|
||||
|
@ -3,14 +3,14 @@
|
||||
<div class="common-layout">
|
||||
<el-container>
|
||||
<el-header>
|
||||
<nav-navigation1></nav-navigation1>
|
||||
<nav-navigation></nav-navigation>
|
||||
</el-header>
|
||||
<el-main class="main">
|
||||
<div class="container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-menu
|
||||
default-active=""
|
||||
:default-active="state.activeIndex"
|
||||
router
|
||||
>
|
||||
<el-menu-item v-for="item in state.userMenu" :index="item.path">
|
||||
@ -28,19 +28,19 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
const state = reactive({
|
||||
activeIndex:'/user',
|
||||
userMenu:[
|
||||
{name: '个人中心', path: '/front/user'},
|
||||
{name: '用户订单', path: '/front/user/order'},
|
||||
{name: '个人中心', path: '/user'},
|
||||
{name: '用户收藏', path: '/order'},
|
||||
{name: '用户文创', path: '/fronttopic'},
|
||||
]
|
||||
})
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
.main{
|
||||
width: 100%;
|
||||
height: calc(100vh - 60px);
|
||||
|
198
ui/src/pages/admin/cate.vue
Normal file
@ -0,0 +1,198 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--增、查交易记录的按钮-->
|
||||
<el-button type="primary" round @click="openAddDialog" size="small">添加</el-button>
|
||||
<!--表格-->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-table :data="state.getList">
|
||||
<!-- 列表结开始-->
|
||||
<el-table-column prop="id" label="编号" align="center" />
|
||||
<el-table-column prop="name" label="分类名称" align="center" />
|
||||
<!-- <el-table-column prop="largePic" label="图片" align="center" >-->
|
||||
<!-- <template #default="{ row }">-->
|
||||
<!-- <!– 点击图片后显示弹框预览 –>-->
|
||||
<!-- <el-image-->
|
||||
<!-- :src="row.path"-->
|
||||
<!-- fit="cover"-->
|
||||
<!-- style="width: 50px;height: 50px"-->
|
||||
<!-- />-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!--列表结束-->
|
||||
<el-table-column label="操作" align="center" >
|
||||
<template #default="scope">
|
||||
<el-button @click="edit(scope.row)" size="small">编辑</el-button>
|
||||
<el-button @click="del(scope.row.id)" type="danger" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-slot:empty>
|
||||
<el-empty description="数据去外太空了~" />
|
||||
</template>
|
||||
</el-table>
|
||||
<!-- 分页控件 -->
|
||||
<el-pagination
|
||||
v-if="state.query.total > 0"
|
||||
:current-page="state.query.page"
|
||||
:page-size="state.query.limit"
|
||||
:total="state.query.total"
|
||||
background layout="prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog v-model="state.dialogVisible" title="新增" width="50%">
|
||||
<el-form
|
||||
:model="state.formData"
|
||||
ref="formRef"
|
||||
label-width="100px"
|
||||
>
|
||||
<!-- 表单开始===============================================================================================================================-->
|
||||
<el-form-item label="分类名称" prop="name" :rules="[{ required: true, message: '请输入分类名称', trigger: 'blur' }]">
|
||||
<el-input v-model="state.formData.name"/>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="图片" prop="image" >-->
|
||||
<!-- <image-upload @update:imageUrl="handleImageUrl" :image-url="state.formData.image"></image-upload>-->
|
||||
<!-- </el-form-item>-->
|
||||
|
||||
<!--表单结束===============================================================================================================================-->
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="state.dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveTransaction(formRef)">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// 交易记录数据
|
||||
import type { FormInstance } from 'element-plus'
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const state = reactive({
|
||||
route:"sys/categories",
|
||||
dialogVisible:false,
|
||||
getList: [],//获取数据
|
||||
query:{
|
||||
total: 0, // 总记录数
|
||||
page: 1, // 当前页码
|
||||
limit: 10, // 每页显示的记录数
|
||||
},
|
||||
formData:{}
|
||||
})
|
||||
|
||||
// 接收子组件传递的路径
|
||||
function handleImageUrl(path: string) {
|
||||
state.formData.image = path
|
||||
}
|
||||
|
||||
// 获取交易记录
|
||||
const init = () => {
|
||||
adminRequest.get(`${state.route}/page`, {
|
||||
params: state.query
|
||||
}).then((res:any) => {
|
||||
state.getList = res.data.list
|
||||
state.query.total = res.data.total
|
||||
})
|
||||
}
|
||||
// 新增交易记录
|
||||
const openAddDialog = () => {
|
||||
// formData = {
|
||||
// name:"",
|
||||
// path:"",
|
||||
// }
|
||||
state.dialogVisible = true
|
||||
}
|
||||
// 编辑交易记录
|
||||
const edit = (row: any) => {
|
||||
state.formData = row
|
||||
state.dialogVisible = true
|
||||
}
|
||||
|
||||
// 保存记录
|
||||
const saveTransaction = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
if (state.formData.id) {
|
||||
// 更新交易记录
|
||||
adminRequest.put(`${state.route}`, state.formData).then(()=>{
|
||||
init()
|
||||
state.dialogVisible = false
|
||||
ElMessage.success("提交成功~")
|
||||
})
|
||||
} else {
|
||||
// 新增交易记录
|
||||
adminRequest.post(`${state.route}`, state.formData).then(() =>{
|
||||
init()
|
||||
state.dialogVisible = false
|
||||
ElMessage.success("提交成功~")
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 删除交易记录
|
||||
const del = async (id: number) => {
|
||||
try {
|
||||
await adminRequest.delete(`${state.route}/${id}`)
|
||||
init()
|
||||
ElMessage.success("删除成功~")
|
||||
} catch (error) {
|
||||
ElMessage.error("删除失败~")
|
||||
}
|
||||
}
|
||||
|
||||
// 页码改变时的处理函数
|
||||
const handlePageChange = (page: number) => {
|
||||
state.query.page = page
|
||||
init()
|
||||
}
|
||||
// 页面加载时获取交易记录
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
:deep(.el-upload) {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
:deep(.el-upload:hover) {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
.avatar{
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
</style>
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "admin"
|
||||
}
|
||||
}
|
||||
</route>
|
272
ui/src/pages/admin/comment.vue
Normal file
@ -0,0 +1,272 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--增、查交易记录的按钮-->
|
||||
<!-- <el-button type="primary" round @click="openAddDialog" size="small">添加</el-button>-->
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="24">-->
|
||||
<!-- <el-form :inline="true" :model="state.query" class="demo-form-inline">-->
|
||||
<!-- <el-form-item label="电影名称:">-->
|
||||
<!-- <el-input v-model="state.query.title" placeholder="请输入电影名称" clearable @input="init" />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-form>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
<!--表格-->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-table :data="state.getList">
|
||||
<!-- 列表结开始-->
|
||||
<el-table-column prop="id" label="编号" align="center" width="120px"/>
|
||||
<el-table-column prop="itemDTO.title" label="电影名称" align="center" width="120px"/>
|
||||
<el-table-column prop="nickName" label="用户昵称" align="center" width="120px" />
|
||||
<el-table-column prop="itemDTO.largePic" label="电影图片" align="center" width="100" >
|
||||
<template #default="{ row }">
|
||||
<!-- 点击图片后显示弹框预览 -->
|
||||
<el-image
|
||||
:src="row.itemDTO.largePic"
|
||||
fit="cover"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="score" label="评分" align="center" width="120px"/>
|
||||
<el-table-column prop="content" label="评论内容" align="center">
|
||||
<template #default="scope">
|
||||
<div style="width: 100%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;">
|
||||
{{ scope.row.content }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
</el-table-column>
|
||||
<!--列表结束-->
|
||||
<el-table-column label="操作" align="center" width="120px">
|
||||
<template #default="scope">
|
||||
<el-button @click="del(scope.row.id)" type="danger" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-slot:empty>
|
||||
<el-empty description="数据去外太空了~" />
|
||||
</template>
|
||||
</el-table>
|
||||
<!-- 分页控件 -->
|
||||
<el-pagination
|
||||
v-if="state.query.total > 0"
|
||||
:current-page="state.query.page"
|
||||
:page-size="state.query.limit"
|
||||
:total="state.query.total"
|
||||
background layout="prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog v-model="state.dialogVisible" title="新增" width="50%">
|
||||
<el-form
|
||||
:model="formData"
|
||||
ref="formRef"
|
||||
label-width="100px"
|
||||
>
|
||||
|
||||
<!-- 表单开始===============================================================================================================================-->
|
||||
|
||||
<el-form-item label="电影名称" prop="title" :rules="[{ required: true, message: '请输入电影名称', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.title"/>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="电影副标题" prop="cardSubtitle" :rules="[{ required: true, message: '请输入电影副标题', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.cardSubtitle"/>
|
||||
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="国家" prop="country" :rules="[{ required: true, message: '请输入国家', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.country"/>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="电影类型" prop="movieType" :rules="[{ required: true, message: '请输入电影类型', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.movieType" placeholder="多个请空格分隔"/>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="导演" prop="director" :rules="[{ required: true, message: '请输入导演', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.director" placeholder="多个请空格分隔"/>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="主演" prop="actor" :rules="[{ required: true, message: '请输入主演', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.actor" placeholder="多个请空格分隔"/>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="上映时间" prop="year" :rules="[{ required: true, message: '请输入上映时间', trigger: 'blur' }]">
|
||||
|
||||
<el-select v-model="formData.year" placeholder="请选择上映年份">
|
||||
<!-- 使用 v-for 动态生成年份列表 -->
|
||||
<el-option
|
||||
v-for="year in years"
|
||||
:key="year"
|
||||
:label="year"
|
||||
:value="year"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="图片" prop="largePic" :rules="[{ required: true, message: '请插入图片', trigger: 'blur' }]">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
|
||||
:show-file-list="false"
|
||||
:on-success="handleAvatarSuccess"
|
||||
>
|
||||
<img v-if="formData.largePic" :src="formData.largePic" class="avatar" />
|
||||
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="评分" prop="ratingValue" :rules="[{ required: true, message: '请输入评分', trigger: 'blur' }]">
|
||||
<el-input-number v-model="formData.ratingValue" :min="1" :max="10" :precision="1" />
|
||||
</el-form-item>
|
||||
<!--表单结束===============================================================================================================================-->
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="state.dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveTransaction(formRef)">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// 交易记录数据
|
||||
import { UploadProps } from 'element-plus'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
const formRef = ref<FormInstance>()
|
||||
const state = reactive({
|
||||
route:"sys/comment",
|
||||
dialogVisible:false,
|
||||
getList: [],//获取数据
|
||||
query:{
|
||||
total: 0, // 总记录数
|
||||
page: 1, // 当前页码
|
||||
limit: 10, // 每页显示的记录数
|
||||
title:"",
|
||||
type:"0",
|
||||
}
|
||||
})
|
||||
|
||||
// 生成年份列表 1990-2015
|
||||
const years = [];
|
||||
for (let year = 1990; year <= 2015; year++) {
|
||||
years.push(year.toString());
|
||||
}
|
||||
const handleAvatarSuccess: UploadProps['onSuccess'] = (
|
||||
response,
|
||||
uploadFile
|
||||
) => {
|
||||
formData.largePic = URL.createObjectURL(uploadFile.raw!)
|
||||
}
|
||||
|
||||
// 表单数据
|
||||
let formData = reactive(<any>{})
|
||||
// 获取交易记录
|
||||
const init = () => {
|
||||
adminRequest.get(`${state.route}/page`, {
|
||||
params: state.query
|
||||
}).then((res:any) => {
|
||||
state.getList = res.data.list
|
||||
state.query.total = res.data.total
|
||||
|
||||
console.log(state.getList)
|
||||
})
|
||||
}
|
||||
// 新增交易记录
|
||||
const openAddDialog = () => {
|
||||
formData = {}
|
||||
state.dialogVisible = true
|
||||
}
|
||||
// 编辑交易记录
|
||||
const edit = (row: any) => {
|
||||
formData = row
|
||||
state.dialogVisible = true
|
||||
}
|
||||
|
||||
// 保存记录
|
||||
const saveTransaction = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
if (formData.id) {
|
||||
// 更新交易记录
|
||||
adminRequest.put(`${state.route}`, formData)
|
||||
} else {
|
||||
// 新增交易记录
|
||||
adminRequest.post(`${state.route}`, formData)
|
||||
}
|
||||
init()
|
||||
state.dialogVisible = false
|
||||
ElMessage.success("提交成功~")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 删除交易记录
|
||||
const del = async (id: number) => {
|
||||
try {
|
||||
await adminRequest.delete(`${state.route}/${id}`)
|
||||
init()
|
||||
ElMessage.success("删除成功~")
|
||||
} catch (error) {
|
||||
ElMessage.error("删除失败~")
|
||||
}
|
||||
}
|
||||
|
||||
// 页码改变时的处理函数
|
||||
const handlePageChange = (page: number) => {
|
||||
state.query.page = page
|
||||
init()
|
||||
}
|
||||
// 页面加载时获取交易记录
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
:deep(.el-upload) {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
:deep(.el-upload:hover) {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
.avatar{
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
</style>
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "admin"
|
||||
}
|
||||
}
|
||||
</route>
|
@ -1,100 +1,122 @@
|
||||
<template>
|
||||
<!-- 客户类型销售情况饼图 -->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<v-chart class="chart" :option="customerTypeSalesOption" autoresize />
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>平台情况</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-row :gutter="20">
|
||||
<el-col v-for="item in state.list" :span="12" :key="item.name">
|
||||
<div class="list-box">
|
||||
<p class="first-p">{{ item.name }}</p>
|
||||
<p class="second-p">
|
||||
<span>{{ item.value }}</span>{{ item.tag }}
|
||||
</p>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 客户购买频次、周期、金额漏斗图 -->
|
||||
<el-row>
|
||||
|
||||
<el-col :span="24">
|
||||
<v-chart class="chart" :option="customerPurchaseFunnelOption" autoresize />
|
||||
<bar-chart />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import VChart from 'vue-echarts'
|
||||
import { PieChart, FunnelChart } from 'echarts/charts'
|
||||
import { TitleComponent, TooltipComponent, LegendComponent, GridComponent } from 'echarts/components'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { use } from 'echarts/core'
|
||||
|
||||
// 引入并使用图表组件
|
||||
use([CanvasRenderer, PieChart, FunnelChart, TitleComponent, TooltipComponent, LegendComponent, GridComponent])
|
||||
import BarChart from '~/components/BarChart.vue'
|
||||
|
||||
// 模拟数据
|
||||
const customerData = ref([
|
||||
{ customerType: '家庭用户', salesAmount: 5000 },
|
||||
{ customerType: '商用用户', salesAmount: 8000 },
|
||||
{ customerType: '工业用户', salesAmount: 12000 },
|
||||
// 更多数据...
|
||||
])
|
||||
const state = reactive(<any>{
|
||||
list: [],
|
||||
row: []
|
||||
});
|
||||
|
||||
const purchaseData = ref([
|
||||
{ stage: '潜在客户', value: 300 },
|
||||
{ stage: '关注产品', value: 200 },
|
||||
{ stage: '询价客户', value: 150 },
|
||||
{ stage: '下单客户', value: 100 },
|
||||
{ stage: '支付客户', value: 70 },
|
||||
// 更多数据...
|
||||
])
|
||||
|
||||
// 客户类型销售情况饼图
|
||||
const customerTypeSalesOption = ref({
|
||||
title: { text: '客户类型销售情况', left: 'center' },
|
||||
tooltip: { trigger: 'item' },
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
data: customerData.value.map(item => item.customerType)
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '销售额',
|
||||
type: 'pie',
|
||||
radius: '55%',
|
||||
data: customerData.value.map(item => ({
|
||||
value: item.salesAmount,
|
||||
name: item.customerType
|
||||
})),
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
onMounted(() => {
|
||||
adminRequest.get("/sys/item/query").then(res => {
|
||||
state.list = res.data;
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
const chartData = ref({
|
||||
columns: ['date', 'sales'],
|
||||
rows: [
|
||||
{ date: '2021-01-01', sales: 100 },
|
||||
{ date: '2021-02-01', sales: 200 },
|
||||
{ date: '2021-03-01', sales: 150 },
|
||||
{ date: '2021-04-01', sales: 220 },
|
||||
{ date: '2021-05-01', sales: 180 }
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
// 客户购买漏斗图(频次、周期、金额)
|
||||
const customerPurchaseFunnelOption = ref({
|
||||
title: { text: '客户购买漏斗分析', left: 'center' },
|
||||
tooltip: { trigger: 'item' },
|
||||
legend: {
|
||||
data: ['客户转化情况']
|
||||
// 图表配置
|
||||
const chartSettings = ref({
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
name: '日期'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '客户转化情况',
|
||||
type: 'funnel',
|
||||
left: '10%',
|
||||
width: '80%',
|
||||
data: purchaseData.value.map(item => ({
|
||||
value: item.value,
|
||||
name: item.stage
|
||||
}))
|
||||
}
|
||||
]
|
||||
})
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '销售量'
|
||||
},
|
||||
title: {
|
||||
text: '销售趋势'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.list-box {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
border-radius: 10px;
|
||||
background-image: url('/a1.png');
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
.first-p {
|
||||
color: white;
|
||||
padding-bottom: 0.5vw;
|
||||
font-size: 1.5vw;
|
||||
text-align: center;
|
||||
padding-top: 30px;
|
||||
}
|
||||
.second-p {
|
||||
color: white;
|
||||
display: flex;
|
||||
margin-bottom: 5px;
|
||||
font-size: 1.4vw;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
span {
|
||||
margin-right: 0.3vw;
|
||||
font-size: 2vw;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "admin"
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
@ -5,8 +5,8 @@
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form :inline="true" :model="state.query" class="demo-form-inline">
|
||||
<el-form-item label="序号:">
|
||||
<el-input v-model="state.query.itemId" placeholder="请输入序号" clearable @input="init" />
|
||||
<el-form-item label="图书名称:">
|
||||
<el-input v-model="state.query.title" placeholder="请输入图书名称" clearable @input="init" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
@ -14,133 +14,141 @@
|
||||
<!--表格-->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-table :data="state.getList">
|
||||
<!-- 列表结开始-->
|
||||
<el-table-column prop="id" label="标号"/>
|
||||
<el-table-column prop="itemId" label="序号"/>
|
||||
<el-table-column prop="orderId" label="订单唯一标识"/>
|
||||
<el-table-column prop="orderDistance" label="订单距离"/>
|
||||
<el-table-column prop="orderStartTime" label="订单开始时间"/>
|
||||
<el-table-column prop="orderEndTime" label="订单结束时间"/>
|
||||
<el-table-column prop="longitude" label="经度"/>
|
||||
<el-table-column prop="dimension" label="维度"/>
|
||||
<el-table-column prop="insertTime" label="插入时间"/>
|
||||
<el-table-column prop="carId" label="车辆编号"/>
|
||||
<!--列表结束-->
|
||||
<el-table-column label="操作">
|
||||
<template #default="scope">
|
||||
<el-button @click="edit(scope.row)" size="small">编辑</el-button>
|
||||
<el-button @click="del(scope.row.id)" type="danger" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-slot:empty>
|
||||
<el-empty description="数据去外太空了~" />
|
||||
</template>
|
||||
</el-table>
|
||||
<el-tabs v-model="state.query.status" class="demo-tabs" @tab-click="handleClick">
|
||||
<el-tab-pane v-for="item in state.getStatus" :label="item.name" :name="item.status">
|
||||
<el-table v-loading="loading" :data="state.getList">
|
||||
<!-- 列表结开始-->
|
||||
<el-table-column prop="id" label="编号" align="center" />
|
||||
<el-table-column prop="title" label="名称" align="center" />
|
||||
<el-table-column prop="categoryName" label="类别" align="center" />
|
||||
<el-table-column prop="largePic" label="图片" align="center" >
|
||||
<template #default="{ row }">
|
||||
<!-- 点击图片后显示弹框预览 -->
|
||||
<el-image
|
||||
style="width: 50px;height: 50px"
|
||||
:src="row.image"
|
||||
fit="cover"
|
||||
:preview-src-list="[row.image]"
|
||||
:preview-teleported="true"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="price" label="价格" align="center" />
|
||||
|
||||
<el-table-column prop="stockQuantity" label="库存数量" align="center" />
|
||||
<el-table-column prop="view" label="预览" align="center" />
|
||||
<el-table-column prop="tag" label="作者" align="center" />
|
||||
<el-table-column prop="sort" label="排序" align="center" />
|
||||
<!--列表结束-->
|
||||
<el-table-column label="操作" align="center" >
|
||||
<template #default="scope">
|
||||
<el-button @click="edit(scope.row)" size="small">编辑</el-button>
|
||||
<el-button @click="del(scope.row.id)" type="danger" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-slot:empty>
|
||||
<el-empty description="数据去外太空了~" />
|
||||
</template>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<!-- 分页控件 -->
|
||||
<el-pagination
|
||||
v-if="state.query.total > 0"
|
||||
:current-page="state.query.page"
|
||||
:page-size="state.query.size"
|
||||
:page-size="state.query.limit"
|
||||
:total="state.query.total"
|
||||
background layout="prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog v-model="state.dialogVisible" title="新增交易记录" width="50%">
|
||||
<el-form :model="formData" ref="form" label-width="100px">
|
||||
<el-dialog v-model="state.dialogVisible" title="新增" width="50%">
|
||||
<el-form
|
||||
:model="formData"
|
||||
ref="formRef"
|
||||
label-width="100px"
|
||||
>
|
||||
|
||||
<!-- 表单开始===============================================================================================================================-->
|
||||
<el-form-item label="图片" prop="image" :rules="[{ required: true, message: '请插入图片', trigger: 'blur' }]">
|
||||
|
||||
<el-form-item label="标号" prop="id" :rules="[{ required: true, message: '请输入标号', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.id" />
|
||||
<image-upload @update:imageUrl="handleImageUrl" :image-url="state.formData.image"></image-upload>
|
||||
|
||||
|
||||
</el-form-item>
|
||||
<el-form-item label="图书名称" prop="title" :rules="[{ required: true, message: '请输入图书名称', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.title"/>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="序号" prop="itemId" :rules="[{ required: true, message: '请输入序号', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.itemId" />
|
||||
|
||||
<el-form-item label="排行榜" prop="sort" :rules="[{ required: true, message: '请输入排行榜', trigger: 'blur' }]">
|
||||
<el-input-number v-model="state.formData.sort" :min="1" :max="500000" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="订单唯一标识" prop="orderId" :rules="[{ required: true, message: '请输入订单唯一标识', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.orderId" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="订单距离" prop="orderDistance" :rules="[{ required: true, message: '请输入订单距离', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.orderDistance" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="订单开始时间" prop="orderStartTime" :rules="[{ required: true, message: '请输入订单开始时间', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.orderStartTime" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="订单结束时间" prop="orderEndTime" :rules="[{ required: true, message: '请输入订单结束时间', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.orderEndTime" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="经度" prop="longitude" :rules="[{ required: true, message: '请输入经度', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.longitude" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="维度" prop="dimension" :rules="[{ required: true, message: '请输入维度', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.dimension" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="插入时间" prop="insertTime" :rules="[{ required: true, message: '请输入插入时间', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.insertTime" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="车辆编号" prop="carId" :rules="[{ required: true, message: '请输入车辆编号', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.carId" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<!--表单结束===============================================================================================================================-->
|
||||
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="state.dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveTransaction">保存</el-button>
|
||||
<el-button type="primary" @click="saveTransaction(formRef)">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// 交易记录数据
|
||||
|
||||
import type {FormInstance, TabsPaneContext} from 'element-plus'
|
||||
const formRef = ref<FormInstance>()
|
||||
const loading = ref(true)
|
||||
const state = reactive({
|
||||
route:"/api/transactions",
|
||||
route:"sys/item",
|
||||
getStatus:['上架','下架','库存警告','售空'],
|
||||
getStatus:[
|
||||
{name:"上架",status:0},
|
||||
{name:"下架",status:1},
|
||||
{name:"库存警告",status:2},
|
||||
{name:"售空",status:3},
|
||||
],
|
||||
dialogVisible:false,
|
||||
getList: [],//获取数据
|
||||
query:{
|
||||
total: 0, // 总记录数
|
||||
page: 1, // 当前页码
|
||||
size: 10, // 每页显示的记录数
|
||||
itemId:"",
|
||||
}
|
||||
limit: 5, // 每页显示的记录数
|
||||
title:"",
|
||||
status:0,
|
||||
},
|
||||
formData:{}
|
||||
})
|
||||
// 表单数据
|
||||
let formData = reactive(<any>{})
|
||||
|
||||
|
||||
// 接收子组件传递的路径
|
||||
function handleImageUrl(path: string) {
|
||||
state.formData.path = path
|
||||
|
||||
}
|
||||
|
||||
// 获取交易记录
|
||||
const init = () => {
|
||||
adminRequest.get(`${state.route}`, {
|
||||
adminRequest.get(`${state.route}/page`, {
|
||||
params: state.query
|
||||
}).then((res:any) => {
|
||||
state.getList = res.data.data
|
||||
state.query.total = res.data.page.total
|
||||
state.getList = res.data.list
|
||||
state.query.total = res.data.total
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
// 新增交易记录
|
||||
const openAddDialog = () => {
|
||||
formData = {}
|
||||
formData = {
|
||||
sort:1
|
||||
}
|
||||
state.dialogVisible = true
|
||||
}
|
||||
// 编辑交易记录
|
||||
@ -148,21 +156,30 @@ const edit = (row: any) => {
|
||||
formData = row
|
||||
state.dialogVisible = true
|
||||
}
|
||||
// 保存记录
|
||||
const saveTransaction = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
if (formData.id) {
|
||||
// 更新交易记录
|
||||
adminRequest.put(`${state.route}`, formData).then(() =>{
|
||||
init()
|
||||
state.dialogVisible = false
|
||||
ElMessage.success("提交成功~")
|
||||
})
|
||||
} else {
|
||||
// 新增交易记录
|
||||
adminRequest.post(`${state.route}`, formData).then(() =>{
|
||||
init()
|
||||
state.dialogVisible = false
|
||||
ElMessage.success("提交成功~")
|
||||
})
|
||||
}
|
||||
|
||||
// 保存交易记录
|
||||
const saveTransaction = async () => {
|
||||
if (formData.id) {
|
||||
// 更新交易记录
|
||||
await adminRequest.put(`${state.route}/${formData.id}`, formData)
|
||||
} else {
|
||||
// 新增交易记录
|
||||
await adminRequest.post(`${state.route}`, formData)
|
||||
}
|
||||
init()
|
||||
state.dialogVisible = false
|
||||
ElMessage.success("提交成功~")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 删除交易记录
|
||||
const del = async (id: number) => {
|
||||
try {
|
||||
@ -179,6 +196,14 @@ const handlePageChange = (page: number) => {
|
||||
state.query.page = page
|
||||
init()
|
||||
}
|
||||
|
||||
const handleClick = (tab: TabsPaneContext, event: Event) => {
|
||||
// 根据 tab 的 name 属性更新状态
|
||||
const selectedStatus = state.getStatus[tab.index].status;
|
||||
state.query.status = selectedStatus;
|
||||
init();
|
||||
};
|
||||
|
||||
// 页面加载时获取交易记录
|
||||
onMounted(() => {
|
||||
init()
|
||||
@ -188,4 +213,37 @@ onMounted(() => {
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
:deep(.el-upload) {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
:deep(.el-upload:hover) {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
.avatar{
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
</style>
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "admin"
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
211
ui/src/pages/admin/leaderboard.vue
Normal file
@ -0,0 +1,211 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--增、查交易记录的按钮-->
|
||||
<el-button type="primary" round @click="openAddDialog" size="small">添加</el-button>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form :inline="true" :model="state.query" class="demo-form-inline">
|
||||
<el-form-item label="文创名称:">
|
||||
<el-input v-model="state.query.title" placeholder="请输入文创名称" clearable @input="init" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!--表格-->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-table :data="state.getList">
|
||||
<!-- 列表结开始-->
|
||||
<el-table-column prop="id" label="编号" align="center" />
|
||||
<el-table-column prop="title" label="文创名称" align="center" />
|
||||
|
||||
<el-table-column prop="view" label="阅读量" align="center" />
|
||||
<el-table-column prop="sort" label="排序" align="center" />
|
||||
<!--列表结束-->
|
||||
<el-table-column label="操作" align="center" >
|
||||
<template #default="scope">
|
||||
<el-button @click="info(scope.row.content)" size="small">详情</el-button>
|
||||
<el-button @click="edit(scope.row)" size="small">编辑</el-button>
|
||||
<el-button @click="del(scope.row.id)" type="danger" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-slot:empty>
|
||||
<el-empty description="数据去外太空了~" />
|
||||
</template>
|
||||
</el-table>
|
||||
<!-- 分页控件 -->
|
||||
<el-pagination
|
||||
v-if="state.query.total > 0"
|
||||
:current-page="state.query.page"
|
||||
:page-size="state.query.limit"
|
||||
:total="state.query.total"
|
||||
background layout="prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog v-model="state.dialogVisible" title="新增" width="50%">
|
||||
<el-form
|
||||
:model="state.formData"
|
||||
ref="formRef"
|
||||
label-width="100px"
|
||||
>
|
||||
|
||||
<!-- 表单开始===============================================================================================================================-->
|
||||
<el-form-item label="文创名称" prop="title" :rules="[{ required: true, message: '请输入文创名称', trigger: 'blur' }]">
|
||||
<el-input v-model="state.formData.title"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="内容" prop="content" :rules="[{ required: true, message: '请编写内容', trigger: 'blur' }]">
|
||||
<e-editor :content="state.formData.content" @update:content="handleImageUrl" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort" :rules="[{ required: true, message: '请输入排序', trigger: 'blur' }]">
|
||||
<el-input-number v-model="state.formData.sort" :min="1" :max="10000" />
|
||||
</el-form-item>
|
||||
<!--表单结束===============================================================================================================================-->
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="state.dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveTransaction(formRef)">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="详情"
|
||||
width="500"
|
||||
>
|
||||
<div v-html="state.info"></div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// 交易记录数据
|
||||
import type { FormInstance } from 'element-plus'
|
||||
const formRef = ref<FormInstance>()
|
||||
const dialogVisible = ref(false)
|
||||
const state = reactive({
|
||||
info:"",
|
||||
route:"sys/topic",
|
||||
dialogVisible:false,
|
||||
getList: [],//获取数据
|
||||
query:{
|
||||
total: 0, // 总记录数
|
||||
page: 1, // 当前页码
|
||||
limit: 10, // 每页显示的记录数
|
||||
title:"",
|
||||
type:1,
|
||||
},
|
||||
formData:{}
|
||||
})
|
||||
const info = (info:string) => {
|
||||
console.log(info)
|
||||
state.info = info
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 获取交易记录
|
||||
const init = () => {
|
||||
adminRequest.get(`${state.route}/page`, {
|
||||
params: state.query
|
||||
}).then((res:any) => {
|
||||
state.getList = res.data.list
|
||||
state.query.total = res.data.total
|
||||
})
|
||||
}
|
||||
// 新增交易记录
|
||||
const openAddDialog = () => {
|
||||
state.formData = {}
|
||||
state.dialogVisible = true
|
||||
}
|
||||
// 编辑交易记录
|
||||
const edit = (row: any) => {
|
||||
state.formData = row
|
||||
state.dialogVisible = true
|
||||
}
|
||||
// 接收子组件传递的路径
|
||||
function handleImageUrl(content: string) {
|
||||
state.formData.content = content
|
||||
}
|
||||
// 保存记录
|
||||
const saveTransaction = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
state.formData.type = 1
|
||||
if (state.formData.id) {
|
||||
// 更新交易记录
|
||||
adminRequest.put(`${state.route}`, state.formData)
|
||||
console.log(state.formData)
|
||||
} else {
|
||||
// 新增交易记录
|
||||
adminRequest.post(`${state.route}`, state.formData)
|
||||
}
|
||||
init()
|
||||
state.dialogVisible = false
|
||||
ElMessage.success("提交成功~")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 删除交易记录
|
||||
const del = async (id: number) => {
|
||||
try {
|
||||
await adminRequest.delete(`${state.route}/${id}`)
|
||||
init()
|
||||
ElMessage.success("删除成功~")
|
||||
} catch (error) {
|
||||
ElMessage.error("删除失败~")
|
||||
}
|
||||
}
|
||||
|
||||
// 页码改变时的处理函数
|
||||
const handlePageChange = (page: number) => {
|
||||
state.query.page = page
|
||||
init()
|
||||
}
|
||||
// 页面加载时获取交易记录
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
:deep(.el-upload) {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
:deep(.el-upload:hover) {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
.avatar{
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
</style>
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "admin"
|
||||
}
|
||||
}
|
||||
</route>
|
201
ui/src/pages/admin/slides.vue
Normal file
@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--增、查交易记录的按钮-->
|
||||
<el-button type="primary" round @click="openAddDialog" size="small">添加</el-button>
|
||||
<!--表格-->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-table :data="state.getList">
|
||||
<!-- 列表结开始-->
|
||||
<el-table-column prop="id" label="编号" align="center" />
|
||||
<el-table-column prop="name" label="轮播名称" align="center" />
|
||||
<el-table-column prop="largePic" label="图片" align="center" >
|
||||
<template #default="{ row }">
|
||||
<!-- 点击图片后显示弹框预览 -->
|
||||
<el-image
|
||||
:src="row.path"
|
||||
fit="cover"
|
||||
style="width: 200px;height: 100px"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!--列表结束-->
|
||||
<el-table-column label="操作" align="center" >
|
||||
<template #default="scope">
|
||||
<el-button @click="edit(scope.row)" size="small">编辑</el-button>
|
||||
<el-button @click="del(scope.row.id)" type="danger" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-slot:empty>
|
||||
<el-empty description="数据去外太空了~" />
|
||||
</template>
|
||||
</el-table>
|
||||
<!-- 分页控件 -->
|
||||
<el-pagination
|
||||
v-if="state.query.total > 0"
|
||||
:current-page="state.query.page"
|
||||
:page-size="state.query.limit"
|
||||
:total="state.query.total"
|
||||
background layout="prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog v-model="state.dialogVisible" title="新增" width="50%">
|
||||
<el-form
|
||||
:model="state.formData"
|
||||
ref="formRef"
|
||||
label-width="100px"
|
||||
>
|
||||
<!-- 表单开始===============================================================================================================================-->
|
||||
|
||||
<el-form-item label="轮播名称" prop="name" :rules="[{ required: true, message: '请输入轮播名称', trigger: 'blur' }]">
|
||||
<el-input v-model="state.formData.name"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="图片" prop="path" :rules="[{ required: true, message: '请插入图片', trigger: 'blur' }]">
|
||||
<image-upload @update:imageUrl="handleImageUrl" :image-url="state.formData.path"></image-upload>
|
||||
</el-form-item>
|
||||
|
||||
<!--表单结束===============================================================================================================================-->
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="state.dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveTransaction(formRef)">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// 交易记录数据
|
||||
import type { FormInstance } from 'element-plus'
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const state = reactive({
|
||||
route:"sys/slides",
|
||||
dialogVisible:false,
|
||||
getList: [],//获取数据
|
||||
query:{
|
||||
total: 0, // 总记录数
|
||||
page: 1, // 当前页码
|
||||
limit: 10, // 每页显示的记录数
|
||||
},
|
||||
formData:{}
|
||||
})
|
||||
|
||||
// 接收子组件传递的路径
|
||||
function handleImageUrl(path: string) {
|
||||
state.formData.path = path
|
||||
|
||||
}
|
||||
|
||||
// 获取交易记录
|
||||
const init = () => {
|
||||
adminRequest.get(`${state.route}/page`, {
|
||||
params: state.query
|
||||
}).then((res:any) => {
|
||||
state.getList = res.data.list
|
||||
state.query.total = res.data.total
|
||||
})
|
||||
}
|
||||
// 新增交易记录
|
||||
const openAddDialog = () => {
|
||||
// formData = {
|
||||
// name:"",
|
||||
// path:"",
|
||||
// }
|
||||
state.dialogVisible = true
|
||||
}
|
||||
// 编辑交易记录
|
||||
const edit = (row: any) => {
|
||||
state.formData = row
|
||||
state.dialogVisible = true
|
||||
}
|
||||
|
||||
// 保存记录
|
||||
const saveTransaction = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
if (state.formData.id) {
|
||||
// 更新交易记录
|
||||
adminRequest.put(`${state.route}`, state.formData).then(()=>{
|
||||
init()
|
||||
state.dialogVisible = false
|
||||
ElMessage.success("提交成功~")
|
||||
})
|
||||
} else {
|
||||
// 新增交易记录
|
||||
adminRequest.post(`${state.route}`, state.formData).then(() =>{
|
||||
init()
|
||||
state.dialogVisible = false
|
||||
ElMessage.success("提交成功~")
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 删除交易记录
|
||||
const del = async (id: number) => {
|
||||
try {
|
||||
await adminRequest.delete(`${state.route}/${id}`)
|
||||
init()
|
||||
ElMessage.success("删除成功~")
|
||||
} catch (error) {
|
||||
ElMessage.error("删除失败~")
|
||||
}
|
||||
}
|
||||
|
||||
// 页码改变时的处理函数
|
||||
const handlePageChange = (page: number) => {
|
||||
state.query.page = page
|
||||
init()
|
||||
}
|
||||
// 页面加载时获取交易记录
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
:deep(.el-upload) {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
:deep(.el-upload:hover) {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
.avatar{
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
</style>
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "admin"
|
||||
}
|
||||
}
|
||||
</route>
|
211
ui/src/pages/admin/topic.vue
Normal file
@ -0,0 +1,211 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--增、查交易记录的按钮-->
|
||||
<el-button type="primary" round @click="openAddDialog" size="small">添加</el-button>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form :inline="true" :model="state.query" class="demo-form-inline">
|
||||
<el-form-item label="话题名称:">
|
||||
<el-input v-model="state.query.title" placeholder="请输入话题名称" clearable @input="init" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!--表格-->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-table :data="state.getList">
|
||||
<!-- 列表结开始-->
|
||||
<el-table-column prop="id" label="编号" align="center" />
|
||||
<el-table-column prop="title" label="话题名称" align="center" />
|
||||
|
||||
<el-table-column prop="view" label="阅读量" align="center" />
|
||||
<el-table-column prop="sort" label="排序" align="center" />
|
||||
<!--列表结束-->
|
||||
<el-table-column label="操作" align="center" >
|
||||
<template #default="scope">
|
||||
<el-button @click="info(scope.row.content)" size="small">详情</el-button>
|
||||
<el-button @click="edit(scope.row)" size="small">编辑</el-button>
|
||||
<el-button @click="del(scope.row.id)" type="danger" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-slot:empty>
|
||||
<el-empty description="数据去外太空了~" />
|
||||
</template>
|
||||
</el-table>
|
||||
<!-- 分页控件 -->
|
||||
<el-pagination
|
||||
v-if="state.query.total > 0"
|
||||
:current-page="state.query.page"
|
||||
:page-size="state.query.limit"
|
||||
:total="state.query.total"
|
||||
background layout="prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog v-model="state.dialogVisible" title="新增" width="50%">
|
||||
<el-form
|
||||
:model="state.formData"
|
||||
ref="formRef"
|
||||
label-width="100px"
|
||||
>
|
||||
|
||||
<!-- 表单开始===============================================================================================================================-->
|
||||
<el-form-item label="话题名称" prop="title" :rules="[{ required: true, message: '请输入话题名称', trigger: 'blur' }]">
|
||||
<el-input v-model="state.formData.title"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="内容" prop="content" :rules="[{ required: true, message: '请编写内容', trigger: 'blur' }]">
|
||||
<e-editor :content="state.formData.content" @update:content="handleImageUrl" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort" :rules="[{ required: true, message: '请输入排序', trigger: 'blur' }]">
|
||||
<el-input-number v-model="state.formData.sort" :min="1" :max="10000" />
|
||||
</el-form-item>
|
||||
<!--表单结束===============================================================================================================================-->
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="state.dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveTransaction(formRef)">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="详情"
|
||||
width="500"
|
||||
>
|
||||
<div v-html="state.info"></div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// 交易记录数据
|
||||
import type { FormInstance } from 'element-plus'
|
||||
const formRef = ref<FormInstance>()
|
||||
const dialogVisible = ref(false)
|
||||
const state = reactive({
|
||||
info:"",
|
||||
route:"sys/topic",
|
||||
dialogVisible:false,
|
||||
getList: [],//获取数据
|
||||
query:{
|
||||
total: 0, // 总记录数
|
||||
page: 1, // 当前页码
|
||||
limit: 10, // 每页显示的记录数
|
||||
title:"",
|
||||
type:0,
|
||||
},
|
||||
formData:{}
|
||||
})
|
||||
const info = (info:string) => {
|
||||
console.log(info)
|
||||
state.info = info
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 获取交易记录
|
||||
const init = () => {
|
||||
adminRequest.get(`${state.route}/page`, {
|
||||
params: state.query
|
||||
}).then((res:any) => {
|
||||
state.getList = res.data.list
|
||||
state.query.total = res.data.total
|
||||
})
|
||||
}
|
||||
// 新增交易记录
|
||||
const openAddDialog = () => {
|
||||
state.formData = {}
|
||||
state.dialogVisible = true
|
||||
}
|
||||
// 编辑交易记录
|
||||
const edit = (row: any) => {
|
||||
state.formData = row
|
||||
state.dialogVisible = true
|
||||
}
|
||||
// 接收子组件传递的路径
|
||||
function handleImageUrl(content: string) {
|
||||
state.formData.content = content
|
||||
}
|
||||
// 保存记录
|
||||
const saveTransaction = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
state.formData.type = 0
|
||||
if (state.formData.id) {
|
||||
// 更新交易记录
|
||||
adminRequest.put(`${state.route}`, state.formData)
|
||||
console.log(state.formData)
|
||||
} else {
|
||||
// 新增交易记录
|
||||
adminRequest.post(`${state.route}`, state.formData)
|
||||
}
|
||||
init()
|
||||
state.dialogVisible = false
|
||||
ElMessage.success("提交成功~")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 删除交易记录
|
||||
const del = async (id: number) => {
|
||||
try {
|
||||
await adminRequest.delete(`${state.route}/${id}`)
|
||||
init()
|
||||
ElMessage.success("删除成功~")
|
||||
} catch (error) {
|
||||
ElMessage.error("删除失败~")
|
||||
}
|
||||
}
|
||||
|
||||
// 页码改变时的处理函数
|
||||
const handlePageChange = (page: number) => {
|
||||
state.query.page = page
|
||||
init()
|
||||
}
|
||||
// 页面加载时获取交易记录
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
:deep(.el-upload) {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
:deep(.el-upload:hover) {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
.avatar{
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
</style>
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "admin"
|
||||
}
|
||||
}
|
||||
</route>
|
207
ui/src/pages/admin/user.vue
Normal file
@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--增、查交易记录的按钮-->
|
||||
<el-button type="primary" round @click="openAddDialog" size="small">添加</el-button>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form :inline="true" :model="state.query" class="demo-form-inline">
|
||||
<el-form-item label="账号:">
|
||||
<el-input v-model="state.query.username" placeholder="请输入账号" clearable @input="init" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!--表格-->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-table :data="state.getList">
|
||||
<!-- 列表结开始-->
|
||||
<el-table-column prop="id" label="id"/>
|
||||
<el-table-column prop="username" label="账号"/>
|
||||
<el-table-column prop="password" label="密码"/>
|
||||
<el-table-column prop="createDate" label="创建时间"/>
|
||||
<el-table-column prop="nickName" label="昵称"/>
|
||||
<el-table-column prop="introduce" label="介绍"/>
|
||||
<!--列表结束-->
|
||||
<el-table-column label="操作" align="center" >
|
||||
<template #default="scope">
|
||||
<el-button @click="edit(scope.row)" size="small">编辑</el-button>
|
||||
<el-button @click="del(scope.row.id)" type="danger" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-slot:empty>
|
||||
<el-empty description="数据去外太空了~" />
|
||||
</template>
|
||||
</el-table>
|
||||
<!-- 分页控件 -->
|
||||
<el-pagination
|
||||
v-if="state.query.total > 0"
|
||||
:current-page="state.query.page"
|
||||
:page-size="state.query.limit"
|
||||
:total="state.query.total"
|
||||
background layout="prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog v-model="state.dialogVisible" title="新增" width="50%">
|
||||
<el-form
|
||||
:model="formData"
|
||||
ref="formRef"
|
||||
label-width="100px"
|
||||
>
|
||||
|
||||
<!-- 表单开始===============================================================================================================================-->
|
||||
|
||||
|
||||
|
||||
|
||||
<el-form-item label="账号" prop="username" :rules="[{ required: true, message: '请输入账号', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.username"/>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="密码" prop="password" :rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.password"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="昵称" prop="nickName" :rules="[{ required: true, message: '请输入昵称', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.nickName"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="介绍" prop="introduce" :rules="[{ required: true, message: '请输入介绍', trigger: 'blur' }]">
|
||||
<el-input v-model="formData.introduce"/>
|
||||
</el-form-item>
|
||||
<!--表单结束===============================================================================================================================-->
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="state.dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveTransaction(formRef)">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance } from 'element-plus'
|
||||
const formRef = ref<FormInstance>()
|
||||
const state = reactive({
|
||||
route:"sys/user-front",
|
||||
dialogVisible:false,
|
||||
getList: [],//获取数据
|
||||
query:{
|
||||
total: 0, // 总记录数
|
||||
page: 1, // 当前页码
|
||||
limit: 10, // 每页显示的记录数
|
||||
username: "",
|
||||
}
|
||||
})
|
||||
|
||||
// 生成年份列表 1990-2015
|
||||
const years = [];
|
||||
for (let year = 1990; year <= 2015; year++) {
|
||||
years.push(year.toString());
|
||||
}
|
||||
|
||||
// 表单数据
|
||||
let formData = reactive(<any>{})
|
||||
// 获取交易记录
|
||||
const init = () => {
|
||||
adminRequest.get(`${state.route}/page`, {
|
||||
params: state.query
|
||||
}).then((res:any) => {
|
||||
state.getList = res.data.list
|
||||
state.query.total = res.data.total
|
||||
})
|
||||
}
|
||||
// 新增交易记录
|
||||
const openAddDialog = () => {
|
||||
formData = {}
|
||||
state.dialogVisible = true
|
||||
}
|
||||
// 编辑交易记录
|
||||
const edit = (row: any) => {
|
||||
formData = row
|
||||
state.dialogVisible = true
|
||||
}
|
||||
|
||||
// 保存记录
|
||||
const saveTransaction = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
if (formData.id) {
|
||||
// 更新交易记录
|
||||
adminRequest.put(`${state.route}`, formData)
|
||||
} else {
|
||||
// 新增交易记录
|
||||
adminRequest.post(`${state.route}`, formData)
|
||||
}
|
||||
init()
|
||||
state.dialogVisible = false
|
||||
ElMessage.success("提交成功~")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 删除记录
|
||||
const del = async (id: number) => {
|
||||
try {
|
||||
await adminRequest.delete(`${state.route}/${id}`)
|
||||
init()
|
||||
ElMessage.success("删除成功~")
|
||||
} catch (error) {
|
||||
ElMessage.error("删除失败~")
|
||||
}
|
||||
}
|
||||
|
||||
// 页码改变时的处理函数
|
||||
const handlePageChange = (page: number) => {
|
||||
state.query.page = page
|
||||
init()
|
||||
}
|
||||
// 页面加载时获取交易记录
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
:deep(.el-upload) {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
:deep(.el-upload:hover) {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
.avatar{
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
</style>
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "admin"
|
||||
}
|
||||
}
|
||||
</route>
|
@ -1,122 +0,0 @@
|
||||
<template>
|
||||
<!-- 销售情况按地区分布 -->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<v-chart class="chart" :option="salesByRegionOption" autoresize />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 地区需求对比 -->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<v-chart class="chart" :option="demandByRegionOption" autoresize />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 客户购买频次分析 -->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<v-chart class="chart" :option="mapOption" autoresize />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import VChart from 'vue-echarts'
|
||||
import { BarChart, PieChart, LineChart } from 'echarts/charts'
|
||||
import { TitleComponent, TooltipComponent, LegendComponent, GridComponent } from 'echarts/components'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { use } from 'echarts/core'
|
||||
|
||||
// 引入并使用图表组件
|
||||
use([CanvasRenderer, BarChart, PieChart, LineChart, TitleComponent, TooltipComponent, LegendComponent, GridComponent])
|
||||
|
||||
// 数据模拟:各地区销售、需求、频次等数据
|
||||
const regionSalesData = ref([
|
||||
{ region: '华北', sales: 1500, demand: 2000, frequency: 30 },
|
||||
{ region: '华东', sales: 2500, demand: 3000, frequency: 40 },
|
||||
{ region: '华南', sales: 1200, demand: 1500, frequency: 25 },
|
||||
{ region: '西南', sales: 800, demand: 1000, frequency: 15 },
|
||||
{ region: '西北', sales: 600, demand: 900, frequency: 10 },
|
||||
{ region: '东北', sales: 900, demand: 1100, frequency: 20 },
|
||||
{ region: '华中', sales: 1800, demand: 2200, frequency: 35 },
|
||||
{ region: '西部', sales: 700, demand: 900, frequency: 18 },
|
||||
{ region: '东南', sales: 2200, demand: 2700, frequency: 50 }
|
||||
])
|
||||
|
||||
// 销售情况按地区分布(柱状图)
|
||||
const salesByRegionOption = ref({
|
||||
title: { text: '各地区销售情况' },
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: { data: ['销售量'] },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: regionSalesData.value.map(item => item.region)
|
||||
},
|
||||
yAxis: { type: 'value' },
|
||||
series: [
|
||||
{
|
||||
name: '销售量',
|
||||
type: 'bar',
|
||||
data: regionSalesData.value.map(item => item.sales)
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// 地区需求对比(折线图)
|
||||
const demandByRegionOption = ref({
|
||||
title: { text: '各地区需求对比' },
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: { data: ['需求量'] },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: regionSalesData.value.map(item => item.region)
|
||||
},
|
||||
yAxis: { type: 'value' },
|
||||
series: [
|
||||
{
|
||||
name: '需求量',
|
||||
type: 'line',
|
||||
data: regionSalesData.value.map(item => item.demand)
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
// 客户购买频次分析(饼图)
|
||||
const purchaseFrequencyOption = ref({
|
||||
title: {
|
||||
text: '各地区客户购买频次分析',
|
||||
left: 'center' // 将标题居中
|
||||
},
|
||||
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
data: regionSalesData.value.map(item => item.region)
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '购买频次',
|
||||
type: 'pie',
|
||||
radius: '55%',
|
||||
center: ['50%', '60%'],
|
||||
data: regionSalesData.value.map(item => ({ value: item.frequency, name: item.region })),
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart {
|
||||
height: 400px;
|
||||
}
|
||||
</style>
|
@ -1,173 +0,0 @@
|
||||
<template>
|
||||
<!-- 销售热力图 -->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<v-chart class="chart" :option="heatmapOption" autoresize />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div style="height: 20px"></div>
|
||||
<!-- 产品销售表现雷达图 -->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<v-chart class="chart" :option="radarOption" autoresize />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import VChart from 'vue-echarts'
|
||||
import { HeatmapChart, RadarChart } from 'echarts/charts'
|
||||
import { TitleComponent, TooltipComponent, GridComponent, VisualMapComponent } from 'echarts/components'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { use } from 'echarts/core'
|
||||
|
||||
// 引入并使用图表组件
|
||||
use([CanvasRenderer, HeatmapChart, RadarChart, TitleComponent, TooltipComponent, GridComponent, VisualMapComponent])
|
||||
|
||||
// 数据模拟:液化气瓶的销售数据
|
||||
const productSalesData = ref([
|
||||
{ type: '液化气瓶A', specification: '5L', quality: '优质', sales: 200 },
|
||||
{ type: '液化气瓶A', specification: '10L', quality: '优质', sales: 150 },
|
||||
{ type: '液化气瓶A', specification: '20L', quality: '中等', sales: 180 },
|
||||
{ type: '液化气瓶B', specification: '5L', quality: '良好', sales: 130 },
|
||||
{ type: '液化气瓶B', specification: '10L', quality: '中等', sales: 220 },
|
||||
{ type: '液化气瓶B', specification: '20L', quality: '优质', sales: 250 },
|
||||
{ type: '液化气瓶C', specification: '5L', quality: '优质', sales: 190 },
|
||||
{ type: '液化气瓶C', specification: '10L', quality: '良好', sales: 160 },
|
||||
{ type: '液化气瓶C', specification: '20L', quality: '中等', sales: 200 },
|
||||
{ type: '液化气瓶D', specification: '5L', quality: '中等', sales: 140 },
|
||||
{ type: '液化气瓶D', specification: '10L', quality: '优质', sales: 270 },
|
||||
{ type: '液化气瓶D', specification: '20L', quality: '良好', sales: 230 },
|
||||
{ type: '液化气瓶E', specification: '5L', quality: '良好', sales: 110 },
|
||||
{ type: '液化气瓶E', specification: '10L', quality: '中等', sales: 130 },
|
||||
{ type: '液化气瓶E', specification: '20L', quality: '优质', sales: 220 },
|
||||
{ type: '液化气瓶F', specification: '5L', quality: '中等', sales: 180 },
|
||||
{ type: '液化气瓶F', specification: '10L', quality: '良好', sales: 160 },
|
||||
{ type: '液化气瓶F', specification: '20L', quality: '优质', sales: 200 },
|
||||
{ type: '液化气瓶G', specification: '5L', quality: '优质', sales: 210 },
|
||||
{ type: '液化气瓶G', specification: '10L', quality: '优质', sales: 230 },
|
||||
{ type: '液化气瓶G', specification: '20L', quality: '中等', sales: 220 }
|
||||
])
|
||||
|
||||
// 销售热力图:展示不同规格和质量等级下的销售情况
|
||||
const heatmapOption = ref({
|
||||
title: { text: '销售热力图' },
|
||||
tooltip: { position: 'top' },
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['液化气瓶A', '液化气瓶B', '液化气瓶C', '液化气瓶D', '液化气瓶E', '液化气瓶F', '液化气瓶G']
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: ['5L', '10L', '20L']
|
||||
},
|
||||
visualMap: {
|
||||
min: 0,
|
||||
max: 250, // 根据实际数据设置最大值
|
||||
calculable: true,
|
||||
orient: 'horizontal',
|
||||
left: 'center',
|
||||
inRange: {
|
||||
color: ['#FFFFFF', '#FF0000'] // 从白色到红色的渐变色,表示从低到高的热力值
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '销售量',
|
||||
type: 'heatmap',
|
||||
data: [
|
||||
[0, 0, 200], // [x, y, value] 表示对应的销售量数据
|
||||
[0, 1, 150],
|
||||
[0, 2, 180],
|
||||
[1, 0, 130],
|
||||
[1, 1, 220],
|
||||
[1, 2, 250],
|
||||
[2, 0, 190],
|
||||
[2, 1, 160],
|
||||
[2, 2, 200],
|
||||
[3, 0, 140],
|
||||
[3, 1, 210],
|
||||
[3, 2, 180],
|
||||
[4, 0, 170],
|
||||
[4, 1, 200],
|
||||
[4, 2, 230],
|
||||
[5, 0, 120],
|
||||
[5, 1, 180],
|
||||
[5, 2, 160],
|
||||
[6, 0, 250],
|
||||
[6, 1, 170],
|
||||
[6, 2, 190]
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
// 销售表现雷达图:展示各类型液化气瓶的销售表现
|
||||
const radarOption = ref({
|
||||
title: {
|
||||
text: '液化气产品销售表现',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {},
|
||||
radar: {
|
||||
indicator: [
|
||||
{ name: '液化气瓶A', max: 300 },
|
||||
{ name: '液化气瓶B', max: 300 },
|
||||
{ name: '液化气瓶C', max: 300 },
|
||||
{ name: '液化气瓶D', max: 300 },
|
||||
{ name: '液化气瓶E', max: 300 },
|
||||
{ name: '液化气瓶F', max: 300 },
|
||||
{ name: '液化气瓶G', max: 300 }
|
||||
]
|
||||
},
|
||||
series: [{
|
||||
name: '销售表现',
|
||||
type: 'radar',
|
||||
data: [
|
||||
{
|
||||
value: productSalesData.value.filter(item => item.type === '液化气瓶A').map(item => item.sales),
|
||||
name: '液化气瓶A'
|
||||
},
|
||||
{
|
||||
value: productSalesData.value.filter(item => item.type === '液化气瓶B').map(item => item.sales),
|
||||
name: '液化气瓶B'
|
||||
},
|
||||
{
|
||||
value: productSalesData.value.filter(item => item.type === '液化气瓶C').map(item => item.sales),
|
||||
name: '液化气瓶C'
|
||||
},
|
||||
{
|
||||
value: productSalesData.value.filter(item => item.type === '液化气瓶D').map(item => item.sales),
|
||||
name: '液化气瓶D'
|
||||
},
|
||||
{
|
||||
value: productSalesData.value.filter(item => item.type === '液化气瓶E').map(item => item.sales),
|
||||
name: '液化气瓶E'
|
||||
},
|
||||
{
|
||||
value: productSalesData.value.filter(item => item.type === '液化气瓶F').map(item => item.sales),
|
||||
name: '液化气瓶F'
|
||||
},
|
||||
{
|
||||
value: productSalesData.value.filter(item => item.type === '液化气瓶G').map(item => item.sales),
|
||||
name: '液化气瓶G'
|
||||
}
|
||||
]
|
||||
}]
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart {
|
||||
height: 400px;
|
||||
}
|
||||
</style>
|
@ -1,123 +0,0 @@
|
||||
<template>
|
||||
<!-- 销售额与毛利对比 -->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<v-chart class="chart" :option="salesMarginOption" ref="salesMarginChart" autoresize />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 线上与线下收入对比 -->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<v-chart class="chart" :option="revenueSourceOption" ref="revenueSourceChart" autoresize />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import VChart from 'vue-echarts'
|
||||
import { LineChart, BarChart } from 'echarts/charts'
|
||||
import { TitleComponent, TooltipComponent, LegendComponent, GridComponent, ToolboxComponent } from 'echarts/components'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { use } from 'echarts/core'
|
||||
|
||||
// 引入并使用图表组件
|
||||
use([CanvasRenderer, LineChart, BarChart, TitleComponent, TooltipComponent, LegendComponent, GridComponent, ToolboxComponent])
|
||||
|
||||
// 模拟数据
|
||||
const salesData = ref([
|
||||
{ date: '2024-01-01', salesAmount: 5000, profit: 1200, onlineRevenue: 2500, offlineRevenue: 2500 },
|
||||
{ date: '2024-01-02', salesAmount: 6000, profit: 1500, onlineRevenue: 3000, offlineRevenue: 3000 },
|
||||
{ date: '2024-01-03', salesAmount: 4000, profit: 1000, onlineRevenue: 2000, offlineRevenue: 2000 },
|
||||
{ date: '2024-01-04', salesAmount: 7000, profit: 1800, onlineRevenue: 3500, offlineRevenue: 3500 },
|
||||
{ date: '2024-01-05', salesAmount: 5500, profit: 1300, onlineRevenue: 2700, offlineRevenue: 2800 },
|
||||
// 更多数据...
|
||||
])
|
||||
|
||||
// 销售额与毛利对比(折线图和柱状图结合)
|
||||
const salesMarginOption = ref({
|
||||
title: { text: '销售额与毛利对比' },
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: { data: ['销售额', '毛利'] },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: salesData.value.map(item => item.date)
|
||||
},
|
||||
yAxis: { type: 'value' },
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
magicType: { show: true, type: ['line', 'bar'] },
|
||||
saveAsImage: {
|
||||
show: true,
|
||||
title: '保存为图片',
|
||||
type: 'png',
|
||||
pixelRatio: 2, // 控制图片清晰度
|
||||
backgroundColor: '#ffffff' // 背景色设置为白色,避免透明背景
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '销售额',
|
||||
type: 'bar',
|
||||
data: salesData.value.map(item => item.salesAmount),
|
||||
itemStyle: { color: 'rgba(255, 127, 80, 0.6)' } // 显色不明显
|
||||
},
|
||||
{
|
||||
name: '毛利',
|
||||
type: 'line',
|
||||
data: salesData.value.map(item => item.profit),
|
||||
itemStyle: { color: 'rgba(135, 206, 250, 0.6)' }, // 显色不明显
|
||||
emphasis: { itemStyle: { color: '#87cefa' } }
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// 线上与线下收入对比(柱状图)
|
||||
const revenueSourceOption = ref({
|
||||
title: { text: '线上与线下收入对比' },
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: { data: ['线上收入', '线下收入'] },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: salesData.value.map(item => item.date)
|
||||
},
|
||||
yAxis: { type: 'value' },
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
magicType: { show: true, type: ['line', 'bar'] },
|
||||
saveAsImage: {
|
||||
show: true,
|
||||
title: '保存为图片',
|
||||
type: 'png',
|
||||
pixelRatio: 2, // 控制图片清晰度
|
||||
backgroundColor: '#ffffff',
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '线上收入',
|
||||
type: 'bar',
|
||||
data: salesData.value.map(item => item.onlineRevenue),
|
||||
itemStyle: { color: 'rgba(50, 205, 50, 0.6)' } // 显色不明显
|
||||
},
|
||||
{
|
||||
name: '线下收入',
|
||||
type: 'bar',
|
||||
data: salesData.value.map(item => item.offlineRevenue),
|
||||
itemStyle: { color: 'rgba(255, 99, 71, 0.6)' } // 显色不明显
|
||||
}
|
||||
]
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart {
|
||||
height: 400px;
|
||||
}
|
||||
</style>
|
375
ui/src/pages/cultural/index.vue
Normal file
@ -0,0 +1,375 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<!-- 主内容区 -->
|
||||
<div class="content-wrapper">
|
||||
<!-- 左侧内容 -->
|
||||
<div class="main-content">
|
||||
<h2 class="section-title">人气创作 / 为你推荐</h2>
|
||||
|
||||
<!-- 推荐内容列表 -->
|
||||
<div class="recommend-list">
|
||||
<article
|
||||
v-for="(post, index) in state.getList"
|
||||
:key="index"
|
||||
class="post-card"
|
||||
>
|
||||
<!-- 小组信息 -->
|
||||
<div class="group-info">
|
||||
<el-tag
|
||||
:type="post.groupType === 'default' ? '' : 'warning'"
|
||||
size="small"
|
||||
>
|
||||
{{ post.nickName }}
|
||||
</el-tag>
|
||||
<span class="post-time">{{ post.createTime }}</span>
|
||||
</div>
|
||||
<!-- 内容主体 -->
|
||||
<div class="post-content">
|
||||
<p class="post-excerpt">
|
||||
{{ post.title }}
|
||||
<a
|
||||
v-if="post.title"
|
||||
class="expand-link"
|
||||
@click="toggleExpand(index)"
|
||||
>
|
||||
{{ expandedIndexes.includes(index) ? '收起' : '查看全文' }}
|
||||
</a>
|
||||
</p>
|
||||
<transition name="el-fade-in">
|
||||
<p
|
||||
v-show="expandedIndexes.includes(index)"
|
||||
class="post-fulltext"
|
||||
>
|
||||
<div v-html="post.content "></div>
|
||||
</p>
|
||||
</transition>
|
||||
</div>
|
||||
<!-- 互动数据 -->
|
||||
<div class="interaction-data" @click="to(post.id)">
|
||||
<div class="stats">
|
||||
<span class="stat-item">
|
||||
<el-icon><comment /></el-icon>
|
||||
{{ post.replies }} 回复
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧边栏 -->
|
||||
<div class="sidebar">
|
||||
<div class="trending-topics">
|
||||
<h3 class="sidebar-title">热门话题 ······</h3>
|
||||
|
||||
<el-button class="more-btn" @click="state.dialogVisible = true">添加文创</el-button>
|
||||
|
||||
<div class="topic-list">
|
||||
<div
|
||||
v-for="(topic, index) in state.trendingTopics"
|
||||
:key="index"
|
||||
class="topic-item"
|
||||
>
|
||||
<div class="topic-meta">
|
||||
<span class="topic-index">{{ index + 1 }}</span>
|
||||
<span class="topic-name">{{ topic.title }}</span>
|
||||
</div>
|
||||
<div class="topic-stats">
|
||||
|
||||
|
||||
<span class="stat-number">{{ topic.view }}次浏览</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="state.dialogVisible" title="新增" width="50%">
|
||||
<el-form
|
||||
:model="state.formData"
|
||||
ref="formRef"
|
||||
label-width="100px"
|
||||
>
|
||||
|
||||
<!-- 表单开始===============================================================================================================================-->
|
||||
<el-form-item label="文创名称" prop="title" :rules="[{ required: true, message: '请输入文创名称', trigger: 'blur' }]">
|
||||
<el-input v-model="state.formData.title"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="内容" prop="content" :rules="[{ required: true, message: '请编写内容', trigger: 'blur' }]">
|
||||
<e-editor :content="state.formData.content" @update:content="handleImageUrl" />
|
||||
</el-form-item>
|
||||
<!--表单结束===============================================================================================================================-->
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="state.dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveTransaction(formRef)">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ElTag, ElButton, ElIcon, type FormInstance } from 'element-plus'
|
||||
import { Comment } from '@element-plus/icons-vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
const formRef = ref<FormInstance>()
|
||||
// 推荐帖子数据
|
||||
const state = reactive({
|
||||
route:"api/topic",
|
||||
dialogVisible:false,
|
||||
query: {
|
||||
total: 0, // 总记录数
|
||||
page: 1, // 当前页码
|
||||
limit: 9999, // 每页显示的记录数
|
||||
title: '',
|
||||
type: '1'
|
||||
},
|
||||
getList: [],
|
||||
trendingTopics:[],
|
||||
formData:{},
|
||||
})
|
||||
/**
|
||||
* 跳转
|
||||
*/
|
||||
const to = (id:number) => {
|
||||
router.push(`/topicInfo/${id}`)
|
||||
}
|
||||
// 接收子组件传递的路径
|
||||
function handleImageUrl(content: string) {
|
||||
state.formData.content = content
|
||||
}
|
||||
function init() {
|
||||
frontRequest.get('/api/topic/page', {
|
||||
params: state.query
|
||||
}).then(res => {
|
||||
state.getList = res.data.list
|
||||
loading.close()
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!userStore().frontIsLogin){
|
||||
toast.warning("登录失效")
|
||||
router.push("/login")
|
||||
}
|
||||
init()
|
||||
let query = state.query
|
||||
query.view='view'
|
||||
frontRequest.get("/api/topic/page",{
|
||||
params: query
|
||||
}).then(res =>{
|
||||
state.trendingTopics = res.data.list
|
||||
})
|
||||
})
|
||||
|
||||
const saveTransaction = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
state.formData.type = 1
|
||||
// 新增交易记录
|
||||
frontRequest.post(`${state.route}`, state.formData).then(()=>{
|
||||
init()
|
||||
state.dialogVisible = false
|
||||
ElMessage.success("提交成功~")
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
// 交互状态
|
||||
const expandedIndexes = ref<number[]>([])
|
||||
|
||||
|
||||
// 方法
|
||||
const toggleExpand = (index: number) => {
|
||||
const idx = expandedIndexes.value.indexOf(index)
|
||||
idx === -1
|
||||
? expandedIndexes.value.push(index)
|
||||
: expandedIndexes.value.splice(idx, 1)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 300px;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 20px;
|
||||
color: #1a1a1a;
|
||||
margin: 0 0 24px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
|
||||
.post-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||
margin-bottom: 24px;
|
||||
padding: 20px;
|
||||
transition: transform 0.2s;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.group-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.post-time {
|
||||
color: #8590a6;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.post-title {
|
||||
font-size: 16px;
|
||||
color: #1a1a1a;
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
|
||||
.post-excerpt {
|
||||
color: #646464;
|
||||
line-height: 1.6;
|
||||
margin: 0 0 8px;
|
||||
|
||||
.expand-link {
|
||||
color: #06a;
|
||||
cursor: pointer;
|
||||
margin-left: 8px;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.post-fulltext {
|
||||
color: #444;
|
||||
line-height: 1.7;
|
||||
margin: 12px 0 0;
|
||||
padding-top: 12px;
|
||||
border-top: 1px dashed #eee;
|
||||
}
|
||||
|
||||
.interaction-data {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #f5f5f5;
|
||||
|
||||
.stats {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
.stat-item {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: #8590a6;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.follow-btn {
|
||||
color: #06a;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
.trending-topics {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.sidebar-title {
|
||||
font-size: 16px;
|
||||
color: #1a1a1a;
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
|
||||
.topic-item {
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.topic-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 6px;
|
||||
|
||||
.topic-index {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
color: #8590a6;
|
||||
}
|
||||
|
||||
.topic-name {
|
||||
font-size: 14px;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
}
|
||||
|
||||
.topic-stats {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: #8590a6;
|
||||
|
||||
.stat-divider {
|
||||
color: #ddd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.more-btn {
|
||||
width: 100%;
|
||||
margin-top: 16px;
|
||||
color: #06a;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "front"
|
||||
}
|
||||
}
|
||||
</route>
|
@ -1,33 +0,0 @@
|
||||
<template>
|
||||
<!--轮播图-->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="h-300px">
|
||||
<carousel></carousel>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="h-350px">
|
||||
<carousel></carousel>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 推荐商品列表-->
|
||||
<item></item>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
import Carousel from '~/components/front/carousel.vue'
|
||||
import Item from '~/components/front/item.vue'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "front"
|
||||
}
|
||||
}
|
||||
</route>
|
@ -1,76 +0,0 @@
|
||||
<!--用户详情-->
|
||||
<template>
|
||||
|
||||
<el-tabs v-model="state.activeName" @tab-click="handleClick">
|
||||
<el-tab-pane v-for="status in state.getOrderStatus" :label="status.name" :name="status.status">
|
||||
<el-table
|
||||
:data="state.getList"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column prop="date" label="Date" width="180" />
|
||||
<el-table-column prop="name" label="Name" width="180" />
|
||||
<el-table-column prop="address" label="Address" />
|
||||
<el-table-column label="操作" min-width="120">
|
||||
<template #default>
|
||||
<el-button link type="primary" size="small" v-if="state.activeName == 1">立即付款</el-button>
|
||||
<el-button link type="primary" size="small" v-if="state.activeName == 3">确认收货</el-button>
|
||||
<el-button link type="primary" size="small" v-if="state.activeName == 4">评价</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
const state = reactive({
|
||||
activeName:1,
|
||||
getOrderStatus:[
|
||||
{name:"待付款",status:1},
|
||||
{name:"未发货",status:2},
|
||||
{name:"待收货",status:3},
|
||||
{name:"待评价",status:4},
|
||||
],
|
||||
getList:[
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取列表数据
|
||||
*/
|
||||
function getList() {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "frontUserInfo"
|
||||
}
|
||||
}
|
||||
</route>
|
207
ui/src/pages/fronttopic/index.vue
Normal file
@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--增、查交易记录的按钮-->
|
||||
<el-button type="primary" round @click="openAddDialog" size="small">添加</el-button>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form :inline="true" :model="state.query" class="demo-form-inline">
|
||||
<el-form-item label="文创名称:">
|
||||
<el-input v-model="state.query.title" placeholder="请输入文创名称" clearable @input="init" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!--表格-->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-table :data="state.getList">
|
||||
<!-- 列表结开始-->
|
||||
<el-table-column prop="id" label="编号" align="center" />
|
||||
<el-table-column prop="title" label="文创名称" align="center" />
|
||||
<el-table-column prop="view" label="阅读量" align="center" />
|
||||
<!--列表结束-->
|
||||
<el-table-column label="操作" align="center" >
|
||||
<template #default="scope">
|
||||
<el-button @click="info(scope.row.content)" size="small">详情</el-button>
|
||||
<el-button @click="edit(scope.row)" size="small">编辑</el-button>
|
||||
<el-button @click="del(scope.row.id)" type="danger" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-slot:empty>
|
||||
<el-empty description="数据去外太空了~" />
|
||||
</template>
|
||||
</el-table>
|
||||
<!-- 分页控件 -->
|
||||
<el-pagination
|
||||
v-if="state.query.total > 0"
|
||||
:current-page="state.query.page"
|
||||
:page-size="state.query.limit"
|
||||
:total="state.query.total"
|
||||
background layout="prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog v-model="state.dialogVisible" title="新增" width="50%">
|
||||
<el-form
|
||||
:model="state.formData"
|
||||
ref="formRef"
|
||||
label-width="100px"
|
||||
>
|
||||
|
||||
<!-- 表单开始===============================================================================================================================-->
|
||||
<el-form-item label="文创名称" prop="title" :rules="[{ required: true, message: '请输入文创名称', trigger: 'blur' }]">
|
||||
<el-input v-model="state.formData.title"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="内容" prop="content" :rules="[{ required: true, message: '请编写内容', trigger: 'blur' }]">
|
||||
<e-editor :content="state.formData.content" @update:content="handleImageUrl" />
|
||||
</el-form-item>
|
||||
|
||||
<!--表单结束===============================================================================================================================-->
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="state.dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveTransaction(formRef)">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="详情"
|
||||
width="500"
|
||||
>
|
||||
<div v-html="state.info"></div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// 交易记录数据
|
||||
import type { FormInstance } from 'element-plus'
|
||||
const formRef = ref<FormInstance>()
|
||||
const dialogVisible = ref(false)
|
||||
const state = reactive({
|
||||
info:"",
|
||||
route:"sys/topic",
|
||||
dialogVisible:false,
|
||||
getList: [],//获取数据
|
||||
query:{
|
||||
total: 0, // 总记录数
|
||||
page: 1, // 当前页码
|
||||
limit: 9999, // 每页显示的记录数
|
||||
title:"",
|
||||
type:1,
|
||||
},
|
||||
formData:{}
|
||||
})
|
||||
const info = (info:string) => {
|
||||
console.log(info)
|
||||
state.info = info
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 获取交易记录
|
||||
const init = () => {
|
||||
frontRequest.get(`/api/topic/page1`, {
|
||||
params: state.query
|
||||
}).then((res:any) => {
|
||||
state.getList = res.data.list
|
||||
state.query.total = res.data.total
|
||||
})
|
||||
}
|
||||
// 新增交易记录
|
||||
const openAddDialog = () => {
|
||||
state.formData = {}
|
||||
state.dialogVisible = true
|
||||
}
|
||||
// 编辑交易记录
|
||||
const edit = (row: any) => {
|
||||
state.formData = row
|
||||
state.dialogVisible = true
|
||||
}
|
||||
// 接收子组件传递的路径
|
||||
function handleImageUrl(content: string) {
|
||||
state.formData.content = content
|
||||
}
|
||||
// 保存记录
|
||||
const saveTransaction = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
state.formData.type = 1
|
||||
if (state.formData.id) {
|
||||
// 更新交易记录
|
||||
adminRequest.put(`${state.route}`, state.formData)
|
||||
console.log(state.formData)
|
||||
} else {
|
||||
// 新增交易记录
|
||||
adminRequest.post(`${state.route}`, state.formData)
|
||||
}
|
||||
init()
|
||||
state.dialogVisible = false
|
||||
ElMessage.success("提交成功~")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 删除交易记录
|
||||
const del = async (id: number) => {
|
||||
try {
|
||||
await adminRequest.delete(`${state.route}/${id}`)
|
||||
init()
|
||||
ElMessage.success("删除成功~")
|
||||
} catch (error) {
|
||||
ElMessage.error("删除失败~")
|
||||
}
|
||||
}
|
||||
|
||||
// 页码改变时的处理函数
|
||||
const handlePageChange = (page: number) => {
|
||||
state.query.page = page
|
||||
init()
|
||||
}
|
||||
// 页面加载时获取交易记录
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
:deep(.el-upload) {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
:deep(.el-upload:hover) {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
.avatar{
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
</style>
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "frontUserInfo"
|
||||
}
|
||||
}
|
||||
</route>
|
@ -2,8 +2,12 @@
|
||||
<!--轮播图-->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="h-300px">
|
||||
<carousel></carousel>
|
||||
<div class="h-250px hot" >
|
||||
<el-space wrap>
|
||||
<div v-for="item in state.commit" :key="i" style="margin-top: 10px;">
|
||||
<el-button text style="width: 290px" @click="to(item.id)">{{item.title}} </el-button>
|
||||
</div>
|
||||
</el-space>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@ -12,23 +16,53 @@
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
|
||||
<!-- 推荐商品列表-->
|
||||
<item></item>
|
||||
|
||||
<hot1></hot1>
|
||||
<!-- 推荐商品列表-->
|
||||
<item :get-list=state.getList></item>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
import Carousel from '~/components/front/carousel.vue'
|
||||
import Item from '~/components/front/item.vue'
|
||||
import Hot1 from '~/components/hot/Hot1.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
const state = reactive({
|
||||
getList: [],
|
||||
commit: [],
|
||||
})
|
||||
|
||||
// frontRequest.get("/api/user/userInfo").then(response =>{
|
||||
// user.frontUserInfo = response.data
|
||||
// })
|
||||
|
||||
onMounted(()=>{
|
||||
frontRequest.get("/api/item/score").then(res =>{
|
||||
state.getList = res.data
|
||||
})
|
||||
|
||||
frontRequest.get("/api/item/commit").then(res =>{
|
||||
state.commit = res.data
|
||||
})
|
||||
})
|
||||
/**
|
||||
* 跳转
|
||||
*/
|
||||
const to = (id:number) => {
|
||||
router.push(`/info/${id}`)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.hot {
|
||||
background-color: rgba(0, 0, 0, 0.2); /* 更浅的黑色 */
|
||||
border-radius: 10px; /* 四周圆弧,值可以根据需要调整 */
|
||||
}
|
||||
:deep(.el-button){
|
||||
color: #2e191e;
|
||||
font-weight: 700;
|
||||
}
|
||||
:deep(.el-button:hover) {
|
||||
background-color: black;
|
||||
}
|
||||
</style>
|
||||
<route lang="json">
|
||||
{
|
||||
|
@ -15,9 +15,17 @@
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
import InfoTop from '~/components/front/info-top.vue'
|
||||
import InfoBottom from '~/components/front/info-bottom.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
onMounted(()=>{
|
||||
if (!userStore().frontIsLogin){
|
||||
toast.warning("登录失效")
|
||||
router.push("/login")
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
|
148
ui/src/pages/leaderboard/index.vue
Normal file
@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<div class="movie-ranking">
|
||||
<div class="header">
|
||||
<h1>图书排行榜</h1>
|
||||
</div>
|
||||
<div class="movie-list">
|
||||
<div class="movie-item" v-for="(movie, index) in state.getList" :key="index" @click="to(movie.id)">
|
||||
<div class="ranking-badge">{{ index + 1 }}</div> <!-- Display rank at the top right -->
|
||||
<img :src="movie.image" class="poster" />
|
||||
<div class="movie-details">
|
||||
<h3>{{ movie.title }}</h3>
|
||||
<p>{{ movie.movieType }}</p>
|
||||
<p style="text-align: center">价格: {{ movie.price }}元 | 作者:{{ movie.tag }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<!-- <el-col :span="6">-->
|
||||
<!-- <!– Placeholder for additional content or sidebar –>-->
|
||||
<!-- d-->
|
||||
<!-- </el-col>-->
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
const state = reactive({
|
||||
route: "api/item",
|
||||
dialogVisible: false,
|
||||
getList: [], // Data fetching
|
||||
query: {
|
||||
total: 0, // Total number of records
|
||||
page: 1, // Current page number
|
||||
limit: 100, // Number of records per page
|
||||
}
|
||||
})
|
||||
|
||||
// Fetch movie data
|
||||
const init = () => {
|
||||
frontRequest.get(`${state.route}/page`, {
|
||||
params: state.query
|
||||
}).then((res: any) => {
|
||||
state.getList = res.data.list
|
||||
state.query.total = res.data.total
|
||||
})
|
||||
}
|
||||
|
||||
// Navigate to movie details page
|
||||
const to = (id: number) => {
|
||||
router.push(`/info/${id}`)
|
||||
}
|
||||
|
||||
// Fetch data on component mount
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.movie-ranking {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
text-align: center;
|
||||
font-size: 48px;
|
||||
font-weight: bold;
|
||||
background: linear-gradient(45deg, #ca067d, #e12304); /* Gradient background */
|
||||
color: transparent;
|
||||
background-clip: text; /* Make the text color match the gradient */
|
||||
-webkit-background-clip: text; /* Safari support */
|
||||
animation: glowing 1.5s infinite alternate; /* Glowing effect */
|
||||
text-shadow: 0 0 5px rgba(255, 255, 255, 0.6), 0 0 10px rgba(255, 255, 255, 0.6), 0 0 15px rgba(255, 255, 255, 0.6); /* Glowing effect */
|
||||
}
|
||||
@keyframes glowing {
|
||||
0% {
|
||||
text-shadow: 0 0 5px rgba(255, 255, 255, 0.6), 0 0 10px rgba(255, 255, 255, 0.6), 0 0 15px rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
50% {
|
||||
text-shadow: 0 0 10px rgb(246, 226, 226), 0 0 20px rgba(255, 255, 255, 0.8), 0 0 30px rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
100% {
|
||||
text-shadow: 0 0 5px rgba(255, 255, 255, 0.6), 0 0 10px rgba(255, 255, 255, 0.6), 0 0 15px rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
.movie-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.movie-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.poster {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.movie-details h3 {
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.movie-details p {
|
||||
margin: 5px 0;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
/* Rank badge style */
|
||||
.ranking-badge {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background-color: rgb(252, 224, 75);
|
||||
color: #2e191e;
|
||||
padding: 5px 10px;
|
||||
border-radius: 50%;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "front"
|
||||
}
|
||||
}
|
||||
</route>
|
@ -6,6 +6,7 @@
|
||||
<div class="module_mian">
|
||||
<div class="module_title">登录帐户</div>
|
||||
<div class="module_desc">输入用户名 & 登录密码</div>
|
||||
|
||||
<div class="module_m">
|
||||
<div class="module_text">用户名</div>
|
||||
<input class="module_input" type="text" placeholder="输入用户名" v-model="login.username" />
|
||||
@ -23,9 +24,9 @@
|
||||
<!-- </div>-->
|
||||
<div class="module_m">
|
||||
<div class="module_code">
|
||||
<el-radio-group v-model="login.role" class="ml-4">
|
||||
<el-radio :label=0 size="large">普通用户</el-radio>
|
||||
<el-radio :label=1 size="large">管理员</el-radio>
|
||||
<el-radio-group v-model="state.role" class="ml-4">
|
||||
<el-radio :label=false size="large">普通用户</el-radio>
|
||||
<el-radio :label=true size="large">管理员</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
@ -41,32 +42,48 @@
|
||||
import { useRouter } from 'vue-router'
|
||||
import { loginAdmin } from '~/api/user/adminUserApi'
|
||||
import { getUuid } from '~/utils/utils'
|
||||
import { loginFront } from '~/api/user/frontUserApi'
|
||||
const router = useRouter()
|
||||
const state = reactive({
|
||||
role: false,
|
||||
captchaUrl: '',
|
||||
loginFrom: {},
|
||||
loading: false
|
||||
})
|
||||
const login = reactive({ username: 'admin', password: 'admin', captcha: '', uuid: '',role: 0,})
|
||||
const login = reactive({
|
||||
username: '',
|
||||
password: '',
|
||||
captcha: '',
|
||||
uuid: '' })
|
||||
const onLogin = () => {
|
||||
state.loading = true
|
||||
if (login.role == 1) {
|
||||
console.log("管理员")
|
||||
if (state.role) {
|
||||
loginAdmin(login).then(response => {
|
||||
state.loading = false
|
||||
ElMessage.success('登录成功')
|
||||
userStore().adminIsLogin = true
|
||||
userStore().adminToken = response.data.token
|
||||
router.push('/admin')
|
||||
adminRequest.get("/sys/user/info").then(response => {
|
||||
userStore().adminUserInfo = response.data
|
||||
router.push('/admin')
|
||||
ElMessage.success("登录成功~")
|
||||
})
|
||||
|
||||
}).catch(() => {
|
||||
state.loading = false
|
||||
onRefreshCode()
|
||||
})
|
||||
} else {
|
||||
loginFront(login)
|
||||
ElMessage.success("登录成功~")
|
||||
router.push('/')
|
||||
frontRequest.post("/api/user/login", login).then(response =>{
|
||||
const user = userStore()
|
||||
|
||||
user.frontToken = response.data.token
|
||||
frontRequest.get("/api/user/userInfo").then(response =>{
|
||||
user.frontIsLogin = true
|
||||
user.frontUserInfo = response.data
|
||||
ElMessage.success("登录成功~")
|
||||
router.push('/')
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
/**
|
||||
@ -82,6 +99,8 @@ const onRefreshCode = () => {
|
||||
}
|
||||
onMounted(() => {
|
||||
getCaptchaUrl()
|
||||
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -91,6 +110,7 @@ onMounted(() => {
|
||||
height: 100vh;
|
||||
background: #FFFFFF;
|
||||
}
|
||||
|
||||
.module {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
@ -98,10 +118,12 @@ onMounted(() => {
|
||||
justify-content: space-between;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.module_img {
|
||||
width: 60%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.module_r {
|
||||
width: 40%;
|
||||
background: #e5efee;
|
||||
@ -109,6 +131,7 @@ onMounted(() => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.module_mian {
|
||||
width: 65%;
|
||||
background: #FFFFFF;
|
||||
@ -117,18 +140,21 @@ onMounted(() => {
|
||||
overflow: hidden;
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
|
||||
.module_title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.module_desc {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
color: #a7a7a7;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.module_m {
|
||||
margin: 0 auto;
|
||||
width: 80%;
|
||||
@ -140,6 +166,7 @@ onMounted(() => {
|
||||
color: #333333;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.module_input {
|
||||
width: 96%;
|
||||
height: 40px;
|
||||
@ -149,10 +176,12 @@ onMounted(() => {
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.module_code {
|
||||
width: 96%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.module_code_input {
|
||||
width: 60%;
|
||||
height: 40px;
|
||||
@ -162,6 +191,7 @@ onMounted(() => {
|
||||
padding-left: 2%;
|
||||
padding-right: 2%;
|
||||
}
|
||||
|
||||
.module_code_img {
|
||||
width: 130px;
|
||||
height: 40px;
|
||||
@ -171,9 +201,11 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.module_radio input {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.forgetpwd {
|
||||
margin: 0 auto;
|
||||
width: 80%;
|
||||
@ -182,6 +214,7 @@ onMounted(() => {
|
||||
margin-top: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.module_button {
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
@ -194,6 +227,7 @@ onMounted(() => {
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.module_button:active {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
86
ui/src/pages/order/index.vue
Normal file
@ -0,0 +1,86 @@
|
||||
<!--用户详情-->
|
||||
<template>
|
||||
|
||||
<el-tabs v-model="state.activeName" @tab-click="handleClick">
|
||||
<el-tab-pane v-for="status in state.getOrderStatus" :label="status.name" :name="status.status">
|
||||
<el-table :data="state.getList">
|
||||
<!-- 列表结开始-->
|
||||
<el-table-column prop="id" label="编号" align="center" />
|
||||
<el-table-column prop="title" label="电影名称" align="center" />
|
||||
<el-table-column prop="year" label="上映时间" align="center" />
|
||||
<el-table-column prop="largePic" label="图片" align="center" >
|
||||
<template #default="{ row }">
|
||||
<!-- 点击图片后显示弹框预览 -->
|
||||
<el-image
|
||||
:src="row.largePic"
|
||||
fit="cover"
|
||||
:preview-src-list="row.largePic.split(',')"
|
||||
:preview-teleported="true"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="country" label="国家" align="center" />
|
||||
<el-table-column prop="movieType" label="电影类型" align="center" />
|
||||
<el-table-column prop="director" label="导演" align="center" />
|
||||
<el-table-column prop="actor" label="主演" align="center" />
|
||||
<el-table-column prop="ratingValue" label="评分" align="center" />
|
||||
<el-table-column prop="ratingCount" label="评分人数" align="center" />
|
||||
<!--列表结束-->
|
||||
<el-table-column label="操作" align="center" >
|
||||
<template #default="scope">
|
||||
|
||||
<el-button @click="del(scope.row.id)" type="danger" size="small">取消</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-slot:empty>
|
||||
<el-empty description="数据去外太空了~" />
|
||||
</template>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
const state = reactive({
|
||||
activeName:1,
|
||||
getOrderStatus:[
|
||||
{name:"全部",status:1},
|
||||
],
|
||||
getList:[]
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取列表数据
|
||||
*/
|
||||
function init() {
|
||||
frontRequest.get("/api/item/list").then(res =>{
|
||||
state.getList = res.data
|
||||
})
|
||||
}
|
||||
const del = (id:number) => {
|
||||
frontRequest.post("/api/behavior/delete",{itemId:id,type:1}).then(res =>{
|
||||
ElMessage.success("收藏成功")
|
||||
init()
|
||||
})
|
||||
init()
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
init()
|
||||
})
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "frontUserInfo"
|
||||
}
|
||||
}
|
||||
</route>
|
@ -3,6 +3,10 @@
|
||||
<div class="module_mian">
|
||||
<div class="module_title">注册帐户</div>
|
||||
<div class="module_desc">输入用户名 & 密码</div>
|
||||
<div class="module_m">
|
||||
<div class="module_text">用户昵称</div>
|
||||
<input class="module_input" type="text" placeholder="输入昵称" v-model="register.nickName" />
|
||||
</div>
|
||||
<div class="module_m">
|
||||
<div class="module_text">用户名</div>
|
||||
<input class="module_input" type="text" placeholder="输入用户名" v-model="register.username" />
|
||||
|
216
ui/src/pages/topic/index.vue
Normal file
@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<div class="topic-square">
|
||||
<!-- 顶部导航 -->
|
||||
<header class="header">
|
||||
<div class="search-bar">
|
||||
<el-input placeholder="搜索话题" v-model="state.query.title" @input="init">
|
||||
<template #prefix>
|
||||
<el-icon><search /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<nav class="nav-tabs">
|
||||
<div class="tab active">话题总览</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<!-- 主体内容 -->
|
||||
<main class="main-content">
|
||||
<!-- 左侧动态列表 -->
|
||||
<div class="dynamic-list">
|
||||
<div v-for="topic in state.getList" :key="topic.id" class="topic-item" @click="to(topic.id)">
|
||||
<div class="content">
|
||||
<h3 class="title" style="color: #34cdfe">{{ topic.title }}</h3>
|
||||
<p class="text"><div v-html=" topic.content"></div></p>
|
||||
<div class="topic-tag">
|
||||
<el-tag type="info" size="small">来自话题 {{ topic.topic }}</el-tag>
|
||||
</div>
|
||||
<div class="interaction">
|
||||
<span class="stats"> 我要回应 </span>
|
||||
<div class="actions">
|
||||
<el-button type="text">
|
||||
{{topic.view}} 预览
|
||||
</el-button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 右侧热门话题 -->
|
||||
<div class="trending-topics">
|
||||
<div class="section-title">话题榜单</div>
|
||||
<div v-for="(topic, index) in state.trendingTopics" :key="topic.id" class="trend-item" @click="to(topic.id)">
|
||||
<div class="rank">{{ index + 1 }}</div>
|
||||
<div class="info">
|
||||
<div class="title">{{ topic.title }}</div>
|
||||
<div class="views">{{ topic.view }}次浏览</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
import { ElLoading } from 'element-plus'
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: 'Loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
})
|
||||
const state = reactive({
|
||||
getList:[],
|
||||
trendingTopics:[],
|
||||
query:{
|
||||
total: 0, // 总记录数
|
||||
page: 1, // 当前页码
|
||||
limit: 9999, // 每页显示的记录数
|
||||
title:"",
|
||||
type:"0",
|
||||
}
|
||||
})
|
||||
|
||||
function init() {
|
||||
frontRequest.get("/api/topic/page",{
|
||||
params: state.query
|
||||
}).then(res =>{
|
||||
state.getList = res.data.list
|
||||
loading.close()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转
|
||||
*/
|
||||
const to = (id:number) => {
|
||||
router.push(`/topicInfo/${id}`)
|
||||
}
|
||||
|
||||
|
||||
onMounted(()=>{
|
||||
init()
|
||||
let query = state.query
|
||||
query.view='view'
|
||||
frontRequest.get("/api/topic/page",{
|
||||
params: query
|
||||
}).then(res =>{
|
||||
state.trendingTopics = res.data.list
|
||||
})
|
||||
})
|
||||
// 分页相关
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(10)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.topic-square {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
display: flex;
|
||||
margin-top: 20px;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
.tab {
|
||||
padding: 12px 20px;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
border-bottom: 2px solid #07a;
|
||||
color: #07a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-content {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 300px;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.dynamic-list {
|
||||
.topic-item {
|
||||
padding: 20px;
|
||||
margin-bottom: 30px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
cursor: pointer;
|
||||
.topic-tag {
|
||||
margin: 15px 0;
|
||||
.el-tag {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.interaction {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
|
||||
.stats {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trending-topics {
|
||||
cursor: pointer;
|
||||
.section-title {
|
||||
font-weight: 500;
|
||||
margin-bottom: 15px;
|
||||
|
||||
}
|
||||
|
||||
.trend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
margin-bottom: 10px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 4px;
|
||||
|
||||
.rank {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: #07a;
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.info {
|
||||
flex: 1;
|
||||
|
||||
.views {
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "front"
|
||||
}
|
||||
}
|
||||
</route>
|
263
ui/src/pages/topicInfo/[id].vue
Normal file
@ -0,0 +1,263 @@
|
||||
<template>
|
||||
<div class="topic-container">
|
||||
<!-- 话题头部 -->
|
||||
<div class="topic-header">
|
||||
<div class="header-main">
|
||||
<h1 class="title">{{ state.info.title }}</h1>
|
||||
</div>
|
||||
<div class="stats">{{state.info.view }}次浏览</div>
|
||||
</div>
|
||||
<!-- 话题描述 -->
|
||||
<div class="topic-description">
|
||||
<p><div v-html="state.info.content"></div></p>
|
||||
</div>
|
||||
<!-- 发帖入口 -->
|
||||
<div class="post-actions">
|
||||
<el-button type="text" icon="edit" @click="dialogVisible = true">说点什么</el-button>
|
||||
</div>
|
||||
<!-- 内容筛选 -->
|
||||
<div class="filter-tabs">
|
||||
<div
|
||||
v-for="tab in tabs"
|
||||
:key="tab"
|
||||
:class="['tab', { active: activeTab === tab }]"
|
||||
@click="activeTab = tab"
|
||||
>
|
||||
{{ tab }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 广播列表 -->
|
||||
<div v-if="state.info.commentEntityList" class="broadcast-list">
|
||||
<div
|
||||
v-for="(item, index) in state.info.commentEntityList"
|
||||
:key="index"
|
||||
class="broadcast-item"
|
||||
>
|
||||
<div class="user-info">
|
||||
<div class="meta">
|
||||
<div class="username">{{ item.nickName }}</div>
|
||||
<div class="time">{{item.createTime}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<p class="text"><div v-html="item.content" class="rich-text-container"></div></p>
|
||||
<!-- <div class="interactions">-->
|
||||
<!-- <span class="response">{{ item.responses }} 回应</span>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-else description="评论哪去了?" />
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="评论"
|
||||
width="500"
|
||||
>
|
||||
<e-editor :content="state.form.content" @update:content="handleImageUrl" />
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="comment">
|
||||
提交
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ElButton, ElAvatar, ElLoading } from 'element-plus'
|
||||
import { useRoute } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const itemId = route.params.id
|
||||
const dialogVisible = ref(false)
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
const activeTab = ref('评论')
|
||||
const tabs = ['评论']
|
||||
// 广播数据
|
||||
const state = reactive({
|
||||
info: {},
|
||||
broadcasts: [
|
||||
{
|
||||
author: 'lolus',
|
||||
avatar: 'https://example.com/avatar1.jpg',
|
||||
time: '2025-02-10T13:38:18',
|
||||
content: '你穿着酥皮大衣坐在那里,我感觉你要碎了...',
|
||||
responses: 154,
|
||||
reposts: 1,
|
||||
},
|
||||
{
|
||||
author: '饮水少年',
|
||||
avatar: 'https://example.com/avatar2.jpg',
|
||||
time: '2025-02-03T10:02:34',
|
||||
content: '只有懂得自我激励自我认可我们才能够度过人生漫漫长路...',
|
||||
responses: 89,
|
||||
reposts: 2,
|
||||
},
|
||||
],
|
||||
form:{}
|
||||
})
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: 'Loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
})
|
||||
const comment = () => {
|
||||
state.form.topicId = itemId
|
||||
if (state.form.content == '<p><br></p>' || state.form.content == ''){
|
||||
ElMessage.warning("不能为空!")
|
||||
return
|
||||
}
|
||||
frontRequest.post("/api/comment",{itemId:itemId,content:state.form.content}).then(response =>{
|
||||
ElMessage.success("发表成功")
|
||||
state.form.content= ""
|
||||
init()
|
||||
dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
function init() {
|
||||
frontRequest.get(`/api/topic/${itemId}`).then((res) => {
|
||||
state.info = res.data
|
||||
loading.close()
|
||||
})
|
||||
|
||||
}
|
||||
onMounted(() => {
|
||||
if (!userStore().frontIsLogin){
|
||||
toast.warning("登录失效")
|
||||
router.push("/login")
|
||||
}
|
||||
init()
|
||||
|
||||
})
|
||||
|
||||
// 接收子组件传递的路径
|
||||
function handleImageUrl(content: string) {
|
||||
state.form.content = content
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.rich-text-container {
|
||||
width: 100%; /* 设置宽度为容器的100%或指定的宽度 */
|
||||
|
||||
overflow: hidden; /* 内容超出部分将被隐藏 */
|
||||
text-overflow: ellipsis; /* 如果需要,可以加上省略号 */
|
||||
word-wrap: break-word; /* 防止长单词或链接溢出 */
|
||||
white-space: normal; /* 允许换行 */
|
||||
box-sizing: border-box; /* 确保padding不会影响布局 */
|
||||
}
|
||||
.topic-container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.topic-header {
|
||||
margin-bottom: 30px;
|
||||
.header-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
font-size: 28px;
|
||||
}
|
||||
}
|
||||
.stats {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.topic-description {
|
||||
padding: 20px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
p {
|
||||
margin: 0;
|
||||
line-height: 1.6;
|
||||
color: #444;
|
||||
}
|
||||
}
|
||||
|
||||
.post-actions {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.filter-tabs {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
.tab {
|
||||
padding: 8px 20px;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
|
||||
&.active {
|
||||
color: #07a;
|
||||
border-bottom: 2px solid #07a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.broadcast-item {
|
||||
padding: 20px;
|
||||
margin-bottom: 25px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
|
||||
.meta {
|
||||
margin-left: 12px;
|
||||
.username {
|
||||
font-weight: 500;
|
||||
}
|
||||
.time {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
.text {
|
||||
margin: 0 0 15px 0;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.interactions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #666;
|
||||
.response {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<route lang="json">
|
||||
{
|
||||
"meta": {
|
||||
"layout": "front"
|
||||
}
|
||||
}
|
||||
</route>
|
@ -5,7 +5,6 @@
|
||||
style="max-width: 600px"
|
||||
:model="state.userInfo"
|
||||
label-width="auto"
|
||||
|
||||
>
|
||||
<el-form-item
|
||||
prop="username"
|
||||
@ -16,7 +15,6 @@
|
||||
message: '用户名不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
|
||||
]"
|
||||
>
|
||||
<el-input v-model="state.userInfo.username" />
|
||||
@ -47,6 +45,19 @@
|
||||
>
|
||||
<el-input v-model="state.userInfo.password" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
prop="confirmPassword"
|
||||
label="确认密码"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '确认密码不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<el-input v-model="state.userInfo.confirmPassword" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitForm(formRef)">提交</el-button>
|
||||
</el-form-item>
|
||||
@ -55,10 +66,29 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { reactive } from 'vue'
|
||||
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
const formRef = ref<FormInstance>()
|
||||
const state = reactive({
|
||||
userInfo: {}
|
||||
})
|
||||
|
||||
function init() {
|
||||
frontRequest.get("/api/user/userInfo").then(res =>{
|
||||
state.userInfo = res.data
|
||||
|
||||
})
|
||||
}
|
||||
onMounted(() =>{
|
||||
// if (!userStore().frontIsLogin){
|
||||
// toast.warning("登录失效")
|
||||
// router.push("/login")
|
||||
// }
|
||||
init()
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* 提交
|
||||
* @param formEl
|
||||
@ -67,17 +97,17 @@ const submitForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
console.log('submit!')
|
||||
} else {
|
||||
console.log('error submit!')
|
||||
|
||||
frontRequest.put("/api/user/update",state.userInfo).then(res =>{
|
||||
ElMessage.success("修改成功")
|
||||
router.push("/login")
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -12,10 +12,18 @@ export const router = createRouter({
|
||||
})
|
||||
// 路由拦截
|
||||
router.beforeEach((to, from, next) => {
|
||||
// 前台处理逻辑
|
||||
if (to.fullPath.includes("/front")){
|
||||
console.log(to.fullPath)
|
||||
console.log(to.fullPath)
|
||||
if (to.fullPath.includes("/front/info")){
|
||||
console.log("公告管理~")
|
||||
//判断有没有登录
|
||||
if (!userStore().frontIsLogin){
|
||||
ElMessage.warning("请先登录~")
|
||||
next('/login');
|
||||
}
|
||||
}
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// 管理员全部限制
|
||||
else if (to.fullPath.includes("/admin")){
|
||||
console.log("管理员认证~")
|
||||
|
@ -3,8 +3,8 @@ import { defineStore } from 'pinia'
|
||||
export default defineStore('navStore', {
|
||||
state() {
|
||||
return {
|
||||
adminPath: "/",
|
||||
frontPath: "/front/",
|
||||
adminPath: "/admin",
|
||||
frontPath: "/front",
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
@ -4,7 +4,7 @@ export default defineStore('userStore', {
|
||||
state() {
|
||||
return {
|
||||
adminIsLogin: false,
|
||||
isLogin: false,
|
||||
frontIsLogin: false,
|
||||
adminToken: "",
|
||||
frontToken: "",
|
||||
adminUserInfo:{},
|
||||
|